我正在尝试用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;
}
有人知道如何更有效地计算大矩阵的行列式吗?如果没有,有没有人知道如何使用其他算法来计算大矩阵的逆矩阵?
谢谢
答案 0 :(得分:16)
指数?不,我相信矩阵求逆是O(N ^ 3)。
我建议使用LU decomposition来求解矩阵方程。使用它时,您无需为行列式求解。
更好的是,查看一个包来帮助你。想到JAMA。
12x12或19x19不是大型matricies。解决具有数十或数百数千自由度的问题是很常见的。
这是一个如何使用JAMA的工作示例。编译和运行时,必须在CLASSPATH中使用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);
}
}
根据quant_dev的推荐,使用Apache Commons Math解决了同样的问题:
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);
}
}
根据您的情况调整这些。
答案 1 :(得分:9)
我建议使用Apache Commons Math 2.0。 JAMA是一个死的项目。 ACM 2.0实际上采用了JAMA的线性代数并进一步发展了它。
答案 2 :(得分:3)
矩阵求逆在计算上非常密集。正如duffymo回答LU是一个很好的算法,还有其他变种(例如QR)。
不幸的是你无法摆脱繁重的计算......如果你没有使用优化的库,那么bottelneck可能是getSubmatrix方法。
此外,如果在计算中考虑,特殊矩阵结构(带质,对称性,对角性,稀疏性)对性能有很大影响。你的里程可能会有所不同......
答案 3 :(得分:3)
你永远不想用这种方式计算逆矩阵。好的,应避免计算逆本身,因为使用像LU这样的分解几乎总是更好。
使用递归计算计算行列式是一个数字上淫秽的事情。事实证明,更好的选择是使用LU分解来计算行列式。但是,如果您打算计算LU因子,那么为什么您可能想要计算逆?您已经通过计算LU因子完成了困难的工作。
一旦有了LU因子,就可以使用它们进行前后替换。
至于19x19矩阵很大,它甚至都不像我想象的那么大。
答案 4 :(得分:3)
la4j(线性代数for 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
答案 5 :(得分:2)
计算行列式的算法确实是指数级的。基本问题是您从定义进行计算,直接定义导致计算的指数量的子确定因素。在计算其行列式或其逆矩阵之前,您确实需要先转换矩阵。 (我想解释有关动态编程的问题,但这个问题无法通过动态编程解决,因为子问题的数量也是指数级的。)
其他人推荐的LU分解是一个不错的选择。如果您不熟悉矩阵计算,您可能还需要查看高斯消元来计算行列式和倒数,因为这可能比较容易理解。
在矩阵求逆中要记住的一件事是数值稳定性,因为你正在处理浮点数。所有好的算法都包括行和/或列的排列以选择合适的枢轴,因为它们被称为。至少在高斯消除中,您希望在每一步都置换列,以便选择绝对值最大的元素作为枢轴,因为这是最稳定的选择。
答案 6 :(得分:2)
由于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);
}
}
答案 7 :(得分:1)
在比赛中击败Matlab很难。他们对精确度也很谨慎。如果你有2.0和2.00001作为支点 - 小心!你的答案最终可能会非常不精确。另外,看看Python的实现(它在某处numpy / scipy ......)
答案 8 :(得分:1)
你必须有一个确切的解决方案吗?近似求解器(Gauss-Seidel非常高效且易于实现)可能对您有用,并且会很快收敛。 19x19是一个非常小的矩阵。我认为我使用的Gauss-Seidel代码可以在眨眼间解决128x128矩阵(但不要引用我的话,已经有一段时间了。)
答案 9 :(得分:0)
对于那些寻求矩阵求逆(不快)的人,请参阅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));
}
}
答案 10 :(得分:0)
通过ACM,您可以在不使用rhs的情况下执行此操作:
RealMatrix A = new Array2DRowRealMatrix(ARR);
System.out.println(new LUDecomposition(A).getSolver().getInverse());