From ccb9e31d8d63e2e415898eed19497f6060b7f307 Mon Sep 17 00:00:00 2001 From: Nail Iskhakov Date: Wed, 30 Jan 2019 05:42:25 +0300 Subject: [PATCH 1/8] Structure and main classes added --- .idea/misc.xml | 2 +- .idea/project.iml | 9 ++++ in.txt | 4 ++ src/solver/AugmentedMatrix.java | 70 +++++++++++++++++++++++++ src/solver/LinearEquationSolver.java | 40 +++++++++++++++ src/solver/Main.java | 76 +++++++++++++++++++++++++++- src/solver/Row.java | 53 +++++++++++++++++++ 7 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 in.txt create mode 100644 src/solver/AugmentedMatrix.java create mode 100644 src/solver/LinearEquationSolver.java create mode 100644 src/solver/Row.java diff --git a/.idea/misc.xml b/.idea/misc.xml index a165cb3..cbb200f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/project.iml b/.idea/project.iml index 47baa8c..d34aa22 100644 --- a/.idea/project.iml +++ b/.idea/project.iml @@ -6,5 +6,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/in.txt b/in.txt new file mode 100644 index 0000000..b0bf574 --- /dev/null +++ b/in.txt @@ -0,0 +1,4 @@ +3 +1 1 2 9 +2 4 -3 1 +3 6 -5 0 \ No newline at end of file diff --git a/src/solver/AugmentedMatrix.java b/src/solver/AugmentedMatrix.java new file mode 100644 index 0000000..87cf1ad --- /dev/null +++ b/src/solver/AugmentedMatrix.java @@ -0,0 +1,70 @@ +package solver; + +import java.util.Arrays; +import java.util.InputMismatchException; +import java.util.Scanner; + +public class AugmentedMatrix { + private Row[] matrix; + + AugmentedMatrix(double[][] matrix) { + setMatrix(matrix); + } + + AugmentedMatrix() {} + + void readMatrix(Scanner scanner) throws InputMismatchException { + int n = scanner.nextInt(); + int m = n+1; + matrix = new Row[n]; + for (int i = 0; i < n; i++) { + matrix[i] = new Row(m); + for (int j = 0; j < m; j++) { + this.set(i, j, scanner.nextDouble()); + } + } + } + + void set(int i, int j, double value) { + matrix[i].set(j, value); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < matrix.length; i++) { + sb.append(Arrays.toString(matrix[i].getRow())); + sb.append("\n"); + } + return sb.toString(); + } + + + public double[][] getMatrix() { + double[][] copy = new double[matrix.length][]; + for(int i = 0; i < matrix.length; i++) { + copy[i] = matrix[i].getRow(); + } + return copy; + } + + public void setMatrix(double[][] matrix) { + this.matrix = new Row[matrix.length]; + for(int i = 0; i < matrix.length; i++) { + this.matrix[i] = new Row(matrix[i]); + } + } + + public Row getRow(int index) { + return this.matrix[index]; + } + + public int[] size() { + int[] size = new int[2]; + size[0] = matrix.length; + size[1] = matrix[0].size(); + return size; + } + + +} diff --git a/src/solver/LinearEquationSolver.java b/src/solver/LinearEquationSolver.java new file mode 100644 index 0000000..7f42326 --- /dev/null +++ b/src/solver/LinearEquationSolver.java @@ -0,0 +1,40 @@ +package solver; + +public class LinearEquationSolver { + AugmentedMatrix matrix; + + LinearEquationSolver(AugmentedMatrix matrix) { + this.matrix = new AugmentedMatrix(matrix.getMatrix()); + } + + public void transformToUpperTriangularForm() { + Row currentRow; + int n = matrix.size()[0]; + for(int i = 0; i < n; i++) { + currentRow = matrix.getRow(i).normalizeRow(i); + for(int j = i + 1; j < n; j++) { + matrix.getRow(j).normalizeRow(i).subtract(currentRow); + } + } + } + + public void transformToLowerTriangularForm() { + Row currentRow; + int n = matrix.size()[0]; + for(int i = n-1; i >= 0; i--) { + currentRow = matrix.getRow(i).normalizeRow(i); + for(int j = i-1; j >= 0; j--) { + matrix.getRow(j).normalizeRow(i).subtract(currentRow); + } + } + } + + public void tranformToNormalizedDiagonalForm() { + transformToUpperTriangularForm(); + transformToLowerTriangularForm(); + } + + public AugmentedMatrix getMatrix() { + return matrix; + } +} diff --git a/src/solver/Main.java b/src/solver/Main.java index 4404714..24da4c5 100644 --- a/src/solver/Main.java +++ b/src/solver/Main.java @@ -1,7 +1,81 @@ package solver; +import java.io.File; +import java.io.FileNotFoundException; +import java.util.InputMismatchException; +import java.util.Scanner; + public class Main { + static String showHelp() { + String helpMsg = "This program is intended to solve Linear Equations with any amount of variables. \n\n" + + "Options: " + + "\n\t * -in [pathToFile] reads file with matrix coefficients and constant column of numbers " + + "\n\t * -out [pathToFile] writes results to the output file " + + "\n\t * -h shows help information " + + "\n\nExample: \njava Solver -in in.txt -out out.txt"; + return helpMsg; + } + public static void main(String[] args) { - System.out.print("Hello world!"); + // Getting and handling console parameters + String inputFile=""; + String outputFile=""; + for(int i = 0; i < args.length; i++) { + try { + switch (args[i]) { + case "-in": + inputFile = args[i + 1]; + break; + case "-out": + outputFile = args[i + 1]; + break; + case "-h" : + System.out.println(showHelp()); + return; + + } + } catch (ArrayIndexOutOfBoundsException e) { + System.err.println("Invalid command line options, use '-h' option to see help"); + return; + } + } + + if(inputFile.isEmpty() || outputFile.isEmpty()) { + System.err.println("Invalid command line options, use '-h' option to see help"); + return; + } + + System.out.println("Input File: " + inputFile + "\nOutput File: " + outputFile); + + // Working with input file + File file = new File(inputFile); + AugmentedMatrix matrix = new AugmentedMatrix(); + try { + Scanner fileScanner = new Scanner(file); + matrix.readMatrix(fileScanner); + } catch(FileNotFoundException e) { + System.out.println("File: " + file.getAbsolutePath() + " doesn't exist"); + return; + } catch (InputMismatchException e) { + System.err.println("Invalid structure of input file: " + file.getAbsolutePath()); + return; + } + + System.out.println(matrix); + +// for(int i = 0; i < matrix.size()[0]; i++) { +// matrix.getRow(i).normalizeRow(0); +// } +// +// matrix.getRow(1).subtract(matrix.getRow(0)); +// matrix.getRow(2).subtract(matrix.getRow(0)); + + LinearEquationSolver solver = new LinearEquationSolver(matrix); + solver.transformToUpperTriangularForm(); + solver.transformToLowerTriangularForm(); + + System.out.println(); + System.out.println(solver.getMatrix()); + } } \ No newline at end of file diff --git a/src/solver/Row.java b/src/solver/Row.java new file mode 100644 index 0000000..537a55b --- /dev/null +++ b/src/solver/Row.java @@ -0,0 +1,53 @@ +package solver; + +import java.util.Arrays; + +public class Row { + private double[] row; + + Row(double[] row) { + setRow(row); + } + + Row(int m) { + row = new double[m]; + } + + Row normalizeRow(int index) { + return divide(row[index]); + } + + Row divide(double v) { + for(int i = 0; i < row.length; i++) { + row[i] = row[i] / v; + } + return this; + } + + Row subtract(Row row) { + for(int i = 0; i < this.row.length; i++) { + this.row[i] = this.row[i] - row.get(i); + } + return this; + } + + public double[] getRow() { + return Arrays.copyOf(row, row.length); + } + + public void setRow(double[] row) { + this.row = Arrays.copyOf(row, row.length); + } + + public void set(int j, double value) { + this.row[j] = value; + } + + public double get(int j) { + return this.row[j]; + } + + public int size() { + return this.row.length; + } +} From f49d2613bb9a50e7903e7fa401343d6631ea82cc Mon Sep 17 00:00:00 2001 From: Nail Iskhakov Date: Thu, 31 Jan 2019 05:32:14 +0300 Subject: [PATCH 2/8] Gauss-Jordan Elimination method implemented --- out.txt | 3 + src/solver/AugmentedMatrix.java | 4 ++ src/solver/LinearEquationSolver.java | 90 +++++++++++++++++++++++++--- src/solver/Main.java | 51 ++++++++++------ src/solver/Row.java | 11 +++- 5 files changed, 134 insertions(+), 25 deletions(-) create mode 100644 out.txt diff --git a/out.txt b/out.txt new file mode 100644 index 0000000..5f5fbe7 --- /dev/null +++ b/out.txt @@ -0,0 +1,3 @@ +1 +2 +3 \ No newline at end of file diff --git a/src/solver/AugmentedMatrix.java b/src/solver/AugmentedMatrix.java index 87cf1ad..5c8b6e7 100644 --- a/src/solver/AugmentedMatrix.java +++ b/src/solver/AugmentedMatrix.java @@ -29,6 +29,10 @@ void set(int i, int j, double value) { matrix[i].set(j, value); } + double get(int i, int j) { + return matrix[i].get(j); + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/src/solver/LinearEquationSolver.java b/src/solver/LinearEquationSolver.java index 7f42326..e1996b1 100644 --- a/src/solver/LinearEquationSolver.java +++ b/src/solver/LinearEquationSolver.java @@ -1,39 +1,115 @@ package solver; +import java.text.DecimalFormat; + public class LinearEquationSolver { AugmentedMatrix matrix; + StringBuilder logs; + double[] result; LinearEquationSolver(AugmentedMatrix matrix) { this.matrix = new AugmentedMatrix(matrix.getMatrix()); + this.logs = new StringBuilder(); } - public void transformToUpperTriangularForm() { + private void transformToUpperTriangularForm() { Row currentRow; int n = matrix.size()[0]; + double coefficient; for(int i = 0; i < n; i++) { - currentRow = matrix.getRow(i).normalizeRow(i); + currentRow = matrix.getRow(i); + normalizeRowsForTransforming(i, currentRow); for(int j = i + 1; j < n; j++) { - matrix.getRow(j).normalizeRow(i).subtract(currentRow); + subtractRowsForTransforming(i,j,currentRow); } } } - public void transformToLowerTriangularForm() { + private void transformToLowerTriangularForm() { Row currentRow; int n = matrix.size()[0]; + double coefficient; for(int i = n-1; i >= 0; i--) { - currentRow = matrix.getRow(i).normalizeRow(i); + currentRow = matrix.getRow(i); + normalizeRowsForTransforming(i, currentRow); for(int j = i-1; j >= 0; j--) { - matrix.getRow(j).normalizeRow(i).subtract(currentRow); + subtractRowsForTransforming(i,j,currentRow); } } } - public void tranformToNormalizedDiagonalForm() { + private void tranformToDiagonalForm() { transformToUpperTriangularForm(); transformToLowerTriangularForm(); } + private void logMessage(String msg) { + logs.append(msg); + logs.append("\n"); + } + + /** + * Devide every element of matrix[j] row by the element at position i and subtracts currentRow from matrix[j] + * (was originally created to move duplicating code from methods) + * @param i + * @param j + * @param currentRow + */ + private void subtractRowsForTransforming(int i, int j, Row currentRow) { + double coefficient = matrix.getRow(j).get(i); + if(coefficient != 1.0) { + matrix.getRow(j).normalizeRow(i); + logMessage(String.format("R%d / %s - R%d -> R%d", j, new DecimalFormat("#.###").format(coefficient), i, j)); + } else { + logMessage(String.format("R%d - R%d -> R%d", j, i, j)); + } + matrix.getRow(j).subtract(currentRow); + } + + /** + * Divide every element of the row by the element at position `i` and logs results + * (was originally created to move duplicating code from methods) + * @param i index of the element at the row + * @param currentRow + */ + private void normalizeRowsForTransforming(int i, Row currentRow) { + double coefficient = currentRow.get(i); + if(coefficient != 1.0) { + currentRow.normalizeRow(i); + logMessage(String.format("R%d / %s -> R%d", i, new DecimalFormat("#.###").format(coefficient), i)); + } + } + + String getLogs() { + return this.logs.toString(); + } + + double[] getResult() { + return result; + } + + String getResultString() { + StringBuilder sb = new StringBuilder(); + for(double coefficient: result) { + sb.append(new DecimalFormat("#.####").format(coefficient)); + sb.append("\n"); + } + sb.deleteCharAt(sb.length()-1); + return sb.toString(); + } + + LinearEquationSolver solve() { + tranformToDiagonalForm(); + // Get coefficients of the last column + int n = this.matrix.size()[0]; + int m = this.matrix.size()[1]; + result = new double[n]; + for(int i = 0; i < n; i++) { + result[i] = matrix.get(i, m-1); + } + return this; + } + public AugmentedMatrix getMatrix() { return matrix; } diff --git a/src/solver/Main.java b/src/solver/Main.java index 24da4c5..f8c115c 100644 --- a/src/solver/Main.java +++ b/src/solver/Main.java @@ -2,20 +2,33 @@ import java.io.File; import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; import java.util.InputMismatchException; import java.util.Scanner; public class Main { - static String showHelp() { - String helpMsg = "This program is intended to solve Linear Equations with any amount of variables. \n\n" + + + static String getHelp() { + String helpMsg = "This program is intended to solve Linear Equations with any amount of variables using Gauss-Jordan Elimination. \n\n" + "Options: " + - "\n\t * -in [pathToFile] reads file with matrix coefficients and constant column of numbers " + + "\n\t * -in [pathToFile] reads file with the first number - number of equations and variables, and augmented matrix of linear equation" + "\n\t * -out [pathToFile] writes results to the output file " + "\n\t * -h shows help information " + - "\n\nExample: \njava Solver -in in.txt -out out.txt"; + "\n\nExample: \njava Solver -in in.txt -out out.txt" + + "\n\nExample of in.txt:\n" + + "3\n" + + "1 1 2 9\n" + + "2 4 -3 1\n" + + "3 6 -5 0"; return helpMsg; } + /** + * Program should start with necessary options -in [pathToFileWithMatrix] and -out [pathToOutputFile] + * Example: > java Solver -in in.txt -out out.txt + * @param args + */ public static void main(String[] args) { // Getting and handling console parameters String inputFile=""; @@ -30,7 +43,7 @@ public static void main(String[] args) { outputFile = args[i + 1]; break; case "-h" : - System.out.println(showHelp()); + System.out.println(getHelp()); return; } @@ -50,8 +63,7 @@ public static void main(String[] args) { // Working with input file File file = new File(inputFile); AugmentedMatrix matrix = new AugmentedMatrix(); - try { - Scanner fileScanner = new Scanner(file); + try(Scanner fileScanner = new Scanner(file);) { matrix.readMatrix(fileScanner); } catch(FileNotFoundException e) { System.out.println("File: " + file.getAbsolutePath() + " doesn't exist"); @@ -61,21 +73,26 @@ public static void main(String[] args) { return; } + // Input matrix output + System.out.println("Input matrix:"); System.out.println(matrix); -// for(int i = 0; i < matrix.size()[0]; i++) { -// matrix.getRow(i).normalizeRow(0); -// } -// -// matrix.getRow(1).subtract(matrix.getRow(0)); -// matrix.getRow(2).subtract(matrix.getRow(0)); + // Solving equation + System.out.println("Start solving linear equation."); LinearEquationSolver solver = new LinearEquationSolver(matrix); - solver.transformToUpperTriangularForm(); - solver.transformToLowerTriangularForm(); + String res = solver.solve().getResultString(); + System.out.println("Rows manipulation:"); + System.out.println(solver.getLogs()); - System.out.println(); - System.out.println(solver.getMatrix()); + System.out.println("The solution is: (" + res.replaceAll("\n", ", ") + ")"); + // Output writing + try(FileWriter fileWriter = new FileWriter(outputFile)) { + fileWriter.write(res); + System.out.println("Saved to " + outputFile); + } catch(IOException e) { + System.err.println("Result cannot be written to the output file, please try other output file"); + } } } \ No newline at end of file diff --git a/src/solver/Row.java b/src/solver/Row.java index 537a55b..e2d59a2 100644 --- a/src/solver/Row.java +++ b/src/solver/Row.java @@ -14,10 +14,19 @@ public class Row { } Row normalizeRow(int index) { - return divide(row[index]); + try { + return divide(row[index]); + } catch (ArithmeticException e) { + // Ignore + // Row cannot be normalized to the zero element + return this; + } } Row divide(double v) { + if(v == 0){ + throw new java.lang.ArithmeticException("Division by zero"); + } for(int i = 0; i < row.length; i++) { row[i] = row[i] / v; } From c6dac313b4932f7f540bc9bf920435d0c2d8357f Mon Sep 17 00:00:00 2001 From: Nail Iskhakov Date: Thu, 31 Jan 2019 05:48:48 +0300 Subject: [PATCH 3/8] Minor changes --- src/solver/AugmentedMatrix.java | 11 ++++++++--- src/solver/LinearEquationSolver.java | 19 +++++++++---------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/solver/AugmentedMatrix.java b/src/solver/AugmentedMatrix.java index 5c8b6e7..7254eb5 100644 --- a/src/solver/AugmentedMatrix.java +++ b/src/solver/AugmentedMatrix.java @@ -13,7 +13,7 @@ public class AugmentedMatrix { AugmentedMatrix() {} - void readMatrix(Scanner scanner) throws InputMismatchException { + public void readMatrix(Scanner scanner) throws InputMismatchException { int n = scanner.nextInt(); int m = n+1; matrix = new Row[n]; @@ -25,11 +25,11 @@ void readMatrix(Scanner scanner) throws InputMismatchException { } } - void set(int i, int j, double value) { + public void set(int i, int j, double value) { matrix[i].set(j, value); } - double get(int i, int j) { + public double get(int i, int j) { return matrix[i].get(j); } @@ -63,9 +63,14 @@ public Row getRow(int index) { return this.matrix[index]; } + /** + * Get dimensions of Augmented matrix + * @return size()[0] - number of rows, size()[1] - number of columns + */ public int[] size() { int[] size = new int[2]; size[0] = matrix.length; + // Consider that all rows have the same size, according to the class initialization size[1] = matrix[0].size(); return size; } diff --git a/src/solver/LinearEquationSolver.java b/src/solver/LinearEquationSolver.java index e1996b1..7efca22 100644 --- a/src/solver/LinearEquationSolver.java +++ b/src/solver/LinearEquationSolver.java @@ -3,9 +3,9 @@ import java.text.DecimalFormat; public class LinearEquationSolver { - AugmentedMatrix matrix; - StringBuilder logs; - double[] result; + private AugmentedMatrix matrix; + private StringBuilder logs; + private double[] result; LinearEquationSolver(AugmentedMatrix matrix) { this.matrix = new AugmentedMatrix(matrix.getMatrix()); @@ -15,7 +15,6 @@ public class LinearEquationSolver { private void transformToUpperTriangularForm() { Row currentRow; int n = matrix.size()[0]; - double coefficient; for(int i = 0; i < n; i++) { currentRow = matrix.getRow(i); normalizeRowsForTransforming(i, currentRow); @@ -28,7 +27,6 @@ private void transformToUpperTriangularForm() { private void transformToLowerTriangularForm() { Row currentRow; int n = matrix.size()[0]; - double coefficient; for(int i = n-1; i >= 0; i--) { currentRow = matrix.getRow(i); normalizeRowsForTransforming(i, currentRow); @@ -49,7 +47,8 @@ private void logMessage(String msg) { } /** - * Devide every element of matrix[j] row by the element at position i and subtracts currentRow from matrix[j] + * Devide every element of matrix[j] row by the element at position i, subtracts currentRow from matrix[j] + * and logs results * (was originally created to move duplicating code from methods) * @param i * @param j @@ -80,15 +79,15 @@ private void normalizeRowsForTransforming(int i, Row currentRow) { } } - String getLogs() { + public String getLogs() { return this.logs.toString(); } - double[] getResult() { + public double[] getResult() { return result; } - String getResultString() { + public String getResultString() { StringBuilder sb = new StringBuilder(); for(double coefficient: result) { sb.append(new DecimalFormat("#.####").format(coefficient)); @@ -98,7 +97,7 @@ String getResultString() { return sb.toString(); } - LinearEquationSolver solve() { + public LinearEquationSolver solve() { tranformToDiagonalForm(); // Get coefficients of the last column int n = this.matrix.size()[0]; From 7cafb915ab93c7f448b0a568d26b8e0cc4a0b07a Mon Sep 17 00:00:00 2001 From: Nail Iskhakov Date: Sun, 10 Feb 2019 21:56:43 +0300 Subject: [PATCH 4/8] Special cases implemented --- in.txt | 9 +- out.txt | 4 +- src/solver/AugmentedMatrix.java | 85 +++++++- src/solver/LinearEquationSolver.java | 304 +++++++++++++++++++++------ src/solver/Main.java | 10 +- src/solver/Row.java | 62 +++++- src/solver/SwapInfo.java | 19 ++ 7 files changed, 409 insertions(+), 84 deletions(-) create mode 100644 src/solver/SwapInfo.java diff --git a/in.txt b/in.txt index b0bf574..090b2e8 100644 --- a/in.txt +++ b/in.txt @@ -1,4 +1,5 @@ -3 -1 1 2 9 -2 4 -3 1 -3 6 -5 0 \ No newline at end of file +4 4 +0 0 2 0 +0 1 5 1 +0 0 6 0 +0 0 12 0 \ No newline at end of file diff --git a/out.txt b/out.txt index 5f5fbe7..af5f51f 100644 --- a/out.txt +++ b/out.txt @@ -1,3 +1 @@ -1 -2 -3 \ No newline at end of file +Infinite solutions \ No newline at end of file diff --git a/src/solver/AugmentedMatrix.java b/src/solver/AugmentedMatrix.java index 7254eb5..c5477d9 100644 --- a/src/solver/AugmentedMatrix.java +++ b/src/solver/AugmentedMatrix.java @@ -1,11 +1,19 @@ package solver; +import java.lang.reflect.Array; +import java.text.DecimalFormat; +import java.util.ArrayList; import java.util.Arrays; import java.util.InputMismatchException; import java.util.Scanner; public class AugmentedMatrix { private Row[] matrix; + private String decimalPattern = "#.####"; + + // We don't always need swapHistory, so method which uses this field must check whether it is initialized or not. + private ArrayList swapHistory; + AugmentedMatrix(double[][] matrix) { setMatrix(matrix); @@ -15,7 +23,7 @@ public class AugmentedMatrix { public void readMatrix(Scanner scanner) throws InputMismatchException { int n = scanner.nextInt(); - int m = n+1; + int m = scanner.nextInt(); matrix = new Row[n]; for (int i = 0; i < n; i++) { matrix[i] = new Row(m); @@ -33,18 +41,72 @@ public double get(int i, int j) { return matrix[i].get(j); } - @Override - public String toString() { + + public String fancyPrint(String decimalPattern) { + if(decimalPattern == null) { + decimalPattern = this.decimalPattern; + } StringBuilder sb = new StringBuilder(); - for(int i = 0; i < matrix.length; i++) { - sb.append(Arrays.toString(matrix[i].getRow())); + double[] row; + for (int i = 0; i < matrix.length; i++) { + row = matrix[i].getRow(); + sb.append("["); + for(int j =0; j < row.length; j++) { + sb.append(new DecimalFormat(decimalPattern).format(row[j])); + sb.append(", "); + } + sb.delete(sb.length()-2, sb.length()); + sb.append("]"); sb.append("\n"); } return sb.toString(); } + @Override + public String toString() { + return fancyPrint(null); + } + + public void swapRows(int firstRowIndex, int secondRowIndex) { + Row temp = matrix[firstRowIndex]; + matrix[firstRowIndex] = matrix[secondRowIndex]; + matrix[secondRowIndex] = temp; + } + + private void addSwapHistoryEntry(int prevIndex, int nextIndex) { + // Initialization check + if(swapHistory==null) { + swapHistory = new ArrayList<>(); + } + swapHistory.add(new SwapInfo(prevIndex, nextIndex)); + } - public double[][] getMatrix() { + public void clearSwapHistory() { + swapHistory.clear(); + } + + public void swapColumns(int firstColumnIndex, int secondColumnIndex) { + swapColumns(firstColumnIndex, secondColumnIndex, true); + } + + public void swapColumns(int firstColumnIndex, int secondColumnIndex, boolean writeLog) { + double coeff1, coeff2; + for(Row row : matrix) { + coeff1 = row.get(firstColumnIndex); + coeff2 = row.get(secondColumnIndex); + row.set(firstColumnIndex, coeff2); + row.set(secondColumnIndex, coeff1); + } + if(writeLog) { + addSwapHistoryEntry(firstColumnIndex, secondColumnIndex); + } + } + + public ArrayList getSwapHistory() { + return swapHistory; + } + + public double[][] getMatrixCopy() { double[][] copy = new double[matrix.length][]; for(int i = 0; i < matrix.length; i++) { copy[i] = matrix[i].getRow(); @@ -63,6 +125,17 @@ public Row getRow(int index) { return this.matrix[index]; } + public Row getColumnCopy(int index) { + int rows = size()[0]; // Number of rows + Row col = new Row(rows); + for(int i = 0; i < rows; i++) { + col.set(i, matrix[i].get(index)); + } + return col; + } + + + /** * Get dimensions of Augmented matrix * @return size()[0] - number of rows, size()[1] - number of columns diff --git a/src/solver/LinearEquationSolver.java b/src/solver/LinearEquationSolver.java index 7efca22..d2f5eda 100644 --- a/src/solver/LinearEquationSolver.java +++ b/src/solver/LinearEquationSolver.java @@ -1,110 +1,292 @@ package solver; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; + +enum SystemType { + NO_SOLUTIONS, + SINGLE_SOLUTION, + INFINITE_NUMBER_OF_SOLUTIONS, + UNKNOWN_ERROR +} public class LinearEquationSolver { private AugmentedMatrix matrix; private StringBuilder logs; - private double[] result; + private String result; + private SystemType resultType; + private String decimalPattern = "#.#####"; LinearEquationSolver(AugmentedMatrix matrix) { - this.matrix = new AugmentedMatrix(matrix.getMatrix()); + this.matrix = new AugmentedMatrix(matrix.getMatrixCopy()); this.logs = new StringBuilder(); } + /** + * It is step 1 of solving System of Linear Equations: transforming matrix to upper triangular form + */ private void transformToUpperTriangularForm() { - Row currentRow; + Row currentRow; double currentCoefficient; + Row nextRow; double nextCoefficient; + double multiplicator; + int n = matrix.size()[0]; // number of rows + int m = matrix.size()[1]; // number of cols + int rowNum, colNum; + // If there are no special cases, then rowNum = colNum (transforming to diagonal form) + for(rowNum = 0, colNum = 0; rowNum < n && colNum < m-1; rowNum++, colNum++) { + currentRow = matrix.getRow(rowNum); + currentCoefficient = currentRow.get(colNum); + + + // Search for the row with non-zero coefficent + int columnOffset = 0; boolean foundInColumn = false; + double coeff; int innerColNum = colNum; + while(currentCoefficient == 0 && innerColNum < m-1) { + + // If a current coefficient is zero, then we should find row with non-zero coefficient below + if (currentCoefficient == 0) { + foundInColumn = false; + int nextRowNum; + for (nextRowNum = rowNum; nextRowNum < n; nextRowNum++) { + coeff = matrix.getRow(nextRowNum).get(innerColNum); + if (coeff != 0) { + // Found row with non-zero i-th coefficient + foundInColumn = true; + break; + } + } + + if(foundInColumn) { + // We should swap rows + // These variables can be equal if previous column was filled with zeros, + // but on this column this row have non-zero coefficient + if(rowNum != nextRowNum) { + this.matrix.swapRows(rowNum, nextRowNum); + logMessage(String.format("R%d <-> R%d", rowNum, nextRowNum)); + } + // If innerColNum != colNum -> we found non-zero coefficient in other column, we should swap columns + if(innerColNum != colNum) { + matrix.swapColumns(colNum, innerColNum); + logMessage(String.format("C%d <-> C%d", colNum, innerColNum)); + } + // Update currentRow and currentCoefficient variables after all swaps + currentRow = matrix.getRow(rowNum); // rowNum after swap references to nextRowNum + currentCoefficient = currentRow.get(colNum); + break; + } else { + innerColNum++; + } + + } + + } + + // If after all (full search of non-zero coefficient in whole matrix) we have zero-coefficient + // We should end up with matrix processing - matrix already transformed + if(currentCoefficient == 0) { + return; + } + + // Make coefficient equal to one by dividing entire row by this coefficient + if(currentCoefficient != 1.0) { + currentRow.divide(currentCoefficient); + logMessage(String.format("R%d / %s -> R%d", rowNum, new DecimalFormat(decimalPattern).format(currentCoefficient), rowNum)); + } + + // Perform actions to zero coefficients in other rows + for(int j = rowNum + 1; j < n; j++) { + nextRow = matrix.getRow(j); + nextCoefficient = nextRow.get(colNum); + currentCoefficient = currentRow.get(colNum); // it must always be equal to one + // nextCoefficient should be zero + if(nextCoefficient != 0) { + multiplicator = nextCoefficient / currentCoefficient * (-1); + nextRow.add(currentRow.multiply(multiplicator, false)); + logMessage(String.format("%s * R%d + R%d -> R%d", new DecimalFormat(decimalPattern).format(multiplicator), rowNum, j, j)); + } + } + } + } + + /** + * It is step 2 of solving System of Linear Equations: reduction of non-diagonal members of upper triangular form of matrix + */ + private void reduceNonDiagonalCoefficients() { + // Start from the end + int rowNum, colNum, nextRowNum; int n = matrix.size()[0]; - for(int i = 0; i < n; i++) { - currentRow = matrix.getRow(i); - normalizeRowsForTransforming(i, currentRow); - for(int j = i + 1; j < n; j++) { - subtractRowsForTransforming(i,j,currentRow); + int coefficientIndex = -1; + double currentCoefficient, nextCoefficient, multiplicator; + Row row; Row nextRow; + for(rowNum = n-1; rowNum >= 0; rowNum--) { + + // Find first non-zero element + row = matrix.getRow(rowNum); + for(int i = 0; i < row.size()-1; i++) { + if(row.get(i) != 0) { + coefficientIndex = i; + break; + } } + + + // If entire row filled with zeros - stop processing, go to the next iteration + if(coefficientIndex == -1) { + continue; + } + + // Start subtracting this row from rows above + for(nextRowNum = rowNum - 1; nextRowNum >= 0; nextRowNum--) { + currentCoefficient = row.get(coefficientIndex); + nextRow = matrix.getRow(nextRowNum); + nextCoefficient = nextRow.get(coefficientIndex); + if (nextCoefficient != 0) { + multiplicator = nextCoefficient / currentCoefficient * (-1); + nextRow.add(row.multiply(multiplicator, false)); + logMessage(String.format("%s * R%d + R%d -> R%d", new DecimalFormat(decimalPattern).format(multiplicator), rowNum, nextRowNum, nextRowNum)); + } + } + } } - private void transformToLowerTriangularForm() { + + /** + * It is step 3 of solving System of Linear Equations: checking for number of solutions + * + * In this method it is essential that matrix has a pseudo-diagonal form (after 1st and 2nd steps were completed) + * + * @return SystemType enum + */ + private SystemType checkResult() { + // Checking for no solution + int rowNum, index; + int n = matrix.size()[0]; // number of rows + int m = matrix.size()[1]; // number of columns Row currentRow; - int n = matrix.size()[0]; - for(int i = n-1; i >= 0; i--) { - currentRow = matrix.getRow(i); - normalizeRowsForTransforming(i, currentRow); - for(int j = i-1; j >= 0; j--) { - subtractRowsForTransforming(i,j,currentRow); + for(rowNum = n-1; rowNum >=0; rowNum--) { + currentRow = matrix.getRow(rowNum); + if(currentRow.isZeroFilled()) { + continue; // Zero rows are insignificant for us + } + + // Getting first non-zero element + index = currentRow.getIndexOfFirstNonZeroElement(); + + // If first non-zero element located in a last column - the system is inconsistent + if(index == m-1) { + return SystemType.NO_SOLUTIONS; } + + // Check for infinite number of solutions + + // We look at last non-trivial row and get the matrix[i][j] first non-zero element, if j - is last column of + // non-augmented matrix - then we have single solution, if j - is not the last column of the non-augmented + // matrix - there is infinite number of solutions + // Example: + // 1 0 0 | 4 1 0 0 | 4 + // 0 1 0 | 5 0 1 0 | 5 + // 0 0 0 | 0 0 0 1 | 0 + // 0 0 0 | 0 0 0 0 | 0 + // INFINITE SOLUTIONS SINGLE SOLUTION + int cols = m -1; // number of columns of non-augmented matrix + if(index+1 != cols) { + return SystemType.INFINITE_NUMBER_OF_SOLUTIONS; + } + + // All other ways were considered -> only one variant + // We have single solution + return SystemType.SINGLE_SOLUTION; } + + return SystemType.UNKNOWN_ERROR; } - private void tranformToDiagonalForm() { + /** + * It is step 4 of solving System of Linear Equations: get original columns' order in matrix by looking at swap history + * + * Look at swap history and do reverse actions in desc order to get original column positions. + */ + public void revokeColumnsSwap() { + int[] reversedSwapIndexes; + ArrayList swapHistory = matrix.getSwapHistory(); + try { + for (int i = swapHistory.size() - 1; i >= 0; i--) { + reversedSwapIndexes = swapHistory.get(i).getReversedSwapInfo(); + matrix.swapColumns(reversedSwapIndexes[0], reversedSwapIndexes[1], false); + logMessage(String.format("Reverse: C%d <-> C%d", reversedSwapIndexes[0], reversedSwapIndexes[1])); + } + matrix.clearSwapHistory(); + } catch(NullPointerException e) { + // Ignore: + // It means that we haven't swapped any columns and swapHistory is not initialized yet + // (we do not initialize swapHistory to preserve memory, i know that in general this is possibly stupid =) ) + return; + } + } + + private void transformToDiagonalForm() { transformToUpperTriangularForm(); - transformToLowerTriangularForm(); + reduceNonDiagonalCoefficients(); + resultType = checkResult(); + revokeColumnsSwap(); } private void logMessage(String msg) { + // System.out.println(msg); logs.append(msg); logs.append("\n"); } /** - * Devide every element of matrix[j] row by the element at position i, subtracts currentRow from matrix[j] - * and logs results - * (was originally created to move duplicating code from methods) - * @param i - * @param j - * @param currentRow + * Get coefficients of single solution + * + * @return solution - array of coefficients */ - private void subtractRowsForTransforming(int i, int j, Row currentRow) { - double coefficient = matrix.getRow(j).get(i); - if(coefficient != 1.0) { - matrix.getRow(j).normalizeRow(i); - logMessage(String.format("R%d / %s - R%d -> R%d", j, new DecimalFormat("#.###").format(coefficient), i, j)); - } else { - logMessage(String.format("R%d - R%d -> R%d", j, i, j)); - } - matrix.getRow(j).subtract(currentRow); - } + private double[] getSingleSolution() { + int n = matrix.size()[0]; // Number of rows + int m = matrix.size()[1]; // Number of columns of augmented matrix + double[] solution = new double[m-1]; + int solutionIndex; + for(int rowNum = 0; rowNum < n; rowNum++) { + if(matrix.getRow(rowNum).isZeroFilled()) { + continue; + } - /** - * Divide every element of the row by the element at position `i` and logs results - * (was originally created to move duplicating code from methods) - * @param i index of the element at the row - * @param currentRow - */ - private void normalizeRowsForTransforming(int i, Row currentRow) { - double coefficient = currentRow.get(i); - if(coefficient != 1.0) { - currentRow.normalizeRow(i); - logMessage(String.format("R%d / %s -> R%d", i, new DecimalFormat("#.###").format(coefficient), i)); + solutionIndex = matrix.getRow(rowNum).getIndexOfFirstNonZeroElement(); + solution[solutionIndex] = matrix.getRow(rowNum).get(m-1); } + return solution; } public String getLogs() { return this.logs.toString(); } - public double[] getResult() { + public String getResult() { return result; } - public String getResultString() { - StringBuilder sb = new StringBuilder(); - for(double coefficient: result) { - sb.append(new DecimalFormat("#.####").format(coefficient)); - sb.append("\n"); - } - sb.deleteCharAt(sb.length()-1); - return sb.toString(); - } public LinearEquationSolver solve() { - tranformToDiagonalForm(); - // Get coefficients of the last column - int n = this.matrix.size()[0]; - int m = this.matrix.size()[1]; - result = new double[n]; - for(int i = 0; i < n; i++) { - result[i] = matrix.get(i, m-1); + transformToDiagonalForm(); + switch(resultType) { + case NO_SOLUTIONS: + result = "No solutions"; + return this; + case INFINITE_NUMBER_OF_SOLUTIONS: + result = "Infinite solutions"; + return this; + case SINGLE_SOLUTION: + double[] res = getSingleSolution(); + StringBuilder sb = new StringBuilder(); + for(double coefficient: res) { + sb.append(new DecimalFormat(decimalPattern).format(coefficient)); + sb.append("\n"); + } + sb.deleteCharAt(sb.length()-1); + result = sb.toString(); } return this; } diff --git a/src/solver/Main.java b/src/solver/Main.java index f8c115c..74b9ca1 100644 --- a/src/solver/Main.java +++ b/src/solver/Main.java @@ -73,6 +73,11 @@ public static void main(String[] args) { return; } + if(matrix.size()[0] == 0 || matrix.size()[1] == 0) { + System.err.println("Invalid structure of input file: " + file.getAbsolutePath()); + return; + } + // Input matrix output System.out.println("Input matrix:"); System.out.println(matrix); @@ -81,10 +86,13 @@ public static void main(String[] args) { // Solving equation System.out.println("Start solving linear equation."); LinearEquationSolver solver = new LinearEquationSolver(matrix); - String res = solver.solve().getResultString(); + String res = solver.solve().getResult(); System.out.println("Rows manipulation:"); System.out.println(solver.getLogs()); + System.out.println("Resulting matrix:"); + System.out.println(solver.getMatrix()); + System.out.println("The solution is: (" + res.replaceAll("\n", ", ") + ")"); // Output writing diff --git a/src/solver/Row.java b/src/solver/Row.java index e2d59a2..6143ea2 100644 --- a/src/solver/Row.java +++ b/src/solver/Row.java @@ -13,15 +13,6 @@ public class Row { row = new double[m]; } - Row normalizeRow(int index) { - try { - return divide(row[index]); - } catch (ArithmeticException e) { - // Ignore - // Row cannot be normalized to the zero element - return this; - } - } Row divide(double v) { if(v == 0){ @@ -33,6 +24,23 @@ Row divide(double v) { return this; } + Row multiply(double v, boolean inplace) { + Row curRow; + if(inplace) { + curRow = this; + } else { + curRow = new Row(row.length); + } + for(int i = 0; i < row.length; i++) { + curRow.set(i, row[i] * v); + } + return curRow; + } + + Row multiply(double v) { + return multiply(v, true); + } + Row subtract(Row row) { for(int i = 0; i < this.row.length; i++) { this.row[i] = this.row[i] - row.get(i); @@ -40,6 +48,23 @@ Row subtract(Row row) { return this; } + Row add(Row row) { + return add(row, true); + } + + Row add(Row nextRow, boolean inplace) { + Row curRow; + if(inplace) { + curRow = this; + } else { + curRow = new Row(row.length); + } + for(int i = 0; i < row.length; i++) { + curRow.set(i, curRow.get(i) + nextRow.get(i)); + } + return curRow; + } + public double[] getRow() { return Arrays.copyOf(row, row.length); } @@ -56,6 +81,25 @@ public double get(int j) { return this.row[j]; } + public boolean isZeroFilled() { + for(int i = 0; i < row.length; i++) { + if( row[i] != 0) { + return false; + } + } + return true; + } + + public int getIndexOfFirstNonZeroElement() { + for(int i = 0; i < row.length; i++) { + if(row[i] != 0 ) { + return i; + } + } + + return -1; + } + public int size() { return this.row.length; } diff --git a/src/solver/SwapInfo.java b/src/solver/SwapInfo.java new file mode 100644 index 0000000..7c65dc2 --- /dev/null +++ b/src/solver/SwapInfo.java @@ -0,0 +1,19 @@ +package solver; + +public class SwapInfo { + private int prevIndex; + private int nextIndex; + + SwapInfo(int prevIndex, int nextIndex) { + this.prevIndex = prevIndex; + this.nextIndex = nextIndex; + } + + public int[] getSwapInfo() { + return new int[]{prevIndex, nextIndex}; + } + + public int[] getReversedSwapInfo() { + return new int[]{nextIndex, prevIndex}; + } +} From 2b4e96b774da575c0fe62f6a7051890966aa2be1 Mon Sep 17 00:00:00 2001 From: Nail Iskhakov Date: Mon, 18 Feb 2019 23:44:53 +0300 Subject: [PATCH 5/8] Augmented matrix updated --- src/solver/AugmentedMatrix.java | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/solver/AugmentedMatrix.java b/src/solver/AugmentedMatrix.java index c5477d9..434d636 100644 --- a/src/solver/AugmentedMatrix.java +++ b/src/solver/AugmentedMatrix.java @@ -1,12 +1,10 @@ package solver; -import java.lang.reflect.Array; -import java.text.DecimalFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.InputMismatchException; import java.util.Scanner; + public class AugmentedMatrix { private Row[] matrix; private String decimalPattern = "#.####"; @@ -15,7 +13,7 @@ public class AugmentedMatrix { private ArrayList swapHistory; - AugmentedMatrix(double[][] matrix) { + AugmentedMatrix(Complex[][] matrix) { setMatrix(matrix); } @@ -28,16 +26,16 @@ public void readMatrix(Scanner scanner) throws InputMismatchException { for (int i = 0; i < n; i++) { matrix[i] = new Row(m); for (int j = 0; j < m; j++) { - this.set(i, j, scanner.nextDouble()); + this.set(i, j, new Complex(scanner.next())); } } } - public void set(int i, int j, double value) { + public void set(int i, int j, Complex value) { matrix[i].set(j, value); } - public double get(int i, int j) { + public Complex get(int i, int j) { return matrix[i].get(j); } @@ -47,12 +45,13 @@ public String fancyPrint(String decimalPattern) { decimalPattern = this.decimalPattern; } StringBuilder sb = new StringBuilder(); - double[] row; + Complex[] row; for (int i = 0; i < matrix.length; i++) { row = matrix[i].getRow(); sb.append("["); for(int j =0; j < row.length; j++) { - sb.append(new DecimalFormat(decimalPattern).format(row[j])); + row[j].setDecimalPattern(decimalPattern); + sb.append(row[j].toString()); sb.append(", "); } sb.delete(sb.length()-2, sb.length()); @@ -90,7 +89,7 @@ public void swapColumns(int firstColumnIndex, int secondColumnIndex) { } public void swapColumns(int firstColumnIndex, int secondColumnIndex, boolean writeLog) { - double coeff1, coeff2; + Complex coeff1, coeff2; for(Row row : matrix) { coeff1 = row.get(firstColumnIndex); coeff2 = row.get(secondColumnIndex); @@ -106,15 +105,15 @@ public ArrayList getSwapHistory() { return swapHistory; } - public double[][] getMatrixCopy() { - double[][] copy = new double[matrix.length][]; + public Complex[][] getMatrixCopy() { + Complex[][] copy = new Complex[matrix.length][]; for(int i = 0; i < matrix.length; i++) { copy[i] = matrix[i].getRow(); } return copy; } - public void setMatrix(double[][] matrix) { + public void setMatrix(Complex[][] matrix) { this.matrix = new Row[matrix.length]; for(int i = 0; i < matrix.length; i++) { this.matrix[i] = new Row(matrix[i]); From d6e5a425d7a39840b7a36b81bc60e7f8a523862b Mon Sep 17 00:00:00 2001 From: Nail Iskhakov Date: Mon, 18 Feb 2019 23:45:21 +0300 Subject: [PATCH 6/8] Complex class added --- src/solver/Complex.java | 123 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/solver/Complex.java diff --git a/src/solver/Complex.java b/src/solver/Complex.java new file mode 100644 index 0000000..ec4fa58 --- /dev/null +++ b/src/solver/Complex.java @@ -0,0 +1,123 @@ +package solver; + +import java.text.DecimalFormat; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Complex { + private final double re; + private final double im; + private String decimalPattern = "#.####"; + + public static final Complex ZERO = new Complex(0, 0); + + public Complex(double real, double imag) { + this.re = real; + this.im = imag; + } + + public Complex(String str) { + Complex cmp = parseComplex(str); + this.re = cmp.re; + this.im = cmp.im; + } + + public Complex(Complex b) { + this.re = b.re; + this.im = b.im; + } + + @Override + public boolean equals(Object x) { + if (x == null) return false; + if (this.getClass() != x.getClass()) return false; + Complex that = (Complex) x; + return (this.re == that.re) && (this.im == that.im); + } + + + + public Complex add(Complex b) { + Complex a = this; + double real = a.re + b.re; + double imag = a.im + b.im; + return new Complex(real, imag); + } + + public Complex subtract(Complex b) { + Complex a = this; + double real = a.re - b.re; + double imag = a.im - b.im; + return new Complex(real, imag); + } + + public Complex multiply(Complex b) { + Complex a = this; + double real = a.re * b.re - a.im * b.im; + double imag = a.im * b.re + a.re * b.im; + return new Complex(real, imag); + } + + public Complex reciprocal() { + Complex a = this; + double denominator = a.re * a.re + a.im * a.im; + double real = a.re / denominator; + double imag = - a.im / denominator; + return new Complex(real, imag); + } + + public Complex divide(Complex b) { + Complex a = this; + return a.multiply(b.reciprocal()); + } + + public static Complex parseComplex(String inp) { + Pattern imagPattern = Pattern.compile("([+-]?\\d+(\\.?\\d+)?([eE][+-]?\\d)?[i|I])|([+-]?[i|I](\\d+(\\.?\\d+)?)?([eE][+-]?\\d)?)"); + + // My numerous attempts, i leave it here) + + // Pattern realPattern = Pattern.compile("([+-]?(?!i|I)?\\d++(\\.?\\d++)?([eE][+-]?\\d)?(?!i|I))"); + // Pattern realPattern = Pattern.compile("(?!(([+-]?\\d+(\\.?\\d+)?([eE][+-]?\\d)?[i|I])))"); + // Matcher re = realPattern.matcher(inp); + // re.find(); + // System.out.printf("Re: " + re.group() + " "); + + + Matcher im = imagPattern.matcher(inp); + String impart, repart; + try { + im.find(); + impart = im.group(); + } catch (IllegalStateException e) { + impart = ""; // If a repart or impart is blank it will be zero + } + repart = inp.replace(impart, ""); // It is easier than finding/creating regexp pattern to find real part + + if(impart.equals("+i") || impart.equals("+I") || impart.equals("i") || impart.equals("I")) { + impart = "1i"; + } else if(impart.equals("-i") || impart.equals("-I")) { + impart = "-1i"; + } + + double rep = repart.isBlank() ? 0 : Double.parseDouble(repart); + double imp = impart.isBlank() ? 0 : Double.parseDouble(impart.replaceAll("i|I", "")); + + return new Complex(rep, imp); + } + + public void setDecimalPattern(String pattern) { + this.decimalPattern = pattern; + } + + @Override + public String toString() { + String ans; + DecimalFormat f = new DecimalFormat(decimalPattern); + if (im == 0) return f.format(re) + ""; + if (re == 0) return f.format(im) + "i"; + if (im < 0) return f.format(re) + "-" + f.format(-im) + "i"; + return f.format(re) + "+" + f.format(im) + "i"; + } + + +} From 63eb3ebc14593fca37969f01318c1526a5c2390f Mon Sep 17 00:00:00 2001 From: Nail Iskhakov Date: Mon, 18 Feb 2019 23:45:55 +0300 Subject: [PATCH 7/8] Complex support added --- src/solver/LinearEquationSolver.java | 49 ++++++++++++++-------------- src/solver/Row.java | 36 ++++++++++---------- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/src/solver/LinearEquationSolver.java b/src/solver/LinearEquationSolver.java index d2f5eda..2a82b70 100644 --- a/src/solver/LinearEquationSolver.java +++ b/src/solver/LinearEquationSolver.java @@ -1,8 +1,7 @@ package solver; -import java.text.DecimalFormat; import java.util.ArrayList; -import java.util.Arrays; + enum SystemType { NO_SOLUTIONS, @@ -27,9 +26,9 @@ public class LinearEquationSolver { * It is step 1 of solving System of Linear Equations: transforming matrix to upper triangular form */ private void transformToUpperTriangularForm() { - Row currentRow; double currentCoefficient; - Row nextRow; double nextCoefficient; - double multiplicator; + Row currentRow; Complex currentCoefficient; + Row nextRow; Complex nextCoefficient; + Complex multiplicator; int n = matrix.size()[0]; // number of rows int m = matrix.size()[1]; // number of cols int rowNum, colNum; @@ -41,16 +40,16 @@ private void transformToUpperTriangularForm() { // Search for the row with non-zero coefficent int columnOffset = 0; boolean foundInColumn = false; - double coeff; int innerColNum = colNum; - while(currentCoefficient == 0 && innerColNum < m-1) { + Complex coeff; int innerColNum = colNum; + while(currentCoefficient.equals(Complex.ZERO) && innerColNum < m-1) { // If a current coefficient is zero, then we should find row with non-zero coefficient below - if (currentCoefficient == 0) { + if (currentCoefficient.equals(Complex.ZERO)) { foundInColumn = false; int nextRowNum; for (nextRowNum = rowNum; nextRowNum < n; nextRowNum++) { coeff = matrix.getRow(nextRowNum).get(innerColNum); - if (coeff != 0) { + if (!coeff.equals(Complex.ZERO)) { // Found row with non-zero i-th coefficient foundInColumn = true; break; @@ -84,14 +83,14 @@ private void transformToUpperTriangularForm() { // If after all (full search of non-zero coefficient in whole matrix) we have zero-coefficient // We should end up with matrix processing - matrix already transformed - if(currentCoefficient == 0) { + if(currentCoefficient.equals(Complex.ZERO)) { return; } // Make coefficient equal to one by dividing entire row by this coefficient - if(currentCoefficient != 1.0) { + if(!currentCoefficient.equals(new Complex(1, 0))) { currentRow.divide(currentCoefficient); - logMessage(String.format("R%d / %s -> R%d", rowNum, new DecimalFormat(decimalPattern).format(currentCoefficient), rowNum)); + logMessage(String.format("R%d / %s -> R%d", rowNum, currentCoefficient.toString(), rowNum)); } // Perform actions to zero coefficients in other rows @@ -100,10 +99,10 @@ private void transformToUpperTriangularForm() { nextCoefficient = nextRow.get(colNum); currentCoefficient = currentRow.get(colNum); // it must always be equal to one // nextCoefficient should be zero - if(nextCoefficient != 0) { - multiplicator = nextCoefficient / currentCoefficient * (-1); + if(!nextCoefficient.equals(Complex.ZERO)) { + multiplicator = nextCoefficient.divide(currentCoefficient).multiply(new Complex(-1, 0)); nextRow.add(currentRow.multiply(multiplicator, false)); - logMessage(String.format("%s * R%d + R%d -> R%d", new DecimalFormat(decimalPattern).format(multiplicator), rowNum, j, j)); + logMessage(String.format("%s * R%d + R%d -> R%d", multiplicator.toString(), rowNum, j, j)); } } } @@ -117,14 +116,14 @@ private void reduceNonDiagonalCoefficients() { int rowNum, colNum, nextRowNum; int n = matrix.size()[0]; int coefficientIndex = -1; - double currentCoefficient, nextCoefficient, multiplicator; + Complex currentCoefficient, nextCoefficient, multiplicator; Row row; Row nextRow; for(rowNum = n-1; rowNum >= 0; rowNum--) { // Find first non-zero element row = matrix.getRow(rowNum); for(int i = 0; i < row.size()-1; i++) { - if(row.get(i) != 0) { + if(!row.get(i).equals(Complex.ZERO)) { coefficientIndex = i; break; } @@ -141,10 +140,10 @@ private void reduceNonDiagonalCoefficients() { currentCoefficient = row.get(coefficientIndex); nextRow = matrix.getRow(nextRowNum); nextCoefficient = nextRow.get(coefficientIndex); - if (nextCoefficient != 0) { - multiplicator = nextCoefficient / currentCoefficient * (-1); + if (!nextCoefficient.equals(Complex.ZERO)) { + multiplicator = nextCoefficient.divide(currentCoefficient).multiply(new Complex(-1, 0)); nextRow.add(row.multiply(multiplicator, false)); - logMessage(String.format("%s * R%d + R%d -> R%d", new DecimalFormat(decimalPattern).format(multiplicator), rowNum, nextRowNum, nextRowNum)); + logMessage(String.format("%s * R%d + R%d -> R%d", multiplicator.toString(), rowNum, nextRowNum, nextRowNum)); } } @@ -244,10 +243,10 @@ private void logMessage(String msg) { * * @return solution - array of coefficients */ - private double[] getSingleSolution() { + private Complex[] getSingleSolution() { int n = matrix.size()[0]; // Number of rows int m = matrix.size()[1]; // Number of columns of augmented matrix - double[] solution = new double[m-1]; + Complex[] solution = new Complex[m-1]; int solutionIndex; for(int rowNum = 0; rowNum < n; rowNum++) { if(matrix.getRow(rowNum).isZeroFilled()) { @@ -279,10 +278,10 @@ public LinearEquationSolver solve() { result = "Infinite solutions"; return this; case SINGLE_SOLUTION: - double[] res = getSingleSolution(); + Complex[] res = getSingleSolution(); StringBuilder sb = new StringBuilder(); - for(double coefficient: res) { - sb.append(new DecimalFormat(decimalPattern).format(coefficient)); + for(Complex coefficient: res) { + sb.append(coefficient.toString()); sb.append("\n"); } sb.deleteCharAt(sb.length()-1); diff --git a/src/solver/Row.java b/src/solver/Row.java index 6143ea2..dfca440 100644 --- a/src/solver/Row.java +++ b/src/solver/Row.java @@ -3,28 +3,28 @@ import java.util.Arrays; public class Row { - private double[] row; + private Complex[] row; - Row(double[] row) { + Row(Complex[] row) { setRow(row); } Row(int m) { - row = new double[m]; + row = new Complex[m]; } - Row divide(double v) { - if(v == 0){ + Row divide(Complex v) { + if(v.equals(Complex.ZERO)){ throw new java.lang.ArithmeticException("Division by zero"); } for(int i = 0; i < row.length; i++) { - row[i] = row[i] / v; + row[i] = row[i].divide(v); } return this; } - Row multiply(double v, boolean inplace) { + Row multiply(Complex v, boolean inplace) { Row curRow; if(inplace) { curRow = this; @@ -32,18 +32,18 @@ Row multiply(double v, boolean inplace) { curRow = new Row(row.length); } for(int i = 0; i < row.length; i++) { - curRow.set(i, row[i] * v); + curRow.set(i, row[i].multiply(v)); } return curRow; } - Row multiply(double v) { + Row multiply(Complex v) { return multiply(v, true); } Row subtract(Row row) { for(int i = 0; i < this.row.length; i++) { - this.row[i] = this.row[i] - row.get(i); + this.row[i] = this.row[i].subtract(row.get(i)); } return this; } @@ -60,30 +60,30 @@ Row add(Row nextRow, boolean inplace) { curRow = new Row(row.length); } for(int i = 0; i < row.length; i++) { - curRow.set(i, curRow.get(i) + nextRow.get(i)); + curRow.set(i, curRow.get(i).add(nextRow.get(i))); } return curRow; } - public double[] getRow() { + public Complex[] getRow() { return Arrays.copyOf(row, row.length); } - public void setRow(double[] row) { + public void setRow(Complex[] row) { this.row = Arrays.copyOf(row, row.length); } - public void set(int j, double value) { - this.row[j] = value; + public void set(int j, Complex value) { + this.row[j] = new Complex(value); } - public double get(int j) { + public Complex get(int j) { return this.row[j]; } public boolean isZeroFilled() { for(int i = 0; i < row.length; i++) { - if( row[i] != 0) { + if( !row[i].equals(Complex.ZERO)) { return false; } } @@ -92,7 +92,7 @@ public boolean isZeroFilled() { public int getIndexOfFirstNonZeroElement() { for(int i = 0; i < row.length; i++) { - if(row[i] != 0 ) { + if(!row[i].equals(Complex.ZERO) ) { return i; } } From c01aed3b06036a8fc920fc39eea84aef984808f7 Mon Sep 17 00:00:00 2001 From: Nail Iskhakov Date: Mon, 18 Feb 2019 23:46:13 +0300 Subject: [PATCH 8/8] Files updated --- in.txt | 9 ++++----- out.txt | 4 +++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/in.txt b/in.txt index 090b2e8..dc67031 100644 --- a/in.txt +++ b/in.txt @@ -1,5 +1,4 @@ -4 4 -0 0 2 0 -0 1 5 1 -0 0 6 0 -0 0 12 0 \ No newline at end of file +3 4 +1+2i -1.5-1.1i 2.12 91+5i +-1+3i 1.2+3.5i -3.3 1+15i +12.31 1.3-5i 12.3i -78.3i \ No newline at end of file diff --git a/out.txt b/out.txt index af5f51f..f6b6cdc 100644 --- a/out.txt +++ b/out.txt @@ -1 +1,3 @@ -Infinite solutions \ No newline at end of file +6.7334-22.9975i +-1.7976+2.084i +15.6994+7.396i \ No newline at end of file