Skip to content

Commit 6ee8b17

Browse files
committed
feat: Add LU decomposition algorithm for matrices
- Implements LU decomposition using Doolittle's method - Decomposes square matrix A into L (lower) and U (upper) triangular matrices - Includes comprehensive test cases with edge case handling - Time complexity: O(n^3), Space complexity: O(n^2) - Resolves #6833
1 parent f693c44 commit 6ee8b17

File tree

2 files changed

+169
-68
lines changed

2 files changed

+169
-68
lines changed

src/main/java/com/thealgorithms/matrix/LUDecomposition.java

Lines changed: 77 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,64 +25,98 @@ public static class LU {
2525
double[][] u;
2626

2727
LU(double[][] l, double[][] u) {
28-
this.l = l;
29-
this.u = u;
30-
}
28+
package com.thealgorithms.matrix;
29+
30+
/**
31+
* LU Decomposition algorithm for square matrices
32+
* Decomposes a matrix A into L (lower triangular) and U (upper triangular)
33+
* such that A = L * U
34+
*
35+
* Time Complexity: O(n^3)
36+
* Space Complexity: O(n^2)
37+
*
38+
* @author Raghu0703
39+
* @see <a href="https://en.wikipedia.org/wiki/LU_decomposition">LU Decomposition</a>
40+
*/
41+
public final class LUDecomposition {
42+
private LUDecomposition() {
3143
}
3244

3345
/**
34-
* Performs LU Decomposition on a square matrix a
35-
*
36-
* @param a input square matrix
37-
* @return LU object containing l and u matrices
46+
* Performs LU decomposition on a square matrix using Doolittle's method
47+
*
48+
* @param matrix The input square matrix
49+
* @return A Result object containing L and U matrices
50+
* @throws IllegalArgumentException if matrix is not square or singular
3851
*/
39-
public static LU decompose(double[][] a) {
40-
int n = a.length;
52+
public static Result decompose(double[][] matrix) {
53+
int n = matrix.length;
54+
55+
// Validate input
56+
if (n == 0) {
57+
throw new IllegalArgumentException("Matrix cannot be empty");
58+
}
59+
for (double[] row : matrix) {
60+
if (row.length != n) {
61+
throw new IllegalArgumentException("Matrix must be square");
62+
}
63+
}
64+
4165
double[][] l = new double[n][n];
4266
double[][] u = new double[n][n];
43-
67+
68+
// Initialize L with identity matrix
4469
for (int i = 0; i < n; i++) {
45-
// upper triangular matrix
46-
for (int k = i; k < n; k++) {
47-
double sum = 0;
48-
for (int j = 0; j < i; j++) {
49-
sum += l[i][j] * u[j][k];
70+
l[i][i] = 1.0;
71+
}
72+
73+
// Perform LU decomposition using Doolittle's method
74+
for (int j = 0; j < n; j++) {
75+
// Calculate U matrix elements
76+
for (int i = 0; i <= j; i++) {
77+
double sum = 0.0;
78+
for (int k = 0; k < i; k++) {
79+
sum += l[i][k] * u[k][j];
5080
}
51-
u[i][k] = a[i][k] - sum;
81+
u[i][j] = matrix[i][j] - sum;
5282
}
53-
54-
// lower triangular matrix
55-
for (int k = i; k < n; k++) {
56-
if (i == k) {
57-
l[i][i] = 1; // diagonal as 1
58-
} else {
59-
double sum = 0;
60-
for (int j = 0; j < i; j++) {
61-
sum += l[k][j] * u[j][i];
62-
}
63-
l[k][i] = (a[k][i] - sum) / u[i][i];
83+
84+
// Calculate L matrix elements
85+
for (int i = j + 1; i < n; i++) {
86+
double sum = 0.0;
87+
for (int k = 0; k < j; k++) {
88+
sum += l[i][k] * u[k][j];
6489
}
90+
91+
if (Math.abs(u[j][j]) < 1e-10) {
92+
throw new IllegalArgumentException("Matrix is singular or nearly singular");
93+
}
94+
95+
l[i][j] = (matrix[i][j] - sum) / u[j][j];
6596
}
6697
}
67-
68-
return new LU(l, u);
98+
99+
return new Result(l, u);
69100
}
70-
101+
71102
/**
72-
* Utility function to print a matrix
73-
*
74-
* @param m matrix to print
103+
* Result class to hold L and U matrices from decomposition
75104
*/
76-
public static void printMatrix(double[][] m) {
77-
for (double[] row : m) {
78-
System.out.print("[");
79-
for (int j = 0; j < row.length; j++) {
80-
System.out.printf("%7.3f", row[j]);
81-
if (j < row.length - 1) {
82-
System.out.print(", ");
83-
}
84-
}
85-
System.out.println("]");
105+
public static class Result {
106+
private final double[][] lMatrix;
107+
private final double[][] uMatrix;
108+
109+
public Result(double[][] l, double[][] u) {
110+
this.lMatrix = l;
111+
this.uMatrix = u;
112+
}
113+
114+
public double[][] getL() {
115+
return lMatrix;
116+
}
117+
118+
public double[][] getU() {
119+
return uMatrix;
86120
}
87121
}
88122
}
Lines changed: 92 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,107 @@
11
package com.thealgorithms.matrix;
22

33
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
4-
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
55
import org.junit.jupiter.api.Test;
66

7-
public class LUDecompositionTest {
7+
class LUDecompositionTest {
8+
9+
private static final double EPSILON = 1e-6;
810

911
@Test
10-
public void testLUDecomposition() {
11-
double[][] a = {{4, 3}, {6, 3}};
12+
void testBasicLUDecomposition() {
13+
double[][] matrix = {
14+
{2, -1, -2},
15+
{-4, 6, 3},
16+
{-4, -2, 8}
17+
};
1218

13-
// Perform LU decomposition
14-
LUDecomposition.LU lu = LUDecomposition.decompose(a);
15-
double[][] l = lu.l;
16-
double[][] u = lu.u;
19+
LUDecomposition.Result result = LUDecomposition.decompose(matrix);
1720

18-
// Reconstruct a from l and u
19-
double[][] reconstructed = multiplyMatrices(l, u);
21+
double[][] expectedL = {
22+
{1.0, 0.0, 0.0},
23+
{-2.0, 1.0, 0.0},
24+
{-2.0, -1.0, 1.0}
25+
};
2026

21-
// Assert that reconstructed matrix matches original a
22-
for (int i = 0; i < a.length; i++) {
23-
assertArrayEquals(a[i], reconstructed[i], 1e-9);
24-
}
27+
double[][] expectedU = {
28+
{2.0, -1.0, -2.0},
29+
{0.0, 4.0, -1.0},
30+
{0.0, 0.0, 3.0}
31+
};
32+
33+
assertMatrixEquals(expectedL, result.getL());
34+
assertMatrixEquals(expectedU, result.getU());
35+
}
36+
37+
@Test
38+
void testIdentityMatrix() {
39+
double[][] identity = {
40+
{1, 0, 0},
41+
{0, 1, 0},
42+
{0, 0, 1}
43+
};
44+
45+
LUDecomposition.Result result = LUDecomposition.decompose(identity);
46+
47+
assertMatrixEquals(identity, result.getL());
48+
assertMatrixEquals(identity, result.getU());
49+
}
50+
51+
@Test
52+
void testTwoByTwoMatrix() {
53+
double[][] matrix = {
54+
{4, 3},
55+
{6, 3}
56+
};
57+
58+
LUDecomposition.Result result = LUDecomposition.decompose(matrix);
59+
60+
double[][] expectedL = {
61+
{1.0, 0.0},
62+
{1.5, 1.0}
63+
};
64+
65+
double[][] expectedU = {
66+
{4.0, 3.0},
67+
{0.0, -1.5}
68+
};
69+
70+
assertMatrixEquals(expectedL, result.getL());
71+
assertMatrixEquals(expectedU, result.getU());
72+
}
73+
74+
@Test
75+
void testNonSquareMatrix() {
76+
double[][] nonSquare = {
77+
{1, 2, 3},
78+
{4, 5, 6}
79+
};
80+
81+
assertThrows(IllegalArgumentException.class, () -> LUDecomposition.decompose(nonSquare));
82+
}
83+
84+
@Test
85+
void testEmptyMatrix() {
86+
double[][] empty = {};
87+
88+
assertThrows(IllegalArgumentException.class, () -> LUDecomposition.decompose(empty));
89+
}
90+
91+
@Test
92+
void testSingularMatrix() {
93+
double[][] singular = {
94+
{1, 2, 3},
95+
{2, 4, 6},
96+
{3, 6, 9}
97+
};
98+
99+
assertThrows(IllegalArgumentException.class, () -> LUDecomposition.decompose(singular));
25100
}
26101

27-
// Helper method to multiply two matrices
28-
private double[][] multiplyMatrices(double[][] a, double[][] b) {
29-
int n = a.length;
30-
double[][] c = new double[n][n];
31-
for (int i = 0; i < n; i++) {
32-
for (int j = 0; j < n; j++) {
33-
for (int k = 0; k < n; k++) {
34-
c[i][j] += a[i][k] * b[k][j];
35-
}
36-
}
102+
private void assertMatrixEquals(double[][] expected, double[][] actual) {
103+
for (int i = 0; i < expected.length; i++) {
104+
assertArrayEquals(expected[i], actual[i], EPSILON);
37105
}
38-
return c;
39106
}
40107
}

0 commit comments

Comments
 (0)