Вычисление обратной матрицы Java
Я пытаюсь вычислить обратную матрицу в Java.
Я следую сопряженному методу (сначала вычисление сопряженной матрицы, затем транспонируйте эту матрицу и, наконец, умножьте ее на обратное значение определителя).
он работает, когда матрица не слишком велика. Я проверил, что для матриц размером до 12x12 результат быстро предоставляется. Однако, когда матрица больше 12x12, время, необходимое для завершения вычисления увеличивается экспоненциально.
матрица, которую мне нужно инвертировать, - 19x19, и это занимает слишком много времени. Метод, который занимает больше времени, является методом, используемым для вычисления определителя.
код, который я использую-это:
public static double determinant(double[][] input) {
int rows = nRows(input); //number of rows in the matrix
int columns = nColumns(input); //number of columns in the matrix
double determinant = 0;
if ((rows== 1) && (columns == 1)) return input[0][0];
int sign = 1;
for (int column = 0; column < columns; column++) {
double[][] submatrix = getSubmatrix(input, rows, columns,column);
determinant = determinant + sign*input[0][column]*determinant(submatrix);
sign*=-1;
}
return determinant;
}
кто-нибудь знает, как вычислить определитель большой матрицы более эффективно? Если нет, кто-нибудь знает, как вычислить обратную большую матрицу с помощью другого алгоритма?
спасибо
10 ответов
экспоненциально? Нет, я считаю, что инверсия матрицы равна O (N^3).
Я бы рекомендовал использовать LU декомпозиция для решения матричного уравнения. Вам не нужно решать для определителя, когда вы его используете.
еще лучше, посмотрите в пакет, чтобы помочь вам. Джама приходит на ум.
12х12 или 19х19 не большие matricies. Это обычное дело для решения проблем с tens or hundreds of тысячи степеней свободы.
вот рабочий пример того, как использовать JAMA. Вы должны иметь JAMA JAR в своем пути к классам при компиляции и запуске:
package linearalgebra;
import Jama.LUDecomposition;
import Jama.Matrix;
public class JamaDemo
{
public static void main(String[] args)
{
double [][] values = {{1, 1, 2}, {2, 4, -3}, {3, 6, -5}}; // each array is a row in the matrix
double [] rhs = { 9, 1, 0 }; // rhs vector
double [] answer = { 1, 2, 3 }; // this is the answer that you should get.
Matrix a = new Matrix(values);
a.print(10, 2);
LUDecomposition luDecomposition = new LUDecomposition(a);
luDecomposition.getL().print(10, 2); // lower matrix
luDecomposition.getU().print(10, 2); // upper matrix
Matrix b = new Matrix(rhs, rhs.length);
Matrix x = luDecomposition.solve(b); // solve Ax = b for the unknown vector x
x.print(10, 2); // print the solution
Matrix residual = a.times(x).minus(b); // calculate the residual error
double rnorm = residual.normInf(); // get the max error (yes, it's very small)
System.out.println("residual: " + rnorm);
}
}
вот та же проблема, решенная с помощью Apache Commons Math, по рекомендации quant_dev:
package linearalgebra;
import org.apache.commons.math.linear.Array2DRowRealMatrix;
import org.apache.commons.math.linear.ArrayRealVector;
import org.apache.commons.math.linear.DecompositionSolver;
import org.apache.commons.math.linear.LUDecompositionImpl;
import org.apache.commons.math.linear.RealMatrix;
import org.apache.commons.math.linear.RealVector;
public class LinearAlgebraDemo
{
public static void main(String[] args)
{
double [][] values = {{1, 1, 2}, {2, 4, -3}, {3, 6, -5}};
double [] rhs = { 9, 1, 0 };
RealMatrix a = new Array2DRowRealMatrix(values);
System.out.println("a matrix: " + a);
DecompositionSolver solver = new LUDecompositionImpl(a).getSolver();
RealVector b = new ArrayRealVector(rhs);
RealVector x = solver.solve(b);
System.out.println("solution x: " + x);;
RealVector residual = a.operate(x).subtract(b);
double rnorm = residual.getLInfNorm();
System.out.println("residual: " + rnorm);
}
}
адаптировать их к вашей ситуации.
Я бы рекомендовал использовать Apache Commons Math 2.0 для этого. Джама-мертвый проект. ACM 2.0 фактически взял линейную алгебру из JAMA и развил ее дальше.
инверсия матрицы вычислительно очень интенсивна. Как duffymo ответил Лу-это хороший алгоритм, и есть другие варианты (ХР, например).
к сожалению вы не можете избавиться от тяжелых расчетов... и, возможно, bottelneck-это метод getSubmatrix, если вы не используете оптимизированную библиотеку.
кроме того, специальные матричные структуры (группа-matricity, симметрия, diagonality, разреженности) оказывают большое влияние на производительность, если учитывать в расчетах. Ваш пробег может отличаться...
вы никогда не хотите вычислять обратную матрицу таким образом. Хорошо, вычисление самого обратного следует избегать, так как почти всегда лучше использовать факторизацию, такую как LU.
вычисление детерминанта с использованием рекурсивных вычислений-это численно непристойная вещь. Оказывается, лучшим выбором является использование факторизации LU для вычисления определителя. Но, если вы собираетесь беспокоиться о вычислении факторов LU, то почему вы, возможно, захотите вычислить инверсный? Вы уже проделали сложную работу, вычисляя коэффициенты LU.
Как только у вас есть факторы LU, вы можете использовать их для замены назад и вперед.
поскольку матрица 19x19 большая, она даже не близка к тому, что я думаю о ней как о большой.
на la4j (линейная алгебра для Java) библиотека поддерживает инверсию матрицы. Вот краткий пример:
Matrix a = new Basic2DMatrix(new double[][]{
{ 1.0, 2.0, 3.0 },
{ 4.0, 5.0, 6.0 },
{ 7.0, 8.0. 9.0 }
});
Matrix b = a.invert(Matrices.DEFAULT_INVERTOR); // uses Gaussian Elimination
ваш алгоритм вычисления определителя действительно экспоненциальный. Основная проблема заключается в том, что вы вычисляете из определения, а прямое определение приводит к экспоненциальному количеству субдетерминант для вычисления. Вам действительно нужно сначала преобразовать матрицу, прежде чем вычислять ее определитель или ее обратный. (Я думал объяснить о динамическом программировании, но эта проблема не может быть решена динамическим программированием, поскольку число подзадач экспоненциально тоже.)
разложение LU, как рекомендовано другими, является хорошим выбором. Если вы новичок в матричном вычислении, вы также можете посмотреть на гауссовское исключение для вычисления детерминант и инверсий, поскольку это может быть немного легче понять сначала.
и одна вещь, которую нужно помнить в инверсии матрицы, - это числовая стабильность, поскольку вы имеете дело с числами с плавающей запятой. Весь хороший алгоритм включает перестановки строк и / или столбцов, чтобы выбрать подходящий повороты, как их называют. По крайней мере, в гауссовском исключении вы хотите на каждом шаге переставлять столбцы так, чтобы в качестве оси выбирался самый большой по абсолютному значению элемент, так как это самый стабильный выбор.
трудно победить Matlab в их игре. Они также cautiou о точности. Если у вас есть 2.0 и 2.00001 в качестве поворотов-берегитесь! Ваш ответ может оказаться очень неточным. Кроме того, проверьте реализацию Python (она находится в numpy / scipy где-то ...)
У вас должно быть точное решение? Приблизительный решатель (Гаусс-Зейдель очень эффективен и прост в реализации), вероятно, будет работать для вас, и будет сходиться очень быстро. 19x19-довольно маленькая матрица. Я думаю, что код Гаусса-Зейделя, который я использовал, мог бы решить матрицу 128x128 в мгновение ока (но не цитируйте меня, это было давно).
поскольку библиотека ACM обновлялась на протяжении многих лет, вот реализация, соответствующая последнему определению для инверсии матрицы.
import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.ArrayRealVector;
import org.apache.commons.math3.linear.DecompositionSolver;
import org.apache.commons.math3.linear.LUDecomposition;
import org.apache.commons.math3.linear.RealMatrix;
import org.apache.commons.math3.linear.RealVector;
public class LinearAlgebraDemo
{
public static void main(String[] args)
{
double [][] values = {{1, 1, 2}, {2, 4, -3}, {3, 6, -5}};
double [][] rhs = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
// Solving AB = I for given A
RealMatrix A = new Array2DRowRealMatrix(values);
System.out.println("Input A: " + A);
DecompositionSolver solver = new LUDecomposition(A).getSolver();
RealMatrix I = new Array2DRowRealMatrix(rhs);
RealMatrix B = solver.solve(I);
System.out.println("Inverse B: " + B);
}
}
для тех, кто ищет инверсию матрицы (не быстро), см.https://github.com/rchen8/Algorithms/blob/master/Matrix.java.
import java.util.Arrays;
public class Matrix {
private static double determinant(double[][] matrix) {
if (matrix.length != matrix[0].length)
throw new IllegalStateException("invalid dimensions");
if (matrix.length == 2)
return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
double det = 0;
for (int i = 0; i < matrix[0].length; i++)
det += Math.pow(-1, i) * matrix[0][i]
* determinant(minor(matrix, 0, i));
return det;
}
private static double[][] inverse(double[][] matrix) {
double[][] inverse = new double[matrix.length][matrix.length];
// minors and cofactors
for (int i = 0; i < matrix.length; i++)
for (int j = 0; j < matrix[i].length; j++)
inverse[i][j] = Math.pow(-1, i + j)
* determinant(minor(matrix, i, j));
// adjugate and determinant
double det = 1.0 / determinant(matrix);
for (int i = 0; i < inverse.length; i++) {
for (int j = 0; j <= i; j++) {
double temp = inverse[i][j];
inverse[i][j] = inverse[j][i] * det;
inverse[j][i] = temp * det;
}
}
return inverse;
}
private static double[][] minor(double[][] matrix, int row, int column) {
double[][] minor = new double[matrix.length - 1][matrix.length - 1];
for (int i = 0; i < matrix.length; i++)
for (int j = 0; i != row && j < matrix[i].length; j++)
if (j != column)
minor[i < row ? i : i - 1][j < column ? j : j - 1] = matrix[i][j];
return minor;
}
private static double[][] multiply(double[][] a, double[][] b) {
if (a[0].length != b.length)
throw new IllegalStateException("invalid dimensions");
double[][] matrix = new double[a.length][b[0].length];
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < b[0].length; j++) {
double sum = 0;
for (int k = 0; k < a[i].length; k++)
sum += a[i][k] * b[k][j];
matrix[i][j] = sum;
}
}
return matrix;
}
private static double[][] rref(double[][] matrix) {
double[][] rref = new double[matrix.length][];
for (int i = 0; i < matrix.length; i++)
rref[i] = Arrays.copyOf(matrix[i], matrix[i].length);
int r = 0;
for (int c = 0; c < rref[0].length && r < rref.length; c++) {
int j = r;
for (int i = r + 1; i < rref.length; i++)
if (Math.abs(rref[i][c]) > Math.abs(rref[j][c]))
j = i;
if (Math.abs(rref[j][c]) < 0.00001)
continue;
double[] temp = rref[j];
rref[j] = rref[r];
rref[r] = temp;
double s = 1.0 / rref[r][c];
for (j = 0; j < rref[0].length; j++)
rref[r][j] *= s;
for (int i = 0; i < rref.length; i++) {
if (i != r) {
double t = rref[i][c];
for (j = 0; j < rref[0].length; j++)
rref[i][j] -= t * rref[r][j];
}
}
r++;
}
return rref;
}
private static double[][] transpose(double[][] matrix) {
double[][] transpose = new double[matrix[0].length][matrix.length];
for (int i = 0; i < matrix.length; i++)
for (int j = 0; j < matrix[i].length; j++)
transpose[j][i] = matrix[i][j];
return transpose;
}
public static void main(String[] args) {
// example 1 - solving a system of equations
double[][] a = { { 1, 1, 1 }, { 0, 2, 5 }, { 2, 5, -1 } };
double[][] b = { { 6 }, { -4 }, { 27 } };
double[][] matrix = multiply(inverse(a), b);
for (double[] i : matrix)
System.out.println(Arrays.toString(i));
System.out.println();
// example 2 - example 1 using reduced row echelon form
a = new double[][]{ { 1, 1, 1, 6 }, { 0, 2, 5, -4 }, { 2, 5, -1, 27 } };
matrix = rref(a);
for (double[] i : matrix)
System.out.println(Arrays.toString(i));
System.out.println();
// example 3 - solving a normal equation for linear regression
double[][] x = { { 2104, 5, 1, 45 }, { 1416, 3, 2, 40 },
{ 1534, 3, 2, 30 }, { 852, 2, 1, 36 } };
double[][] y = { { 460 }, { 232 }, { 315 }, { 178 } };
matrix = multiply(
multiply(inverse(multiply(transpose(x), x)), transpose(x)), y);
for (double[] i : matrix)
System.out.println(Arrays.toString(i));
}
}