web-dev-qa-db-fra.com

Calcul de matrice inverse Java

J'essaie de calculer la matrice inverse en Java.

Je suis la méthode adjointe (premier calcul de la matrice adjointe, puis transposition de cette matrice et enfin, multipliez-la pour l'inverse de la valeur du déterminant).

Cela fonctionne quand la matrice n'est pas trop grosse. J'ai vérifié que pour les matrices allant jusqu'à 12x12, le résultat est rapidement fourni. Cependant, lorsque la matrice est supérieure à 12x12, le temps nécessaire pour effectuer le calcul augmente de manière exponentielle.

La matrice que j'ai besoin d'inverser est 19x19, et cela prend trop de temps. La méthode qui consomme le plus de temps est la méthode utilisée pour le calcul du déterminant.

Le code que j'utilise est:

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;
}   

Est-ce que quelqu'un sait comment calculer plus efficacement le déterminant d'une grande matrice? Sinon, est-ce que quelqu'un sait comment calculer l'inverse d'une grande matrice en utilisant un autre algorithme?

Merci

12
dedalo

Exponentiellement? Non, je crois que l'inversion de la matrice est O (N ^ 3).

Je recommanderais d'utiliser LU decomposition pour résoudre une équation matricielle. Vous n'avez pas à résoudre pour le déterminant lorsque vous l'utilisez.

Mieux encore, cherchez un forfait pour vous aider. JAMA vient à l'esprit.

Les formats 12x12 ou 19x19 ne sont pas de grandes matrices. Il est courant de résoudre des problèmes avec des dizaines ou des centaines de milliers de degrés de liberté.

Voici un exemple pratique d'utilisation de JAMA. Vous devez avoir le bocal JAMA dans votre CLASSPATH lorsque vous compilez et exécutez:

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);
    }
}

Voici le même problème résolu avec Apache Commons Math, selon la recommandation de 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);
    }
}

Adaptez-les à votre situation.

16
duffymo

Je recommanderais d'utiliser Apache Commons Math 2.0 pour cela. JAMA est un projet mort. ACM 2.0 a en fait emprunté l’algèbre linéaire à JAMA et l’a développée davantage.

9
quant_dev

La bibliothèque la4j (algèbre linéaire pour Java) prend en charge l’inversion de matrice. Voici le bref exemple:

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
3
Vladimir Kostyukov

L'inversion de matrice est très intensive en calcul. Comme le répond le duffymo, LU est un bon algorithme, et il existe d'autres variantes (QR, par exemple).

Malheureusement, vous ne pouvez pas vous débarrasser des calculs lourds ... et peut-être que bottelneck est la méthode getSubmatrix si vous n'utilisez pas une bibliothèque optimisée.

De plus, les structures matricielles spéciales (matrices de bande, symétrie, diagonalité, faible densité) ont un impact important sur les performances si elles sont prises en compte dans les calculs. Votre kilométrage peut varier...

3
Pablo Rodriguez

Vous ne voulez JAMAIS calculer une matrice inverse de cette façon. Ok, le calcul de l'inverse lui-même doit être évité, car il est presque toujours préférable d'utiliser une factorisation telle qu'une LU.

Le calcul du déterminant à l'aide de calculs récursifs est une opération numériquement obscène. Il s'avère qu'un meilleur choix consiste à utiliser une factorisation LU pour calculer un déterminant. Mais si vous avez la peine de calculer les facteurs LU, pourquoi voudriez-vous éventuellement calculer l'inverse? Vous avez déjà effectué le travail difficile en calculant les facteurs LU.

Une fois que vous avez les facteurs LU, vous pouvez les utiliser pour effectuer des substitutions en aval et en aval.

Dans la mesure où une matrice 19x19 étant volumineuse, elle n’est même pas proche de ce que je penserais de grande.

3
user85109

Depuis que la bibliothèque ACM a été mise à jour au fil des ans, voici l'implémentation conforme à la dernière définition de l'inversion de matrice.

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);
    }
}
2
Soumya Kanti

Votre algorithme pour calculer un déterminant est en effet exponentiel. Le problème de base est que vous calculez à partir de la définition et que la définition directe conduit à une quantité exponentielle de sous-déterminants à calculer. Vous devez vraiment transformer la matrice avant de calculer son déterminant ou son inverse. (J'ai pensé expliquer la programmation dynamique, mais ce problème ne peut pas être résolu par une programmation dynamique car le nombre de sous-problèmes est également exponentiel.)

La décomposition de LU, comme recommandé par d’autres, est un bon choix. Si vous débutez dans le calcul matriciel, vous pouvez également vous intéresser à l’élimination gaussienne pour calculer les déterminants et les inverses, ce qui pourrait être un peu plus facile à comprendre au début.

Et une chose à retenir dans l'inversion de matrice est la stabilité numérique, car vous avez affaire à des nombres à virgule flottante. Tous les bons algorithmes incluent des permutations de lignes et/ou de colonnes pour choisir les pivots appropriés, comme on les appelle. Au moins dans l’élimination gaussienne, vous voulez permuter les colonnes à chaque étape de sorte que l’élément le plus grand en valeur absolue soit choisi comme pivot, car c’est le choix le plus stable.

2
JaakkoK

Devez-vous avoir une solution exacte? Un solveur approximatif ( Gauss-Seidel est très performant et facile à mettre en œuvre) fonctionnera probablement pour vous et convergera très rapidement. 19x19 est une matrice assez petite. Je pense que le code de Gauss-Seidel que j'ai utilisé pourrait résoudre une matrice de 128x128 en un clin d'œil (mais ne me citez pas dessus, cela fait longtemps).

1
davidtbernal

Il est difficile de battre Matlab à leur jeu. Ils sont également prudents quant à la précision. Si vous avez les pivots 2.0 et 2.00001 - faites attention! Votre réponse pourrait être très imprécise. Découvrez également l'implémentation de Python (il est quelque part dans numpy/scipy ...)

1
Hamish Grubijan

Pour ceux qui recherchent une inversion matricielle (pas rapide), voir 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));
    }

}
0
CoolMind