私は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;
}
大規模な行列の行列式をより効率的に計算する方法を誰かが知っていますか?そうでない場合、他のアルゴリズムを使用して大きな行列の逆行列を計算する方法を誰かが知っていますか?
ありがとう
指数関数的に?いいえ、マトリクス反転はO(N ^ 3)だと思います。
LU分解 を使用して行列方程式を解くことをお勧めします。行列式を使用するときに、その行列式を解く必要はありません。
さらに良いことに、あなたを助けるためにパッケージを調べてください。 [〜#〜] jama [〜#〜] が思い浮かびます。
12x12や19x19は大きなマトリックスではありません。数十または数百の数千の自由度の問題を解決するのが一般的です。
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);
}
}
これらをあなたの状況に適応させてください。
これにはApache Commons Math 2.0の使用をお勧めします。 JAMAは死んだプロジェクトです。 ACM 2.0は実際にJAMAから線形代数を取り、それをさらに発展させました。
la4j (Linear Algebra 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
行列の反転は、計算上非常に負荷がかかります。 duffymoが答えたようにLUは優れたアルゴリズムであり、他のバリアント(たとえば、QR)があります。
残念ながら、重い計算を取り除くことはできません...最適化されたライブラリを使用していない場合、ボットテルネックはgetSubmatrixメソッドです。
また、特別なマトリックス構造(バンドのマトリックス性、対称性、対角性、スパース性)を計算で考慮すると、パフォーマンスに大きな影響があります。あなたのマイレージは異なる場合があります...
この方法で逆行列を計算する必要はありません。 OK、LUのような因数分解を使用することはほとんど常に良いので、逆の計算自体は避けるべきです。
再帰的計算を使用した行列式の計算は、数値的にわいせつなことです。 LU因数分解を使用して行列式を計算することをお勧めします。しかし、LU係数を計算するのが面倒な場合は、なぜ逆数を計算する必要があるのでしょうか?すでにLU係数を計算することによって困難な作業を行っています。
LU係数を取得したら、それらを使用して置換を前後に行うことができます。
19x19のマトリックスが大きい限り、それは私が思っているほど大きくはありません。
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);
}
}
行列式を計算するアルゴリズムは確かに指数関数的です。基本的な問題は、定義から計算することであり、まっすぐな定義は、計算するべき指数の量のサブ行列式を導きます。行列式またはその逆行列を計算する前に、まず行列を変換する必要があります。 (動的プログラミングについて説明しようと思いましたが、副問題の数も指数関数的であるため、動的プログラミングではこの問題を解決できません。)
他の人が推奨するLU分解は、良い選択です。行列計算を初めて使用する場合は、行列式と逆行列を計算するためにガウスの消去法を確認することもできます。これは、最初は少し理解しやすいためです。
そして、あなたが浮動小数点数を扱っているので、行列反転で覚えておかなければならないことが一つあります。すべての優れたアルゴリズムには、呼び出されたときに適切なピボットを選択するための行および/または列の順列が含まれています。少なくともガウスの消去法では、各ステップで列を並べ替えて、絶対値が最大の要素がピボットとして選択されるようにします。これが最も安定した選択であるためです。
正確な解決策が必要ですか?近似ソルバー( Gauss-Seidel は非常にパフォーマンスが高く、簡単に実装できます)がうまく機能し、非常に速く収束します。 19x19は非常に小さなマトリックスです。私が使用したGauss-Seidelコードは、瞬く間に128x128の行列を解くことができると思います(しかし、私にそれを引用しないでください、しばらくの間です)。
ゲームでMatlabを倒すのは難しいです。また、精度についても注意が必要です。ピボットとして2.0および2.00001を使用している場合-注意してください!あなたの答えは非常に不正確になるかもしれません。また、Pythonの実装も確認してください(numpy/scipyのどこかにあります...)
マトリックスの反転(高速ではない)を求める人は、 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));
}
}