Java逆矩阵计算

时间:2010-01-02 19:55:05

标签: java matrix matrix-inverse determinants

我正在尝试用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;
}   

有人知道如何更有效地计算大矩阵的行列式吗?如果没有,有没有人知道如何使用其他算法来计算大矩阵的逆矩阵?

谢谢

11 个答案:

答案 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());