/*
MatrixManager의 정의 (행렬에 필요한 메소드를 정의)
MatrixManager.cpp
*/
#include "MatrixManager.h"
MATRIX MATRIX_MANGER::GetTransposeMatrix(MATRIX _matrix)
{
// 전치행렬은 원래 행렬의 행과 열의 갯수가 다르다.
double ** transpose = MATRIX_MANGER::CustomAllocator(_matrix.GetCol(), _matrix.GetRow());
double ** matrix = _matrix.GetMatrix();
for (int i = 0; i < _matrix.GetRow(); i++)
{
for (int j = 0; j < _matrix.GetCol(); j++)
{
transpose[j][i] = matrix[i][j]; // 전치행렬은 원래행렬의 행과 열을 바꾼것과 같다.
}
}
// 전치행렬은 행과 열이 바뀜에 주의하자
MATRIX ret(transpose, _matrix.GetCol(), _matrix.GetRow());
return ret;
}
double ** MATRIX_MANGER::GetTransposeMatrix(double ** _matrix, int _row, int _col)
{
// 전치행렬은 원래 행렬의 행과 열의 갯수가 다르다.
double ** transpose = MATRIX_MANGER::CustomAllocator(_col, _row);
for (int i = 0; i < _row; i++)
{
for (int j = 0; j < _col; j++)
{
transpose[j][i] = _matrix[i][j]; // 전치행렬은 원래행렬의 행과 열을 바꾼것과 같다.
}
}
return transpose; // 사용 후에는 반드시 CustomDeallocator()를 사용해야함!
}
double MATRIX_MANGER::GetDeterminant(MATRIX _matrix)
{
// 4x4를 초과하는 행렬은 허용하지 않는다
if (_matrix.GetRow() != _matrix.GetCol() || (_matrix.GetRow() > 4))
return ERROR_CODE;
double det = 0; // 실제 리턴할 행렬식 값
switch (_matrix.GetRow())
{
// 2x2의 행렬식은 ad-bc로 정의된다.
case 2:
det = TwoByTwo(_matrix.GetMatrix());
break;
// 3x3의 행렬식은 재귀적으로 2x2행렬의 집합으로 이루어진다.
case 3:
{
double **matrix = _matrix.GetMatrix();
double **minor = nullptr; // 소행렬
bool sign = true; // 부호는 +,-,+,-가 반복된다.
double val = 0;
for (int i = 0; i < _matrix.GetRow(); i++)
{
minor = CustomAllocator(2, 2); // 소행렬을 위해 2x2 크기로 동적할당
GetMinorMatix(minor, matrix, _matrix.GetRow(), FIX, i); // 소행렬을 얻어서 minor에 저장시킴
val += (sign ? 1 : -1) * matrix[FIX][i] * TwoByTwo(minor); // 부호 곱하기 2x2행렬 공식인 ad-bc * 해당원소를 넣어주면 된다.
// 아까 동적할당한 자원을 해제시킴
if (minor != nullptr)
CustomDeallocator(minor, 2);
sign = !sign; // 부호 스왑
}
det = val;
}
break;
// 4x4를 초과하는 행렬은 가우스소거법 같은 방법이 더 효율적이다.
case 4:
{
double **matrix = _matrix.GetMatrix();
double **minor1 = nullptr; // 3x3 행렬식
double **minor2 = nullptr; // 2x2 행렬식
bool sign1 = true; // 3x3행렬식의 부호는 +,-,+,-가 반복된다.
bool sign2 = true; // 2x2행렬식의 부호
double val[4];// 각 소행렬의 값을 저장할 배열 (4x4행렬식은 4개의 3x3행렬식으로 이루어짐)
memset(val, 0, sizeof(double) * 4); // 값 초기화
for (int i = 0; i < _matrix.GetRow(); i++, sign2 = true)
{
minor1 = CustomAllocator(3, 3); // 소행렬을 위해 3x3 크기로 동적할당
GetMinorMatix(minor1, matrix, _matrix.GetRow(), FIX, i); // 소행렬을 얻어서 minor1에 저장시킴
for (int j = 0; j < _matrix.GetRow() - 1; j++)
{
// 방금 위에서 얻은 행렬식의 행렬식을 구한다(재귀적인 성질)
minor2 = CustomAllocator(2, 2); // 소행렬을 위해 2x2 크기로 동적할당
GetMinorMatix(minor2, minor1, _matrix.GetRow() - 1, FIX, j);
val[i] += (sign2 ? 1 : -1) * minor1[FIX][j] * TwoByTwo(minor2); // 행렬식의 공식 적용
sign2 = !sign2; // 부호 스왑
// 2x2 소행렬 할당 해제
if (minor2 != nullptr)
CustomDeallocator(minor2, 2);
}
val[i] *= (sign1 ? 1 : -1) * matrix[FIX][i]; // 3x3행렬식의 부호*해당원소의 값을 곱해서 방금 나온 값에 더해줌
// 3x3 소행렬 할당 해제
if (minor1 != nullptr)
CustomDeallocator(minor1, 3);
sign1 = !sign1; // 부호 스왑
}
// 나온 값을 모~두 더해줌
for (int i = 0; i < 4; i++)
det += val[i];
}
break;
}
return det; // 계산해서 나온 행렬식 리턴
}
void MATRIX_MANGER::GetMinorMatix(double **_minor, double **_matrix, int _n, int _removeRow, int _removeCol)
{
// x=minor의 행, y=minor의 열
int x, y;
bool checker = false; // 조건문에 들어갔는지 검사용
x = y = 0;
for (int i = 0; i < _n; i++)
{
// i행을 소거해야할 때는 그냥 무시
if (i == _removeRow)
continue;
for (int j = 0; j < _n; j++)
{
// j열을 소거해야할 때에도 무시
if (j == _removeCol)
continue;
// matrix의 실직적인 소행렬 minor를 저장함
else
{
checker = true;
_minor[x][y++] = _matrix[i][j];
}
}
// 소행렬을 다음 행, 첫 열로 이동
if (checker == true)
{
x++;
y = 0;
}
}
}
double ** MATRIX_MANGER::CustomAllocator(int _row, int _col)
{
double ** matrix = nullptr;
// m x n의 배열을 메모리 동적할당
matrix = new double*[_row];
for (int i = 0; i < _row; i++)
{
matrix[i] = new double[_col];
memset(matrix[i], 0, sizeof(double) * _col);
}
return matrix;
}
void MATRIX_MANGER::CustomDeallocator(double **_matrix, int _demension)
{
if (_matrix != nullptr)
{
// 메모리 동적할당 해제
for (int i = 0; i < _demension; i++)
delete[]_matrix[i];
delete[]_matrix;
_matrix = nullptr;
}
}
MATRIX MATRIX_MANGER::GetInverseMatrix(MATRIX _matrix)
{
// 역행렬은 정방행렬에만 있다.
if (_matrix.GetRow() != _matrix.GetCol())
{
cout << "정방행렬이 아니므로 역행렬을 구할 수 없습니다." << endl;
return NULL;
}
// 행렬식값이 0이면 특이행렬(역행렬이 없음)
if (GetDeterminant(_matrix) == 0)
{
cout << "가역행렬이 아닙니다." << endl;
return NULL;
}
// 4x4미만의 행렬만 역행렬을 구한다.
if (_matrix.GetRow() > 4 || _matrix.GetCol() > 4)
{
cout << "4x4미만의 행렬만 가능합니다." << endl;
return NULL;
}
double **inverse = CustomAllocator(_matrix.GetRow(), _matrix.GetCol()); // 일단 역행렬을 위한 할당
double **minor = nullptr; // 소행렬을 위한 배열
double val = 0; // 행렬식 값을 임시로 저장하기 위한 변수
bool sign = true; // 부호를 + - + - 로 활용하기 위한 변수
// 행, 열의 갯수만큼 반복문을 돌려서
for (int i = 0; i < _matrix.GetRow(); i++)
{
for (int j = 0; j < _matrix.GetCol(); j++)
{
minor = CustomAllocator(_matrix.GetRow() - 1, _matrix.GetCol() - 1); // 소행렬을 위해 (row-1)x(col-1) 크기로 동적할당
GetMinorMatix(minor, _matrix.GetMatrix(), _matrix.GetRow(), i, j); // 소행렬을 얻어서 minor에 저장시킴(소거행열은 i,j)
MATRIX sudDet(minor, _matrix.GetRow() - 1, _matrix.GetCol() - 1); // 아까 얻은 소행렬을 MATRIX 인스턴스로 만듦
// (i+1)+(j+1)가 (공식으로는 i+j이지만 인덱스는 0부터 시작하므로) 짝 수 이면 부호가 양수이고, 홀 수 이면 음수가 된다.
if (((i + 1) + (j + 1)) % 2 == 0)
sign = true;
else
sign = false;
// 부호 * 소행렬의 행렬식값을 곱해준다
val = (sign == true ? 1 : -1) * GetDeterminant(sudDet);
// 0에 마이너스 부호 붙는 것 방지
if (val == 0.0)
val = 0;
inverse[i][j] = val; // 이를 역행렬 배열에 저장한다.
// 사용한 자원은 반납해야 메모리 누수가 없다.
if (minor != nullptr)
CustomDeallocator(minor, _matrix.GetRow() - 1);
}
}
// 위에서 만든 여인수행렬을 전치행렬화 하여 딸림행렬로 만든다.(리턴값이 딸림행렬이다)
inverse = MATRIX_MANGER::GetTransposeMatrix(inverse, _matrix.GetRow(), _matrix.GetCol());
// 역행렬은 원래행렬의 행렬식(det) 값을 딸림행렬의 각 요소에 나눠주면 된다.
double det = MATRIX_MANGER::GetDeterminant(_matrix);
for (int i = 0; i < _matrix.GetRow(); i++)
for (int j = 0; j < _matrix.GetCol(); j++)
inverse[i][j] /= det;
// 방금 만든 역행렬(double **형)을 MATRIX 인스턴스로 만들어서 리턴한다.
MATRIX MatInverse(inverse, _matrix.GetRow(), _matrix.GetCol());
return MatInverse;
}
MATRIX MATRIX_MANGER::GetMultiplyMatrix(MATRIX _m1, MATRIX _m2)
{
// A행렬의 열의 갯수와 B행렬의 행의 갯수가 일치하지 않으면 곱셈은 성립하지 않는다.
if (_m1.GetCol() != _m2.GetRow())
{
cout << "곱셈이 정의되지 않습니다." << endl;
return NULL;
}
// 곱셈이 성립할 때 생성되는 행렬의 행과 열의 수
int row = _m1.GetRow();
int col = _m2.GetCol();
double **mat1 = _m1.GetMatrix(); // 행렬 A의 double **형
double **mat2 = _m2.GetMatrix(); // 행렬 B의 double **형
double **ret = MATRIX_MANGER::CustomAllocator(row, col); // 곱셈으로 생성되는 행렬은 (A의 행 x B의 열) 차원이다.
double val = .0; // 곱한 결과를 저장할 변수
int x, y; // 결과 행렬의 인덱스
x = y = 0;
// 행렬의 곱 순서대로 곱한 결과를 ret에 저장한다.
// A = m x n, B = n x p 일 때, C = m x p 와 같다.
for (int i = 0, k = 0; i < _m1.GetRow(); k++, val = 0)
{
for (int j = 0; j < _m2.GetRow(); j++)
{
val += mat1[i][j] * mat2[j][k]; // 곱한 결과를 저장해둔다.
}
// 곱한 결과를 C(결과)행렬에 저장하고, 다음 인덱스를 넘어가야하면 조건에 맞게 처리한다.
ret[x][y++] = val;
if (y == col)
{
x++; // 다음 행
y = 0; // 첫 열로
k = -1; // B는 다시 처음처럼 곱셈을 해야하므로(바로 for문 조건으로 인해 1 증가하므로 -1이다)
i++; // A를 다음 행으로
}
}
// 결과를 MATRIX로 만들어 리턴
MATRIX matRet(ret, row, col);
return matRet;
}
MATRIX MATRIX_MANGER::GetIdentity(int _demension)
{
// 파라미터 차원의 행렬을 생성한다.
double **ret = CustomAllocator(_demension, _demension);
// 단위행렬을 만들고
for (int i = 0; i < _demension; i++)
ret[i][i] = 1.0;
// 리턴한다.
MATRIX matRet(ret, _demension, _demension);
return matRet;
}
bool MATRIX_MANGER::IsIdentity(MATRIX _matrix)
{
if (_matrix.GetRow() != _matrix.GetCol())
return false;
double **matrix = _matrix.GetMatrix();
for (int i = 0; i < _matrix.GetRow(); i++)
{
for (int j = 0; j < _matrix.GetCol(); j++)
{
// main diagonal일 때 1이 아니면 단위행렬이 아니다.
if (i == j)
{
if (matrix[i][j] != 1)
return false;
else
continue;
}
// 그 외에는 모두 0이어야 한다.
if (matrix[i][j] != 0)
return false;
}
}
return true;
}