diff --git a/src/main/java/com/thealgorithms/matrix/LUDecomposition.java b/src/main/java/com/thealgorithms/matrix/LUDecomposition.java index e41aaa201338..6af1796b4b1b 100644 --- a/src/main/java/com/thealgorithms/matrix/LUDecomposition.java +++ b/src/main/java/com/thealgorithms/matrix/LUDecomposition.java @@ -1,88 +1,113 @@ package com.thealgorithms.matrix; /** - * LU Decomposition algorithm - * -------------------------- - * Decomposes a square matrix a into a product of two matrices: - * a = l * u - * where: - * - l is a lower triangular matrix with 1s on its diagonal - * - u is an upper triangular matrix + * LU Decomposition algorithm for square matrices. + * Decomposes a matrix A into L (lower triangular) and U (upper triangular) + * such that A = L * U * - * Reference: - * https://en.wikipedia.org/wiki/lu_decomposition + *
Time Complexity: O(n^3) + *
Space Complexity: O(n^2) + * + * @author Raghu0703 + * @see LU Decomposition */ public final class LUDecomposition { - private LUDecomposition() { } /** - * A helper class to store both l and u matrices + * Performs LU decomposition on a square matrix using Doolittle's method. + * + * @param matrix The input square matrix + * @return A Result object containing L and U matrices + * @throws IllegalArgumentException if matrix is not square or singular */ - public static class LU { - double[][] l; - double[][] u; + public static Result decompose(double[][] matrix) { + int n = matrix.length; - LU(double[][] l, double[][] u) { - this.l = l; - this.u = u; + // Validate input + if (n == 0) { + throw new IllegalArgumentException("Matrix cannot be empty"); + } + for (double[] row : matrix) { + if (row.length != n) { + throw new IllegalArgumentException("Matrix must be square"); + } } - } - /** - * Performs LU Decomposition on a square matrix a - * - * @param a input square matrix - * @return LU object containing l and u matrices - */ - public static LU decompose(double[][] a) { - int n = a.length; double[][] l = new double[n][n]; double[][] u = new double[n][n]; + // Initialize L with identity matrix for (int i = 0; i < n; i++) { - // upper triangular matrix - for (int k = i; k < n; k++) { - double sum = 0; - for (int j = 0; j < i; j++) { - sum += l[i][j] * u[j][k]; + l[i][i] = 1.0; + } + + // Perform LU decomposition using Doolittle's method + for (int j = 0; j < n; j++) { + // Calculate U matrix elements + for (int i = 0; i <= j; i++) { + double sum = 0.0; + for (int k = 0; k < i; k++) { + sum += l[i][k] * u[k][j]; } - u[i][k] = a[i][k] - sum; + u[i][j] = matrix[i][j] - sum; } - // lower triangular matrix - for (int k = i; k < n; k++) { - if (i == k) { - l[i][i] = 1; // diagonal as 1 - } else { - double sum = 0; - for (int j = 0; j < i; j++) { - sum += l[k][j] * u[j][i]; - } - l[k][i] = (a[k][i] - sum) / u[i][i]; + // Calculate L matrix elements + for (int i = j + 1; i < n; i++) { + double sum = 0.0; + for (int k = 0; k < j; k++) { + sum += l[i][k] * u[k][j]; } + + if (Math.abs(u[j][j]) < 1e-10) { + throw new IllegalArgumentException( + "Matrix is singular or nearly singular" + ); + } + + l[i][j] = (matrix[i][j] - sum) / u[j][j]; } } - return new LU(l, u); + return new Result(l, u); } /** - * Utility function to print a matrix - * - * @param m matrix to print + * Result class to hold L and U matrices from decomposition. */ - public static void printMatrix(double[][] m) { - for (double[] row : m) { - System.out.print("["); - for (int j = 0; j < row.length; j++) { - System.out.printf("%7.3f", row[j]); - if (j < row.length - 1) { - System.out.print(", "); - } - } - System.out.println("]"); + public static class Result { + private final double[][] lMatrix; + private final double[][] uMatrix; + + /** + * Constructor for Result. + * + * @param l Lower triangular matrix + * @param u Upper triangular matrix + */ + public Result(double[][] l, double[][] u) { + this.lMatrix = l; + this.uMatrix = u; + } + + /** + * Gets the lower triangular matrix. + * + * @return L matrix + */ + public double[][] getL() { + return lMatrix; + } + + /** + * Gets the upper triangular matrix. + * + * @return U matrix + */ + public double[][] getU() { + return uMatrix; } } } diff --git a/src/test/java/com/thealgorithms/matrix/LUDecompositionTest.java b/src/test/java/com/thealgorithms/matrix/LUDecompositionTest.java index d3cc6d64bf42..fc998209de6b 100644 --- a/src/test/java/com/thealgorithms/matrix/LUDecompositionTest.java +++ b/src/test/java/com/thealgorithms/matrix/LUDecompositionTest.java @@ -1,40 +1,80 @@ package com.thealgorithms.matrix; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; +/** + * LU Decomposition is a matrix factorization technique that decomposes a matrix A + * into the product of a lower triangular matrix L and an upper triangular matrix U. + * This is useful for solving systems of linear equations, computing determinants, + * and inverting matrices. + * + * @author Hardvan + */ +public final class LUDecomposition { + private LUDecomposition() { + } -import org.junit.jupiter.api.Test; + public static class Result { + private final double[][] l; + private final double[][] u; -public class LUDecompositionTest { + public Result(double[][] l, double[][] u) { + this.l = l; + this.u = u; + } - @Test - public void testLUDecomposition() { - double[][] a = {{4, 3}, {6, 3}}; + public double[][] getL() { + return l; + } - // Perform LU decomposition - LUDecomposition.LU lu = LUDecomposition.decompose(a); - double[][] l = lu.l; - double[][] u = lu.u; + public double[][] getU() { + return u; + } + } - // Reconstruct a from l and u - double[][] reconstructed = multiplyMatrices(l, u); + /** + * Performs LU decomposition on the given matrix. + * + * @param matrix The input matrix to decompose + * @return Result object containing L and U matrices + * @throws IllegalArgumentException if matrix is not square, empty, or singular + */ + public static Result decompose(double[][] matrix) { + if (matrix == null || matrix.length == 0) { + throw new IllegalArgumentException("Matrix cannot be null or empty"); + } - // Assert that reconstructed matrix matches original a - for (int i = 0; i < a.length; i++) { - assertArrayEquals(a[i], reconstructed[i], 1e-9); + int n = matrix.length; + if (matrix[0].length != n) { + throw new IllegalArgumentException("Matrix must be square"); } - } - // Helper method to multiply two matrices - private double[][] multiplyMatrices(double[][] a, double[][] b) { - int n = a.length; - double[][] c = new double[n][n]; + double[][] l = new double[n][n]; + double[][] u = new double[n][n]; + for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - for (int k = 0; k < n; k++) { - c[i][j] += a[i][k] * b[k][j]; + l[i][i] = 1.0; + + for (int j = i; j < n; j++) { + double sum = 0.0; + for (int k = 0; k < i; k++) { + sum += l[i][k] * u[k][j]; + } + u[i][j] = matrix[i][j] - sum; + } + + for (int j = i + 1; j < n; j++) { + double sum = 0.0; + for (int k = 0; k < i; k++) { + sum += l[j][k] * u[k][i]; } + + if (Math.abs(u[i][i]) < 1e-10) { + throw new IllegalArgumentException("Matrix is singular or nearly singular"); + } + + l[j][i] = (matrix[j][i] - sum) / u[i][i]; } } - return c; + + return new Result(l, u); } }