[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를 아래 조건을 만족하도록 수정, 추가하시오.
- 행, 열 개수를 매개변수로 받는 생성자
- Matrix 객체를 매개변수로 받는 복사 생성자(깊은 복사)
- 연산자 오버로딩1: 행렬의 더하기, 빼기, 곱(상수 or 다른 행렬)
- 연산자 오버로딩2: ==, != 연산이 가능하도록 구현
- Transpose, Rank, 단위행렬, 행 개수, 열 개수 메소드 구현
- 정사각행렬일 경우 역행렬 메소드 구현 (Gauss 소거법, LU분해 등)
- 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;
}
}
}
댓글을 불러오는 중입니다.