Расчет определителя методом Гаусса

от автора

Здравствуйте. Не так давно, выполняя задание по программированию (JAVA), я столкнулся с проблемой, поиск решения в интернете ничего существенного не дал, но в итоге я справился сам и решил выложить свое решение сюда, так как Хабр часто выдается в первых 10 результатах поиска и, возможно, когда-нибудь это кому-то поможет.

Теперь суть проблемы

Проверяя свою функцию по вычислению определителя на примере матрицы:
1 2 3
4 5 6
7 8 9

я получал вот такой результат:
1 2 3
0 -3 -6
0 -14 -28

а должен:

1 2 3
0 -3 -6
0 0 0

то есть вторая строка расчитывается верно, а последняя нет, когда начал проверять дебагером, выяснил вот что:
при i=0, первая строка верно вычитается и из второй и из третей и мы получаем:

1 2 3
0 -3 -6
0 -6 -12

но затем внешний цикл заходит на новый круг и i=1, и далее мы начинаем вычитать вторую строку из последующих, и считает он неверно, по причине того, что берет один элемент из исходной матрицы (mt1.getElem(j, i)), которая неизменна, но если этот элемент заменить на mt.getElem(j, i), то тогда будет неправильно вычитаться даже первая строка из второй, и мы получим:

1 2 3
0 5 6
0 0 9

При этом код, на тот момент, был вот в таком состоянии:

    public static double gauss (Matrix mt1) {     int rows = mt1.getSize().getRows();     int cols = mt1.getSize().getColumns();     double result = 1;     //копируем полученную матрицу     Matrix mt = new Matrix();     mt.setSize(rows, cols);     for (int i=0; i<rows; i++){         for (int j=0; j<cols; j++){             mt.setElem(i, j, mt1.getElem(i, j));         }     }     //строим треугольную матрицу     for (int i=0; i<cols; i++){         for (int j=i+1; j<rows; j++){             for (int k=i; k<cols; k++){                 double tmp = mt.getElem(j, k)-((mt.getElem(i, k)*mt1.getElem(j, i))/mt.getElem(i, i));                 mt.setElem(j, k, tmp);             }                         }     }     System.out.println(mt.toString());     for (int x=0; x<rows; x++){         result = result*mt.getElem(x, x);     }     return result; } 

Функции getElem, setElem, setSize, getSize, getRows, getColumns были определены в другом классе.

Решение

… оказалось, как всегда простым

Когда я подавал матрицу на вход функции, то я тут же её копировал, чтобы не испортить исходную матрицу, так как она еще могла понадобиться в работе. И получалось так, что после проведения изменений в матрице, она перезаписывалась сама в себя. Но один из элементов (коэффициент умножения) надо было брать из исходной матрицы, так как если его брать из «рабочей», то во после окончания внутреннего цикла этот элемент менялся и в дальнейшем коэффициенты и, следовательно, определитель, считались неверное. Но если брать этот элемент из исходной матрицы, то корректно занулялся только первый столбец, для второго и последующих коэффициент должен рассчитываться, исходя из изменившихся значений матрицы, а он брался из исходных, и опять же рассчитывался неверный определитель.

А решалось все довольно просто. Надо было лишь завести еще одну матрицу, назовем ее «статической» в которую я копировал «рабочую» перед занулением и нужные коэффициенты брались из нее, таким образом в расчете всегда участвовали актуальные данные. Вот код:

/*Матрицы mt и mt2 необходимы, чтобы не испортить получаемую матрицу mt1,     которая может быть использована в других областях программы     строим треугольную матрицу*/     for (int i=0; i<cols; i++){                  //проверка на НЕнулевой i-тый элемент строки         if (mt.getElem(i, i)==0 && i<rows-1)         {         count = i;             do{             mt.getElem(count, 0);             count++;             }while(mt.getElem(count, 0) == 0);         mt = MatrixCalculator.swapLine(i, count, mt);         result = -result; // Если была совершена перестановка —          }                 // меняем знак определителя                  /*копирование "рабочей" матрицы в "статическую", необходимо          для корректного расчета коофициентов умножения строк перед вычитанием*/         for (int x=0; x<rows; x++){             for (int y=0; y<cols; y++){                 mt2.setElem(x, y, mt.getElem(x, y));             }         }                  //зануление i-того столбца                  for (int j=i+1; j<rows; j++){             for (int k=i; k<cols; k++){                 double tmp = mt.getElem(j, k)-((mt.getElem(i, k)*mt2.getElem(j, i))/mt.getElem(i, i));                 mt.setElem(j, k, tmp);             }                         }              }     //Вычисление определителя     for (int x=0; x<rows; x++){         result = result*mt.getElem(x, x);     }       return result; } 
Заключение

Предоставлю полный код приложение, может кому интересно. Спасибо за внимание и интересно было бы послушать как бы вы решили такую проблему.

Код

Главный класс, точка входа в приложение

package matrix;  import java.util.Arrays; import java.util.Scanner;  /**  * Приложение для выполнения следующих операций с квадратными марицами:<hr/>  * -Сложение<hr/>  * -Умножение<hr/>  * -Транспонирование<hr/>  * -Нахождение определителя<hr/>  * -Решение СЛАУ методом Крамера<hr/>  * -Сортировка массива матриц по возрастанию значения определителя<hr/>  * @author группа: АС-10и2 студенты: Курганов И.Д., Иванов И.А.  */ public class Main {      /**      * @param args the command line arguments      */     public static void main(String[] args) {         /*Так как для 4 из 6 пунктов нам нужен идентичный ввод матриц,           *его мы вынесли за главный цикл          */         Scanner sc = new Scanner(System.in);         System.out.print("Введите размерность матриц: ");         int rows = sc.nextInt();         double value;         //Создаем первую матрицу         Matrix mat1 = new Matrix();         mat1.setSize(rows, rows);         //Создаем вторую матрицу         Matrix mat2 = new Matrix();         mat2.setSize(rows, rows);         //Вводим значения первой матрицы         System.out.println("Ввод первой матрицы: ");         for (int i=0; i<rows; i++){             int iNum = i+1;             for (int j=0; j<rows; j++){                 int jNum = j+1;                 System.out.println("Введите "+jNum+" элемент "+iNum+" столбца: ");                 value = sc.nextDouble();                 mat1.setElem(i, j, value);             }         }         //Вводим значения второй матрицы         System.out.println("Ввод второй матрицы: ");         for (int i=0; i<rows; i++){             int iNum = i+1;             for (int j=0; j<rows; j++){                 int jNum = j+1;                 System.out.println("Введите "+jNum+" элемент "+iNum+" столбца: ");                 value = sc.nextDouble();                 mat2.setElem(i, j, value);             }         }         int menuItem = 0;         if (menuItem >= 0 && menuItem <=6){         //Главный цикл         do {             System.out.println("--------------Меню---------------");             System.out.println("1. Сложение введенных матриц");             System.out.println("2. Умножение введенных матриц");             System.out.println("3. Транспонирование первой матрицы");             System.out.println("4. Нахождение определителя первой матрицы");             System.out.println("5. Решение СЛАУ методом Крамера");             System.out.println("6. Сортировка массива матриц по возрастанию определителей матриц");             System.out.println("0. Выход");             menuItem = sc.nextInt();             switch (menuItem){                 case 1:{                     mat1 = MatrixCalculator.addition(mat1, mat2);                     System.out.println("Сумма матриц: ");                     System.out.println(mat1.toString());                     break;                 }                 case 2:{                     mat1 = MatrixCalculator.multiplication(mat1, mat2);                     System.out.println("Произведение матриц: ");                     System.out.println(mat1.toString());                     break;                 }                 case 3:{                     mat1 = MatrixCalculator.transpose(mat1);                     System.out.println("Транспонированная матрица: ");                     System.out.println(mat1.toString());                     break;                 }                 case 4:{                     value = MatrixCalculator.gauss(mat1);                     if (Math.abs(value) == 0){                         System.err.println("Определитель равен нулю — матрица вырожденная");                         break;                     }                     System.out.println("Определитель равен: "+value);                     break;                 }                    case 5:{                     System.out.print("Введите количество уравнений в СЛАУ: ");                     rows = sc.nextInt();                     mat1 = new Matrix();                     mat1.setSize(rows, rows);                     double [] answers = new double[rows];                     //Ввод системы уравнений                         for (int i=0; i<rows; i++) {                             int iNum = i+1;                             for (int j=0; j<rows; j++) {                                 int jNum = j+1;                                 System.out.print("Введите "+jNum+" элемент "+iNum+" уравнения: "+"  ");                                 value = sc.nextDouble();                                 mat1.setElem(i, j, value);                             }                             System.out.println("Введите ответ на "+iNum+" уравнение системы: ");                             double val = sc.nextDouble();                             answers[i] = val;                         }                                          double [] f = new double[rows];                     f = MatrixCalculator.kramer(mat1, answers);                     System.out.print("Корни СЛАУ: ");                         for (int i=0; i<rows; i++){                             int iNum = i+1;                             System.out.print("x"+iNum+"="+f[i]+" ");                 }                     System.out.println("");                 break;             }             case 6:{                 System.out.println("Введите длину массива матриц: ");                 int arrLenght = sc.nextInt();                 Matrix [] mArr = new Matrix[arrLenght];                 double determenant;                 //Создание массива матриц                 mArr = MatrixCalculator.matArrayCreate(arrLenght);                 for (int i=0; i<arrLenght; i++){                     System.out.print(mArr[i].toString());                     System.out.print("Определитель матрицы равен: ");                     System.out.println(determenant=MatrixCalculator.gauss(mArr[i]));                     System.out.println("");                 }                 System.out.println("*************************************************");                 System.out.println("Отсортированный массив матриц:");                 System.out.println("");                 Arrays.sort(mArr); //Сортировка и вывод                 for (int i=0; i<arrLenght; i++){                     System.out.print(mArr[i].toString());                     System.out.print("Определитель матрицы равен: ");                     System.out.println(determenant=MatrixCalculator.gauss(mArr[i]));                     System.out.println("");                 }                 break;                 }                              }                      } while(menuItem != 0);         }else{             System.err.println("Неверно выбран пункт меню");}     } } 

Класс Matrix

package matrix;  /**  * Класс <b>Matrix</b> содержит реализацию основных действий с матрицами  * @author группа: АС-10и2 студенты: Курганов И.Д., Иванов И.А.  */ public class Matrix implements Comparable<Matrix> {     private double[][] matrix;          /**      * Конструктор по умолчанию      */     public Matrix(){   } /**  * Функция установки размера матрицы  * @param rows количество строк в матрице. Тип <b>int</b>  * @param columns количество столбцов в матрице. Тип <b>int</b>  */ public void setSize(int rows, int columns){     matrix = new double[rows][columns];   } /**  * Функция получения размера матрицы, необходима, например, при создании  * новой матрицы, для установления размера  * @return размер матрицы. Тип <b>MatrixSize</b>  */ public MatrixSize getSize(){     int rows = matrix.length;     int columns = matrix[0].length;     return new MatrixSize(rows, columns);   } /**  * Функция для установления значения элемента матрицы  * @param rowNum номер строки в матрице. Тип <b>int</b>  * @param colNum номер столбца в матрице. Тип <b>int</b>  * @param value новое значение элемента массива с указанными координатами. Тип <b>double</b>  */ public void setElem(int rowNum, int colNum, double value){     matrix[rowNum][colNum] = value;   } /**  * Функция получения значения элемента матрицы  * @param rowNum номер строки в матрице. Тип <b>int</b>  * @param colNum номер столбца в матрице. Тип <b>int</b>  * @return значение элемента массива с указанными координатами. Тип <b>double</b>  */ public double getElem(int rowNum, int colNum){     return matrix[rowNum][colNum];   } /**  * Функция получения определителя матрицы. Основана на методе <b>Gauss</b>, класса <b>MatrixCalculator</b>.  * Сделана для удобства  * @param mt1 матрица, опредеелитель которой необходимо вычислить. Тип <b>Matrix</b>  * @return det - значение определителя матрицы. Тип <b>double</b>  */ public double getDet(Matrix mt1){     double det = MatrixCalculator.gauss(mt1);     return det; } /**  * Функция создания  новой матрицы (конструктор с параметрами).   * @param rows количество строк в матрице. Тип <b>int</b>  * @param columns количество столбцов в матрице. Тип <b>int</b>  * @return mt - матрицу, с указанными размерами. Тип <b>Matrix</b>  */ public static Matrix creatMatrix(int rows, int columns){     Matrix mt = new Matrix();     mt.setSize(rows, columns);     return mt;   } /**  * Функция приведения матрицы к строке.  * @return Представленную в виде строки матрицу.  */     @Override public String toString(){     StringBuilder sb = new StringBuilder();     for(int i=0; i<matrix.length; i++){     for(int j=0; j<matrix[i].length; j++){     sb.append(matrix[i][j] + "   ");   }          sb.append("\n");   }      return sb.toString(); } /**  * Функция сравнения объектов  * Необходима для метода <b>Array.sort</b>, который сортирует по возрастанию  * элементы массива матриц.  * @param mat матрица сравниваемая с текущей матрицей — элементом массива  * @return -1 — текущая матрица меньше полученной, 0 — равна, 1 — больше  */     @Override public int compareTo(Matrix mat) {    Matrix tmp = (Matrix)mat;     if(this.getDet(this) < tmp.getDet(tmp))       {         /* текущее меньше полученного */         return -1;       }            else if(this.getDet(this) > tmp.getDet(tmp))       {         /* текущее больше полученного */         return 1;       }         /* текущее равно полученному */         return 0;      } /**  * Класс для работы с размерами матриц  */ public class MatrixSize{     private int rows, columns; /**  * Конструктор с парамерами  * @param rows количество строк в матрице. Тип <b>int</b>  * @param columns количество столбцов в матрице. Тип <b>int</b>  */     public MatrixSize(int rows, int columns){     this.rows = rows;     this.columns = columns; } /**  * Функция для получения значения количества строк в матрице  * @return Количество строк. Тип <b>int</b>  */ public int getRows(){     return this.rows; } /**  * Функция для получения значения количества столбцов в матрице  * @return Количество столбцов. Тип <b>int</b>  */ public int getColumns(){     return this.columns; } } } 

Библеотека функций, класса Matrix

package matrix;  import java.util.Random; import java.util.Scanner;  /**  * Библиотека функций для работы с квадратными матрицами.  * Содержит следующие методы:  * - <b>creatMatrix</b> — создание матрицы  * - <b>addition</b> — сложение 2х матриц  * - <b>multiplication</b> — умножение 2х матриц  * - <b>swapLine</b> — смена местами 2х строк матрицы  * - <b>transpose</b> — транспонирование матрицы  * - <b>gauss</b>— нахождение определителя методом Гаусса  * - <b>matArrayCreate</b> — создание массива матриц  * - <b>kramer</b> — решение СЛАУ методом Крамера  * - <b>matArrayCreate</b> — создание массива матриц  * @author группа: АС-10и2 студенты: Курганов И.Д., Иванов И.А.  */ public class MatrixCalculator{     	private MatrixCalculator() { } /**  * Функция создания матрицы по уже имеющимся значениям  * @param rows количество строк в матрице. Тип <b>int</b>  * @param columns количество столбцов в матрице. Тип <b>int</b>  * @param data двумерный массив данных, элементы которого будут занесены элементы матрицы. Тип <b>double [][]</b>  * @return матрицу mt.Тип <b>Matrix</b>  */         public static Matrix creatMatrix(int rows, int columns, double[][] data){     Matrix mt = Matrix.creatMatrix(rows, columns);         for(int i=0; i<data.length; i++){             for(int j=0; j<data[i].length; j++){         mt.setElem(i, j, data[i][j]);             }         }     return mt;   } /**  * Функция сложения 2х матриц.  * @param mt1 первое слагаемое. Тип <b>Matrix</b>  * @param mt2 второе слагаемое. Тип <b>Matrix</b>  * @return Матрицу, элементы которой — суммы соответствующих элементов матриц <b>mt1</b> и <b>mt2</b>. Тип <b>Matrix</b>  */ public static Matrix addition(Matrix mt1, Matrix mt2) {     int rows = mt1.getSize().getRows();     int cols = mt1.getSize().getColumns();     int rows2 = mt2.getSize().getRows();     int cols2 = mt2.getSize().getColumns();         if ((rows == rows2) && (cols == cols2)) {             Matrix mt = Matrix.creatMatrix(rows, cols);             for (int i = 0; i < rows; i++) {                 for (int j = 0; j < cols; j++) {                 mt.setElem(i, j, mt1.getElem(i, j) + mt2.getElem(i, j));                 }             }     return mt; } else {     return null;     }   } /**  * Функция умножения 2х матриц  * @param mt1 первое умножаемое. Тип <b>Matrix</b>  * @param mt2 первое умножаемое. Тип <b>Matrix</b>  * @return Матрицу, элементы которой — произведения соответствующих элементов матриц <b>mt1</b> и <b>mt2</b>. Тип <b>Matrix</b>  */ public static Matrix multiplication(Matrix mt1, Matrix mt2) {     int rows = mt1.getSize().getRows();     int cols = mt1.getSize().getColumns();     int rows2 = mt2.getSize().getRows();     int cols2 = mt2.getSize().getColumns();         if (cols == rows2) {             Matrix mt = Matrix.creatMatrix(rows, cols2);             double tmp = 0;             for (int i = 0; i < rows; i++) {                 for (int c = 0; c < cols2; c++) {                     for (int j = 0; j < cols; j++) {                     tmp = mt1.getElem(i, j) * mt2.getElem(j, c);                     }                 mt.setElem(i, c, tmp);                 tmp = 0;                 }             }     return mt; } else {     return null;     }   } /**  * Функция смены местами 2х строк матрицы  * @param swopLine номер строки, которую необходимо заменить строкой <b>targetLine</b>. Тип <b>int</b>  * @param targetLine номер целевой строки, которой необходимо заменить строку <b>swopLine</b>. Тип <b>int</b>  * @param mt1 матрица, в которой проводится замена строк. Тип <b>Matrix</b>  * @return матрицу, в которой строки <b>swopLine</b> и <b>targetLine</b>, изменены местами  */ public static Matrix swapLine(int swopLine, int targetLine, Matrix mt1) {     int rows = mt1.getSize().getRows();     int cols = mt1.getSize().getColumns();     double tmp;     int secondLine = targetLine;         for (int firstLine=swopLine; swopLine<rows; swopLine++) {             for (int j=0; j<cols; j++) {                 tmp = mt1.getElem(firstLine, j);                 mt1.setElem(firstLine, j, mt1.getElem(secondLine, j));                 mt1.setElem(secondLine, j, tmp);             }         }     return mt1; } /**  * Функция транспонирования матрицы  * @param mt1 тарнспонируемая матрица. Тип <b>Matrix</b>  * @return тарнспонированную матрицу. Тип <b>Matrix</b>  */ public static Matrix transpose (Matrix mt1) {     int rows = mt1.getSize().getRows();     int cols = mt1.getSize().getColumns();     Matrix mt = Matrix.creatMatrix(rows, cols);     double tmp = 0;         for (int i=0; i<rows; i++){             for (int j=0; j<cols; j++){                 tmp = mt1.getElem(i, j);                 mt.setElem(j, i, tmp);                 tmp = 0;             }         }     return mt; }  /**  * Функция нахождения определителя матрицы методом Гаусса  * @param mt1 матрица, определитель которой необходимо найти. Тип <b>Matrix</b>  * @return result — значение определителя матрицы. Тип <b>double</b>   */ public static double gauss (Matrix mt1) {     int rows = mt1.getSize().getRows();     int cols = mt1.getSize().getColumns();     double result = 1;     int count;     /*копируем полученную матрицу в "рабочую" матрицу mt (Она обрабатывается       в циклах)*/     Matrix mt = new Matrix();     mt.setSize(rows, cols);     for (int i=0; i<rows; i++){         for (int j=0; j<cols; j++){             mt.setElem(i, j, mt1.getElem(i, j));         }     }     /*копируем полученную матрицу в "статическую" матрицу mt2, в неё       копируется "рабочая матрица" mt, после зануления очередного столбца*/     Matrix mt2 = new Matrix();     mt2.setSize(rows, cols);     for (int i=0; i<rows; i++){         for (int j=0; j<cols; j++){             mt2.setElem(i, j, mt1.getElem(i, j));         }     }     /*Матрицы mt и mt2 необходимы, чтобы не испортить получаемую матрицу mt1,     которая может быть использована в других областях программы     строим треугольную матрицу*/     for (int i=0; i<cols; i++){                  //проверка на НЕнулевой i-тый элемент строки         if (mt.getElem(i, i)==0 && i<rows-1)         {         count = i;             do{             mt.getElem(count, 0);             count++;             }while(mt.getElem(count, 0) == 0);         mt = MatrixCalculator.swapLine(i, count, mt);         result = -result; // Если была совершена перестановка —          }                 // меняем знак определителя                  /*копирование "рабочей" матрицы в "статическую", необходимо          для корректного расчета коофициентов умножения строк перед вычитанием*/         for (int x=0; x<rows; x++){             for (int y=0; y<cols; y++){                 mt2.setElem(x, y, mt.getElem(x, y));             }         }                  //зануление i-того столбца                  for (int j=i+1; j<rows; j++){             for (int k=i; k<cols; k++){                 double tmp = mt.getElem(j, k)-((mt.getElem(i, k)*mt2.getElem(j, i))/mt.getElem(i, i));                 mt.setElem(j, k, tmp);             }                         }              }     //Вычисление определителя     for (int x=0; x<rows; x++){         result = result*mt.getElem(x, x);     }       return result; } /**  * Функция решения СЛАУ методом Крамера  * @param mt1 коофиценты СЛАУ. Тип <b>Matrix</b>  * @param ans массив ответов СЛАУ. Тип <b>double []</b>  * @return <b>xArr</b> — массив корней СЛАУ. Тип <b>double []</b>  */ public static double [] kramer (Matrix mt1, double [] ans) {     int rows = mt1.getSize().getRows();     int cols = mt1.getSize().getColumns();     double [] xArr = new double[rows];     Matrix mtDet = new Matrix();     mtDet.setSize(rows, cols);       for (int i=0; i<rows; i++){         /*Копирование входной матрицы во временную, для нахождения определителя          */         for (int x=0; x<rows; x++){             for (int y=0; y<cols; y++){                 mtDet.setElem(x, y, mt1.getElem(x, y));             }         }              }     //Нахождение Главного определителя     double det = MatrixCalculator.gauss(mtDet);     Matrix mt = new Matrix();     mt.setSize(rows, cols);     //Копирование входной матрицы mt1 в "рабочую" mt     for (int i=0; i<rows; i++){         for (int x=0; x<rows; x++){             for (int y=0; y<cols; y++){                 mt.setElem(x, y, mt1.getElem(x, y));             }         }         //замена j-того столбца, столбцом ответов         for (int j=0; j<cols; j++){             mt.setElem(j, i, ans[j]);         }         //Нахождение корней СЛАУ         xArr[i] = MatrixCalculator.gauss(mt)/det;     }     return xArr; } /**  * Функция создания массива матриц  * @param qty количество элементов массива. Тип <b>int</b>  * @return <b>matArray</b> — массив матриц. Тип <b>Matrix []</b>  */ public static Matrix [] matArrayCreate (int qty) {     Scanner scan = new Scanner(System.in);     Matrix [] matArray = new Matrix[qty];     Random rand = new Random();     System.out.println("Массив будет наполнен случайными числами");     //сделано для удобства, руками долго вбивать         for (int i=0; i<qty; i++){             int number = i+1;             System.out.println("Введите размерность "+number+" матрицы — элемента массива:");             int matSize = scan.nextInt();             Matrix mat = new Matrix();             mat.setSize(matSize, matSize);                 for (int j=0; j<matSize; j++){                     for (int k=0; k<matSize; k++){                                                 mat.setElem(j, k, rand.nextInt(30)-15);                     }                 }             matArray[i]=mat;         }     return matArray; }  } 

ссылка на оригинал статьи http://habrahabr.ru/post/170753/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *