본문 바로가기

임시보관함

매트릭스 연산을 위한 C++프로그래밍

1. 프로그램 개요


    ◆ 본 프로그램은 컴퓨터 연산의 기본이 되고 또한 통계학에서 많이 다루게 되는 행렬의

        연산과 역행렬을 구하는 기능을 한다.      

    ◆ 우선 Matrixmain클래스와 Matrix클래스 LUDecomposition클래스 이렇게 세개의

        클래스로  구성되어 있다.

         ▶ Matrix클래스는 Matrix의 생성과 덧셈, 뺄셈, 곱, 행렬식값, 역행렬, 동치 판단의

             기능이 있다.

         ▶ Matrixmain클래스는 생성된 Matrix의 행과 열, 원소값을 할당해주면 과제에 명시된

             연산을 수행한다.

         ▶ LUDecomposition 클래스는 행렬을 상삼각과 하삼각 행렬로 분해를 한다.

             이 프로그램 소스의 핵심 부분이며 역행렬을 구하는 방법중에 한가지이다.

       

      ◆ 작동 원리 : Matrixmain클래스에서 행렬의 행과 열, 원소값을 할당하면 Matrix클래스

                         에서 행렬을 생성한다. 연산에따라 덧셈 곱 뺄셈등 여러가지를 할수 있다.,

                         그리고 역행렬을 구할때는  LUDecomposition클래스에서 생성된 행렬을

                         하삼각 상삼각행렬로 분해후에 연산에 따라 역행렬을 만든다.

 

2. 프로그램 작성시 사용된 이론

    ◆다음과 같은 행렬이 있다고 할때

        

 

    ◆ 행렬의 덧셈

        ▶ 위 두행렬 사이에 n = n' , m = m' 이어야 덧셈이 가능하다.

        ▶ 덧셈의 공식은 다음과 같다.

              

         ▶ 결과 행렬을 C라고 할때 일반적인 식은 다음과 같다.

                                        

                                                        

  ◆ 행렬의 뺄셈

       ▶ 위 두행렬 사이에 n = n' , m = m' 이어야 뺄셈이 가능하다.

       ▶ 뺄셈의 공식은 다음과 같다.

 

       ▶ 결과 행렬을 C라고 할때 일반적인 식은 다음과 같다.


 

  ◆ 행렬의 곱셈  
     ▶ 위 두행렬 사이에  m = n' 이어야 곱셈이 가능하다.
     ▶ 곱셈의 공식은 다음과 같다.
 

       

    

       ▶ 결과 행렬을 C라고 할때 일반적인 식은 다음과 같다.

                       

                                         

   

   ◆ 행렬의 동치 
       ▶ 위 두행렬 사이에 n = n, m = m' 조건이 만족되어야 한다.
       ▶ 두 행렬의 동치는 다음과 같다.
               
                                   
 
  ◆ 행렬의 역행렬
      ▶ 정방행렬의 역행렬 구하기 (LU분해법)
      ▶ LU 분해법이란??
          행렬을 상삼각행렬과 하삼각행렬로 나누어서 다음과 같은 연산으로 역행렬을 구한다.
           

      위와 같은 수순으로 역행렬을 구할수가 있다. 그럼 우선 LU분해하는 방법을 알아 보자.
        
              ★LU분해법★  <--클릭
 
 
   ◆ 행렬의 행렬식 
       ▶ 위에서 분해한 상삼각행렬을 이용하면 행렬식값은 쉽게 구할수 있다.
       ▶ 상삼각행렬의 원소를 a라 하고 가우스 소거법 시행중에 일어난 행교체의 횟수를 i라고
           하면 행렬식 값은 다음과 같다.
 
                              
   
 
 
 3. 프로그램 작성 코드
         

==================================================================================

Matrixmain클래스

==================================================================================

/*****

 * author 재철

 * Date   4 16

 * Matrixmain클래스 행렬값을 할당 해주고 계산 값들을 출력하는 클래스

 */

class Matrixmain

{

   public static void main(String[] args) {

       Matrix A = new Matrix(4,4);              //행렬의 행과 할당

       A.mat = new double[][]{{1,0,6,8},{0,1,5,4},

                               {0,0,-1,0},{0,0,0,-1}}; //행렬 원소 할당

 

       result(A);          

       }

 /*****

  * 과제유형에 따른 행렬 계산 

  * @param A Matrix 변수

  */

   public static void result(Matrix A) {

      LUDecomposition lu = new LUDecomposition(A);  //LU분해

      Matrix L = lu.getL(A);             //lower triangle

      Matrix U = lu.getU(A);             //upper triangle

      int pive = lu.piv();              //행변환에 따른 det부호

      double detA = A.det(U,pive);       //행렬식 계산

 

      Matrix inverA = A.inverse(L,U,pive);     //역행렬 계산

      /***

       * A*A+A-invA 계산과 행렬식값 출력

       */

      Matrix A1 = A.times(A);

      Matrix A2 = A1.add(A);

      Matrix A3 = A2.subtract(inverA);

      System.out.println("A*A-A+invA = ");

      A3.printMatrix();

      System.out.println("det(A) = "+detA);

      }

}

==================================================================================

Matrix클래스 

==================================================================================

/*****

 * @author 박재철

 * Date    4 16

 * Matrix클래스 Matrix생성자를 생성하고 덧셈, 뺄셈, 역행렬등 행렬의 연산을

 * 하는 클래스.

 */

class Matrix {           

    public int row;                 //생성할 행렬의

    public int col;                 //생성할 행렬의

    public double[][] mat;          //행렬들의

 

    /*****

     * 행렬 생성하는 메소드

     * @param row int 변수

     * @param col int 변수

     */

    public Matrix(int row, int col) {

       this.row = row;

       this.col = col;

       this.mat = new double[row][col];

    }

   /*****

    * 행렬의 덧셈을 하는 메소드

    * @param B Matrix 변수

    * @return result 계산된 행렬

    */

     public Matrix add(Matrix B) {

         Matrix result = new Matrix(B.row,B.col);

         if(this.row == B.row && this.col == B.col){ //행렬덧셈의 조건

             for(int i=0; i<B.row; i++) {

                for(int j=0; j<B.col;j++) {

                   result.mat[i][j]= this.mat[i][j]+

                                      B.mat[i][j];

                }

             }

         }else{

             System.out.println(" 행렬은 덧셈이 불가능합니다.");

         }

         return result;

     }

    /*****

     * 행렬의 뺄셈을 계산하는 메소드

     * @param B Matrix 변수

     * @return result 계산된 행렬

     */

     public Matrix subtract(Matrix B){

         Matrix result = new Matrix(B.row,B.col);

         if(this.row == B.row && this.col == B.col){//행렬 뺄셈 조건

            for(int i=0; i<B.row; i++) {

                for(int j=0; j<B.col;j++) {

                   result.mat[i][j]=this.mat[i][j]-B.mat[i][j];

                }

            }

         }else{

             System.out.println(" 행렬은 뺄셈이 불가능합니다.");

         }

         return result;

     }

    /*****

     * 행렬의 곱을 계산하는 메소드

    * @param B Matrix 변수

    * @return result 계산된 행렬

     */

     public Matrix times(Matrix B) {

        Matrix result = new Matrix(B.row,B.col);

        if(this.col == B.row){              //행렬 조건

           for(int i=0; i<this.row; i++) {

               for(int j=0; j<this.col;j++) {

                   for(int k = 0; k < this.col; k++){

                       result.mat[i][j]=result.mat[i][j]

                                        +this.mat[i][k]

                                        *B.mat[k][j];

                   }

               }

            }

         }else{

             System.out.println(" 행렬은 곱셈이 불가능합니다.");

         }

         return result;

      }

        

    /*****

     * 행렬을 출력하는 메소드

    */

     public void printMatrix() {

        for(int i=0; i<this.row; i++) {

            for(int j=0; j<this.col;j++) {

                System.out.print(this.mat[i][j]+"  ");

            }

            System.out.println();

         }

     }

 

    /*****

    * 행렬식 값을 계산하는 메소드

     * @param U Matrix 변수

     * @param piv int 변수  : 행변환에 따른 행렬식값 부호

     * @return piv*determin  행렬식

     */

    public double det(Matrix U,int piv) {

      double determin = 1;                          

      for(int i=0; i<this.row; i++) {

          determin *= U.mat[i][i];        //상삼각의 대각원소

      }

      return piv*determin;

    }

    /*****

     * 역행렬을 구하는 메소드

    * @param L Matrix 변수

     * @param U Matrix 변수

     * @param piv int 변수

     * @return invers 역행렬

     */

     public Matrix inverse(Matrix L, Matrix U, int piv) {

        Matrix D = new Matrix(this.row,this.col);

        Matrix invers = new Matrix(this.row,this.col);

              

        if(this.det(U, piv) == 0) {       //역행렬 존재 여부

           System.out.println(" 역행렬이 존재 하지 않습니다.");

           System.exit(0);

        }

        /*****

         * A = LU 분해가 가능 행렬이면

         * LUx = I 에서 Ux = D 하면 LD =I이므로 D 구하면

         * Ux = D 에서 x A 역행렬

         * 2번의 수식을 참고

         */

        for(int i=0; i<this.row ;i++) {   

            for(int j=0; j<this.col; j++){

                if(i==j){

                    D.mat[i][i] = 1;

                    continue;

                }

            for(int k=j; k<=i-1; k++) {

                if(i<j){

                    D.mat[i][j] = 0;

                }else{

                    D.mat[i][j] += (-1/L.mat[i][i])*L.mat[i][k]

                                   *D.mat[k][j];

                }

            }

        }

    }

              

       for(int i=this.row-1; i >= 0; i--) {

           for(int j=this.row-1; j >= 0; j--) {

               for(int k=this.row-1; k >= i+1; k--){

                   if(i == this.row-1) {

                      invers.mat[this.row-1][j]=D.mat[this.row-1][j]

                                /U.mat[this.row-1][this.row-1];

                   }else{

                   invers.mat[i][j] += U.mat[i][k]*invers.mat[k][j];

                   }

                }

                invers.mat[i][j] = (1/U.mat[i][i])*

                                   (D.mat[i][j]-invers.mat[i][j]);

            }

       }

       return invers;

}

 

    /*****

     *행렬의 동치 여부를 검사

    * @param B Matrix 변수

     * @return 행렬이 동치 인지 아닌지 boolean 변수

    */

     public boolean equals(Matrix B){

        if(this.col != B.col || this.row != B.row)

           return false;

          else{

             for(int i=0 ; i<this.row ; i++){

                for(int j=0 ; j<this.col ; j++){

                    if(this.mat[i][j] != B.mat[i][j]){

                       return false;

                     }

                 }

             }

         }

         return true;

     }          

}

==================================================================================

 LUDecomposition클래스 

==================================================================================

/*****

 * @author 박재철

 *  Date   4 16

 *  LUDecomposition 클래스 행렬을 LU형태로 분해 시키는 클래스

 */

public class LUDecomposition 

{

    double[][] getL;           //하삼각 행렬

    double[][] getU;           //상삼각 행렬

    int pivsign = 1;           //행변환에 따른 부호

 

    public LUDecomposition(Matrix B) {

        Matrix L = new Matrix(B.row,B.col);

        getL = new double[B.row][B.col];

        getU = new double[B.row][B.col];

 

        int cnt = 0, replaceRow = 0;

        double[][] m = new double[B.row][B.col];  

        for(int i=0; i<B.row-1; i++){

            replaceRow = i+1;  

            cnt = 0;

               /*****

                * 대각원소가 0이면 pivoting 필수 while부분은

                            * pivoting 부분을 담당

                */

             while(B.mat[i][i] == 0 && replaceRow  < B.row) { 

                  B = permutation(B,i,replaceRow);

                  cnt += 1;

                  replaceRow += cnt;

                  pivsign = -pivsign;

              }

              if(replaceRow == B.row){

                   continue;

             }

               //가우스 소거법에 의한 상삼각행렬 만들기

              for(int j=i+1; j<B.col; j++) {

                   //가우스 소거법이용시 사용하는 계수들

                 m[i][j] = B.mat[j][i]/B.mat[i][i];               

                 for(int k=i; k<B.col; k++) {

                   B.mat[j][k] = B.mat[j][k]- m[i][j]*B.mat[i][k];

                                  

                    //상삼각행렬을 만들때 이용한 계수를 사용하여 하삼각행렬 생성

                        if(i<j){

                            L.mat[j][i] = m[i][j];   

                        }

                        L.mat[k][k] = 1;  

                     }

                }

            }

                   

            //만들어진 LU행렬을 각각 배열 값으로 저장

             for(int l=0; l<B.row; l++) {

                 for(int h=0; h<B.col;h++) {           

                     getL[l][h] =  L.mat[l][h];

                     getU[l][h] =  B.mat[l][h];

                  }

              }

         }

      

       /*****

        * 치환 행렬 담당 하는 메소드

        * @param B Matrix 변수

        * @param r int 변수        : 대각원소가 0인행

        * @param newr int 변수    : 행과 교체될

        * @return 치환된 행렬

        */

      

       public Matrix permutation(Matrix B ,int r, int newr) {

             /****

              * 치환 행렬 생성 부분 permutat 치환 행렬이며 행렬과

              * 곱셈 연산을 수행하면

              *  r행과 newr행의 교체가 이루어진다.

              */

             Matrix permutat = new Matrix(B.row,B.col);

             for(int i=0; i<B.row ; i++) {

                    for(int j=0; j<B.col ; j++) {

                           if(i == j) {

                                 permutat.mat[i][j] = 1;

                           }

                           else {

                                 permutat.mat[i][j] = 0;

                           }

                    }

              }

              permutat.mat[r][r] = permutat.mat[newr][newr] = 0;

              permutat.mat[r][newr] = permutat.mat[newr][r] = 1;

              Matrix C = permutat.times(B);

              return C;

        }

 

       /****

        * 하삼각행렬 반환 하는 메소드

        * @param B Matrix 변수

        * @return L 하삼각행렬

        */

       public Matrix getL(Matrix B) {

             Matrix L = new Matrix(B.row,B.col);

              for(int i=0; i<B.row; i++) {

                     for(int j=0; j<B.col;j++) {

                             L.mat[i][j] = getL[i][j];

                     }

              }

              return L;

       }

       /****

        * 상삼각행렬 반환 하는 메소드

        * @param B Matrix 변수

        * @return U 상삼각행렬

        */

       public Matrix getU(Matrix B) {

             Matrix U = new Matrix(B.row,B.col);

              for(int i=0; i<B.row; i++) {

                     for(int j=0; j<B.col;j++) {

                             U.mat[i][j] = getU[i][j];

                     }

              }

              return U;

       }

       /*****

        * 행변환에 따른 행렬식 부호결정하는 메소드

        * @return pivsign 행렬식 부호

        */

       public int piv() {

             return pivsign;

       }

}

==================================================================================


4. 프로그램 작성시 발생한 에러

 

    ◆ 프로그램 작성시에 Eclipse를 사용하여 별다른 에러는 발생하지 않았습니다.

        다음과 같은 에러 외에는 발생하지 않았습니다.

       
 
D:\>javac Matrixmain.java
Matrixmain.java:27: det(Matrix,int) in Matrix cannot be applied to (Matrix,double)

                double detA = A.det(U,pive);                
                                  ^
Matrixmain.java:29: inverse(Matrix,Matrix,int) in Matrix cannot be applied to (Matrix,Matrix,double)
                Matrix inverA = A.inverse(L,U,pive);                                        
    ^
2 errors
D:\>
        에러 종류 : 컴파일 에러
        에러 원인 : 에소드에서 인자를 받을때 같은 형의 인자가 아닐때 발생
        해결 방안 : 인자의 형을 통일 시켜준다.
 
     ◆ 프로그램을 작성하면서 에러라기보다는 수식의약간의 틀림으로 인해서 오차가 많이
         나고 값도 다르고 해서 여러번의 반복 작업 끝에 정확한 값을 계산하게
         되었습니다.        

5.  출력 결과 및 R과비교

  


                           =====  java  ======        ====== R =======

A*A-A+invA =         1.0  0.0  0.0  0.0              1    0    0    0
                            0.0  1.0  0.0  0.0              0    1    0    0
                            0.0  0.0  1.0  0.0              0    0    1    0
                            0.0  0.0  0.0  1.0              0    0    0    1
                           =================         ================
        det(A) =                  1.0                             [1] 1


<R 코드>
x<- matrix(c(1,0,6,8,0,1,5,4,0,0,-1,0,0,0,0,-1),nrow = 4,byrow=T)
x%*%x - x +solve(x)

 det(x)


6. 레포트 작성시 애로 사항 및 건의 사항
     ◆  가장 어려웠던 부분은 역행렬을 구하는 부분입니다. 작년에 배운 수치해석을

          기반으로 LU분해법을 사용하게 되었는데 생각보다 수식이 복잡하고 어려웠

          습니다. 특히 분해후에 역행열을 구하는 과정도 꽤 복잡해서 고생을 좀 한거

          같습니다.


7. 참고한 책과 웹사이트

      ◆ 저자 : Rcihard L.Burden / J. Douglas Faires

                    번역 : 유해영 심홍태 허정연

          책이름 : 수치해석

          출판사 : 사이텍미디어

출처 : Tong - 93kcsup님의 Visual C++통