Celenort
Conciencia
게시물 253
오늘 0
전체 0
A site about logging consciousness

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

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

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. 행렬 곱의 행, 열 수 불만족 등)
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;
        }
    }
}
댓글을 불러오는 중입니다.