Post

[C#] 2차원 인덱서 과제

[C#] 2차원 인덱서 과제

2차원 인덱서를 활용한 Matrix Class는 다음가 같다.

1
2
3
4
5
6
7
8
9
public class Matrix  
{ 
  private double[,] data;
  public double this[int row, int column]
  {
    get{return data[row, column];}
    set{data[row, column]=value;}
  }
}

위 Matrix class를 아래 조건을 만족하도록 수정, 추가하시오.

  1. 행, 열 개수를 매개변수로 받는 생성자
  2. Matrix 객체를 매개변수로 받는 복사 생성자(깊은 복사)
  3. 연산자 오버로딩1: 행렬의 더하기, 빼기, 곱(상수 or 다른 행렬)
  4. 연산자 오버로딩2: ==, != 연산이 가능하도록 구현
  5. Transpose, Rank, 단위행렬, 행 개수, 열 개수 메소드 구현
  6. 정사각행렬일 경우 역행렬 메소드 구현 (Gauss 소거법, LU분해 등)
  7. ToString 메소드 오버라이드하여, 행렬 출력 메소드 구현
    • 각종 오류 및 이상 조건에 대한 예외 처리할 것 (ex. 행렬 곱의 행, 열 수 불만족 등)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
using System;
using System.Collections;
/// <summary>
/// 구현된 클래스 :
/// Matrix
/// 
/// 생성자 : 
/// Matrix(int row, int column) or Matrix(Matrix mat)
/// Row x column으로 입력 받음 + 행렬 mat을 입력으로 받고 이와 동일한 차원, 값을 가짐(Deepcopy 생성자)
/// Matrix(int row, int column)으로 생성시 모든 원소는 0으로 초기화됨
/// 
/// 연산자 오버로딩 : 
/// ==, != (Matrix, Matrix) => boolean
/// +, -, * (Matrix, Matrix) => Matrix 
/// * (double, Matrix ) or (Matrix, double) => Matrix 
/// ToString (Matrix) => string  / 소숫점 5자리에서 반올림하여 \t로 구분지어 표시
/// (실제 계산에서는 반올림하지 않고 표기시만 반올림하여 표시)
/// 
/// 메소드 : 
/// public Matrix DeepCopy() // 생성자가 아닌 메소드 형식으로 Deepcopy
/// public void IdentifyMatrix(int dim) // dim x dim의 항등행렬로 this를 변경함
/// public static Matrix IdentityMatrix(int dim) // dim x dim의 항등행렬을 반환하는 메서드
/// public int GetRow() // this에 해당하는 행렬의 Row를 가져옴
/// public int GetColumn() //this에 해당하는 행렬의 Column을 가져옴
/// public void Transpose() //this에 해당하는 행렬을 전치하는 메서드
/// public Matrix TransposeMatrix() // this의 전치행렬을 반환하는 메서드
/// public void SwapRow(int n1, int n2) // this에 해당하는 행렬의 n1, n2 행을 서로 바꿈
/// public Matrix InverseMatrix() // this에 해당하는 행렬의 역행렬을 반환. 역행렬이 없거나 정방행렬이 아니면 오류 출력
/// public int Rank() // this에 해당하는 행렬의 Rank를 출력. 정방행렬이 아니어도 구동가능.
/// 
/// </summary>

namespace task2_number
{
    class Program
    {
        static void Main(string[] args)
        {
            //Todo : Test cases 대입
            Matrix mat = new Matrix(3, 3);
            mat[1, 1] = 1;
            mat[1, 2] = 1;
            mat[1, 3] = 1;
            mat[2, 1] = 0;
            mat[2, 2] = 0;
            mat[2, 3] = 4;
            mat[3, 1] = 1;
            mat[3, 2] = 0;
            mat[3, 3] = 0;
            Console.WriteLine(mat);
            Matrix inv = new Matrix(3, 3);
            inv = mat.InverseMatrix();
            Console.WriteLine(inv);
        }
    }
    public class Matrix
    {
        private double precision { get; } = 1.0E-10; // 계산의 정밀도 지정. (Rank, Inverse에서 사용)
        private int row;
        private int column; //Matrix row, column 정의
        public Matrix(int row, int column)
        {
            this.row = row;
            this.column = column;
            data = new double[this.row + 1, this.column + 1]; //  data[x,0], data[0,y]는 편의상 0으로 치환. data[1,1] = 1행 1열의 값
        }
        private double[,] data;
        public Matrix(Matrix mat) // Matrix 를 입력 받고, 입력받은 행렬을 그대로 복사하는 복사 생성자
        {
            data = new double[mat.row + 1, mat.column + 1];
            this.row = mat.row;
            this.column = mat.column;
            for (int i = 1; i <= mat.row; i++) // [1,1]이 1행 1렬이므로 1~mat.row까지 2중 for문 구동
            {
                for (int j = 1; j <= mat.column; j++)
                {
                    this[i, j] = mat[i, j];
                }
            }
        }
        public Matrix DeepCopy() // 생성자가 아닌 메서드 형태의 DeepCopy 구현
        {
            Matrix copiedMatrix = new Matrix(this.row, this.column);
            for (int i = 1; i <= this.row; i++)
            {
                for (int j = 1; j <= this.column; j++)
                {
                    copiedMatrix[i, j] = this[i, j];
                }
            }
            return copiedMatrix;
        }
        public void IdentifyMatrix(int dim) // this에 해당하는 행렬을 dim x dim 단위행렬로 바꿈
        {
            this.row = dim;
            this.column = dim; //dim x dim 의 정방행렬로 this의 필드 수정
            data = new double[this.row + 1, this.column + 1]; //인덱서 크기도 새로 지정
            for (int i = 1; i <= this.row; i++)
            {
                for (int j = 1; j <= this.column; j++)
                {
                    if (i == j)
                    {
                        this[i, j] = 1; //대각성분일 때만 1, 나머지일 때 0
                    }
                    else
                    {
                        this[i, j] = 0;
                    }
                }
            }
        }
        public static Matrix IdentityMatrix(int dim) //dim x dim에 해당하는 Identity Matrix를 반환.
        {
            Matrix imat = new Matrix(dim, dim);
            for (int i = 1; i <= imat.row; i++)
            {
                for (int j = 1; j <= imat.column; j++)
                {
                    if (i == j)
                    {
                        imat[i, j] = 1;
                    }
                    else
                    {
                        imat[i, j] = 0;
                    }
                }
            }
            return imat;
        }
        public double this[int row, int column] //인덱서
        {
            get
            {
                if (this.row < row || this.column < column)
                {
                    throw new Exception("행렬의 행이나 열의 갯수보다 큰 index를 대입했습니다.");
                }
                else if (row < 1 || column < 1)
                {
                    throw new Exception("행렬의 행이나 열의 갯수는 0보다 커야 합니다."); //row, column 0보다 작거나 행이나 열의 갯수보다 큰 값이 대입되지 않도록 오류처리
                }
                else
                {
                    return data[row, column];
                }
            }
            set
            {
                if (this.row < row || this.column < column)
                {
                    throw new Exception("행렬의 행이나 열의 갯수보다 큰 index를 대입했습니다.");
                }
                else if (row < 1 || column < 1)
                {
                    throw new Exception("행렬의 행이나 열의 갯수는 0보다 커야 합니다.");

                }
                else
                {
                    data[row, column] = value;
                }
            }

        }

        public static Matrix operator +(Matrix m1, Matrix m2) // + 연산자 오버로딩
        {
            if (m1.row != m2.row || m1.column != m2.column)
                throw new Exception("더하려는 행렬들의 dimension이 맞지 않습니다.");
            else
            {
                Matrix mresult = new Matrix(m1.row, m1.column);
                for (int i = 1; i <= m1.row; i++)
                {
                    for (int j = 1; j <= m1.column; j++)
                    {
                        mresult[i, j] = m1[i, j] + m2[i, j];
                    }
                }
                return mresult;
            }
        }
        public static Matrix operator -(Matrix m1, Matrix m2) // - 연산자 오버로딩
        {
            if (m1.row != m2.row || m1.column != m2.column)
                throw new Exception("빼려는 행렬들의 dimension이 맞지 않습니다.");
            else
            {
                Matrix mresult = new Matrix(m1.row, m1.column);
                for (int i = 1; i <= m1.row; i++)
                {
                    for (int j = 1; j <= m1.column; j++)
                    {
                        mresult[i, j] = m1[i, j] - m2[i, j];
                    }
                }
                return mresult;
            }
        }
        public static Matrix operator *(double k, Matrix m1) // 상수곱
        {
            Matrix mresult = new Matrix(m1.row, m1.column);
            for (int i = 1; i <= m1.row; i++)
            {
                for (int j = 1; j <= m1.column; j++)
                {
                    mresult[i, j] = k * m1[i, j];
                }
            }
            return mresult;
        }
        public static Matrix operator *(Matrix m1, double k) // 상수곱 (역순)
        {
            Matrix mresult = new Matrix(m1.row, m1.column);
            for (int i = 1; i <= m1.row; i++)
            {
                for (int j = 1; j <= m1.column; j++)
                {
                    mresult[i, j] = k * m1[i, j];
                }
            }
            return mresult;
        }
        public static Matrix operator *(Matrix m1, Matrix m2) // 행렬곱
        {
            if (m1.column != m2.row) //m1의 열과 m2의 행이 같지 않을 때 오류 throw
                throw new Exception("곱하려는 행렬들의 dimension이 맞지 않습니다: mxn, nxp");
            else
            {
                Matrix mresult = new Matrix(m1.row, m2.column);
                for (int i = 1; i <= m1.row; i++)
                {
                    for (int j = 1; j <= m2.column; j++)
                    {
                        mresult[i, j] = 0; //initialize

                        for (int k = 1; k <= m1.column; k++)
                        {
                            mresult[i, j] += m1[i, k] * m2[k, j]; // m1's ith , m2's jth vector곱 수행
                        }
                    }
                }
                return mresult;
            }
        }
        public static Boolean operator ==(Matrix m1, Matrix m2)
        {
            if (m1.row != m2.row || m1.column != m2.column)
            {
                throw new Exception("비교하려는 행렬의 dimension이 다릅니다.");
            }
            else
            {
                bool breakfor = false;
                for (int i = 1; i <= m1.row; i++)
                {
                    if (breakfor == true)//2번째 for에서 breakfor =true가 되면 첫번째 for도 break
                    {
                        break;
                    }
                    for (int j = 1; j <= m1.column; j++)
                    {
                        if (m1[i, j] == m2[i, j])
                        {
                            continue;
                        }
                        else
                        {
                            breakfor = true; //break는 이중 포문 뭇 뚫으므로 signal 보냄.
                            break;
                        }
                    }

                }
                if (breakfor == true) // 한번이라도 breakfor를 건드렸으면 true이고, 같지 않으므로.
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }

        }
        public static Boolean operator !=(Matrix m1, Matrix m2)
        {
            if (m1 == m2) // == operator에 따라 정의됨.
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        public int GetRow() //Row, Column을 얻어오는 메소드.
        {
            return this.row;
        }
        public int GetColumn()
        {
            return this.column;
        }
        public override string ToString() //ToString 메소드 오버라이드
        {
            String str = new string("");
            for (int i = 1; i <= this.row; i++)
            {
                for (int j = 1; j <= this.column; j++)
                {
                    str += Math.Round(this[i, j], 5).ToString();
                    if (j != this.column)
                        str += "\t"; // 한 원소를 입력후 Tab
                }
                if (i != this.row)
                    str += "\n"; // 한 행을 입력 후 엔터
            }
            str += "\n"; //모든 행렬을 입력후 엔터
            return str;
        }
        public void Transpose() //Transpose Method
        {
            Matrix tempMat = new Matrix(this.row, this.column);
            for (int i = 1; i <= this.row; i++)
            {
                for (int j = 1; j <= this.column; j++)
                {
                    tempMat[i, j] = this[i, j]; //this에 해당하는 행렬이 달라지므로 이를 미리 복사해둠
                }
            }
            this.row = tempMat.column;
            this.column = tempMat.row;
            data = new double[this.row + 1, this.column + 1];
            for (int i = 1; i <= tempMat.row; i++)
            {
                for (int j = 1; j <= tempMat.column; j++)
                {
                    this[j, i] = tempMat[i, j]; //Transpose 실시
                }
            }
        }
        public Matrix TransposeMatrix() //Transpose Method
        {
            Matrix result = new Matrix(this.column, this.row); //행과 열의 차원이 바뀐 result 행렬 생성
            for (int i = 1; i <= this.row; i++)
            {
                for (int j = 1; j <= this.column; j++)
                {
                    result[j, i] = this[i, j]; //Transpose 실시
                }
            }
            return result;
        }

        public void SwapRow(int num1, int num2)
        {
            if (num1 == num2)
            {
                return;
            }
            else if (num1 > this.row || num2 > this.row || num2 < 1 || num1 < 1)
            {
                throw new Exception("Swap 하려는 Row의 index가 행렬의 최대 index보다 큽니다.");
            }
            else
            {
                Matrix tempVec = new Matrix(1, this.column); // 임시 저장용 1xm 벡터 생성
                for (int i = 1; i <= this.column; i++)
                {
                    tempVec[1, i] = this[num1, i];
                    this[num1, i] = this[num2, i];
                    this[num2, i] = tempVec[1, i];

                }

            }
        }

        public Matrix InverseMatrix()
        {
            if (this.row != this.column)
                throw new Exception("정방행렬이 아니어서 역행렬을 구할 수 없습니다.");
            else
            {
                //가우스-조르단 소거법 이용
                Matrix aug = new Matrix(this.row, this.column * 2); //augmented matrix, 원래행렬에 I를 옆에 추가한거 만들기
                for (int i = 1; i <= this.row; i++) //i=1부터 행의 갯수까지 for 시작
                {
                    for (int j = 1; j<= this.column;j++) //j=1부터 열의 갯수까지 for 시작
                    {
                        aug[i, j] = this[i, j]; //aug행렬의 왼쪽에는 원래 행렬을,
                    }
                    for (int k = this.column+1;k<=this.column*2;k++)
                    {
                        if ((k - this.column) == i)
                        {
                            aug[i, k] = 1; //오른 쪽에는 항등행렬을 대입
                        }
                        else
                            aug[i, k] = 0;
                    }
                }

                int row_used = 0; // 이미 정렬된 row의 갯수 기록하기 위한 index
                for (int c = 1; c <= this.column; c++) // 1개의 column에 대해 leading 1 아래에 0 이외의 숫자가 나오지 않게.
                {
                    int indexer = 0; // c번째 열 내에서 0이 아닌 원소의 갯수
                    for (int i = 1; i <= aug.row - row_used; i++)
                    {
                        if (aug[row_used + i, c] != 0)
                        {
                            aug.SwapRow(row_used + i, row_used + indexer + 1); //0이 아닌 row가 있다면, indexer+1번째 row와 바꿔치기 함.
                            if (indexer > 0)
                            {
                                double ratio = aug[row_used + indexer + 1, c] / aug[row_used + 1, c]; // leading 1을 만들기 위하여 행을 상수로 나누어줌
                                for (int j = 1; j <= aug.column; j++)
                                {
                                    aug[row_used + indexer + 1, j] -= aug[row_used + 1, j] * ratio;
                                    if (aug[row_used + indexer + 1, j] < precision && aug[row_used + indexer + 1, j] > -1 * precision) // 너무 작은 숫자는 0으로 처리
                                    {
                                        aug[row_used + indexer + 1, j] = 0;
                                    }
                                }
                            }
                            indexer++;
                        }

                    }
                    if (indexer != 0) //indexer가 0이 아니다 → c열에서 0이 아닌 행이 맨 위로 갔으므로,
                    {
                        row_used++; //이미 사용된 행 +1;
                    }
                }
                //여기까지 거치면 일반행렬이 REF 행렬로 바뀌어 있음


                for (int c = 1;c<=this.column;c++)
                {
                    for (int r = 1; r<=this.column;r++)
                    {
                        if (r == c || aug[r, c] == 0)
                            continue;
                        double ratio = aug[c, c] / aug[r, c];
                        for (int i =1; i<=aug.column;i++)
                        {

                            aug[r, i] *= ratio;
                            aug[r, i] -= aug[c, i];
                            if (aug[r,i] < precision && aug[r,i]>-1*precision) // 너무 작은 숫자는 0으로 처리
                            {
                                aug[r,i] = 0;
                            }
                        }
                    }
                }
                //REF → RREF로 변환. leading 1 아래에 0만 위치하도록 기본행연산 실행
                for (int r=1;r<=this.column;r++)
                {
                    double ratio2 = aug[r, r];

                    for (int j = 1;j<=aug.column;j++)
                    {
                        aug[r, j] /= ratio2;
                    }
                }
                Matrix outp = new Matrix(this.row, this.row);
                for (int i = 1; i <= this.row; i++)
                {
                    for (int j = 1;j<=this.column;j++)
                    {
                        double a = aug[i, this.row + j];
                        double b = a;
                        if (a == double.NegativeInfinity || a == double.PositiveInfinity || Double.IsNaN(a)) 
                        {
                            throw new Exception("역행렬이 존재하지 않는 행렬입니다.");
                            //ratio가 0일 때 그 값이 inf이거나 NaN이므로 이 때 는 역행렬 존재하지 않음.
                        }
                        else
                        {
                            outp[i, j] = a; //확장행렬의 오른쪽 부분만 잘라냄
                        }
                    }
                }
                return outp;
                



            }
        }
        public int Rank()
        {
            Matrix rmat = new Matrix(this.row, this.column); 
            for (int i = 1; i <= this.row; i++)
            {
                for (int j = 1; j <= this.column; j++)
                {
                    rmat[i, j] = this[i, j];
                }
            }
            int row_used = 0;
            for (int c=1;c<=rmat.column;c++)
            {
                int indexer = 0;
                int passedcounter = 0;
                for (int i = 1; i<=rmat.row-row_used;i++)
                {
                    if (rmat[row_used+i,c] !=0)
                    {
                        passedcounter++;
                        rmat.SwapRow(row_used+i, row_used+indexer + 1);
                        if (indexer>0)
                        {
                            double ratio = rmat[row_used+indexer+1, c] / rmat[row_used + 1, c];
                            for (int j = 1; j <= rmat.column; j++)
                            {
                                rmat[row_used + indexer + 1, j] -= rmat[row_used + 1, j] * ratio;
                                if (rmat[row_used + indexer + 1, j] < precision&&rmat[row_used+indexer+1,j]>-1*precision) // 너무 작은 숫자는 0으로 처리
                                {
                                    rmat[row_used + indexer + 1, j] = 0;
                                }
                            }
                        }
                        indexer++;
                    }

                }
                if (passedcounter != 0)
                {
                    row_used++;
                }
            }
            for (int r=1; r<=rmat.column&&r<=rmat.row;r++)
            {
                for (int c=1; c<=rmat.column;c++)
                {
                    if (rmat[r, r] == 0) 
                        break;
                    rmat[r, c] /= rmat[r, r];
                }
            }
            int nullity = 0;
            for (int r=rmat.row;r>0;r--)
            {
                for (int c = 1;c<=rmat.column;c++)
                {
                    if (rmat[r,c]==0)
                    {
                        if (c==rmat.column)
                        {
                            nullity++;
                        }
                        continue;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            return rmat.row - nullity;
        }
    }
}
This post is licensed under CC BY 4.0 by the author.