diff --git a/.idea/misc.xml b/.idea/misc.xml index a165cb3..df60b67 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/src/plan.txt b/src/plan.txt new file mode 100644 index 0000000..1a17dd1 --- /dev/null +++ b/src/plan.txt @@ -0,0 +1,60 @@ +На этой стадии нужно написать программу, которая +1.считывает коэффициенты системы линейных уравнений из файла. +1.1 Первая строка содержит N - число переменных и уравнений. +1.2 Каждая последующая строка - N+1 чисел - N коэффициентов уравнения, + и последнее число - правая часть уравнения - константа. +2.решает систему линейных уравнений +3.записывает ответ в другой файл. +3.1 В файл должны быть записаны только ответы, разделенные "\n". +4.пути к файлам следует передавать как аргументы командной строки. +5.Весь процесс решения/преобразований со строками должен быть выведен на консоль. +6.Постарайтесь создать различные классы такие как: Matrix, Row, LinearEquation. +Пример вывода: The lines which start with > represent the user input. + +> java Solver -in in.txt -out out.txt +Start solving the equation. +Rows manipulation: +-2 * R1 + R2 -> R2 +-3 * R1 + R3 -> R3 +0.5 * R2 -> R2 +-3 * R2 + R3 -> R3 +-2 * R3 -> R3 +3.5 * R3 + R2 -> R2 +-2 * R3 + R1 -> R1 +-1 * R2 + R1 -> R1 +The solution is: (1.0, 2.0, 3.0) +Saved to file out.txt + +Алгоритм преобразований. +1.Обнулить все коэффициенты первого столбца кроме первого, он должен стать = 1. +2.Последовательно обнулить все коэффициенты второго и последующего столбцов +пока в последней строке все коэффициенты не обнулятся кроме последнего столбца. +3.Вторая часть алгоритма - обнулять коэффициенты выше диагонали, +начиная с последней строки до первой. +4.После этих преобразований решением уравнений будет правая часть матрицы. +Стадия 4. +Может быть случай, когда коэффициент, на который мы будем умножать будет равен 0. +Поэтому перед операцией умножения нужно проверить, коэффициент на равенство 0. +Если нужно искать ненулевой элемент ниже и правее. +1. Если ненулевой элемент ниже, то нужно переставить строки местами +2. Если ниже все элементы нулевые, то продолжаем искать правее +от элемента. Если находим, переставляем столбцы,строки. +Состояние столбцов/строк до перестановки необходимо запомнить, +чтобы после реализации вернуть все на место в исходном порядке и +вывести решение в нужном порядке. В общей сложности может быть +много перестановок. +Если ненулевой элмент не находится ниже и правее, то ищется +ненулевой элемент по всей левой части матрицы и при нахождении +переставляем соответствущие строки и столбцы. +Если ненулевой элемент не найден, первая часть алгоритма завершается. + +После этого проверяем на количество решений. +1. все коэффициенты левой части нулевые, п.ч. ненулевая - нет решений. +2. если количество ненулевых элементов на главной диагонали +совпадает с количеством уравнений (в п.ч. не нули) - одно решение +3. если на главной дигонали есть нулевые элементы + в п.ч.нули +-> бесконечно много решений. +C1,C2,C3(1<->2)C2,C1,C3(1<->3)C3,C1,C2 +colMove[1]=3 +colMove[2]=1 +colMove[3]=2 diff --git a/src/solver/InvalidDataFileFormat.java b/src/solver/InvalidDataFileFormat.java new file mode 100644 index 0000000..589a023 --- /dev/null +++ b/src/solver/InvalidDataFileFormat.java @@ -0,0 +1,7 @@ +package solver; + +public class InvalidDataFileFormat extends Exception{ + public InvalidDataFileFormat(String msg, Exception cause) { + super(msg, cause); + } +} diff --git a/src/solver/Main.java b/src/solver/Main.java index 4404714..5c8706d 100644 --- a/src/solver/Main.java +++ b/src/solver/Main.java @@ -1,7 +1,125 @@ package solver; +import java.io.File; + +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + public class Main { - public static void main(String[] args) { - System.out.print("Hello world!"); + static String inFile; + static String outFile; + public static void main(String[] args) throws InvalidDataFileFormat { + //get_in_file + if (args.length!=4) { + System.out.println("Invalid comand-line arguments"); + return; + } + + for (int i = 0; i < args.length; i+=2) { + if (args[i].equalsIgnoreCase("-in")) + inFile = args[i+1]; + else if (args[i].equalsIgnoreCase("-out")) + outFile = args[i+1]; + } + try { + List lines = Files.readAllLines( Paths.get(inFile)); + int n=0,v=0; + int k=0; + try { + for (String s:lines.get(0).split("\\s")) { + if (k==0) + n = Integer.parseInt(s);//количество строк - уравнений + else + v = Integer.parseInt(s);//количество столбцов - переменных + k++; + } + if (v==0) v=n; + } + catch (NumberFormatException e){ + throw new InvalidDataFileFormat( + //String.format("The string '%s' cannot be parsed as an array of numbers", s), + String.format("Incorrect data file format, '%s' cannot be parsed as two numbers", lines.get(0)), + e); + } + lines.remove(0); + if (lines.size() != n) { + throw new InvalidDataFileFormat("Incorrect data file format, count of data rows isn't equal n", null); + } + System.out.println("Start solving the equation.\n" + + "Rows manipulation:"); + Matrix m = new Matrix(n,v); + for (String line : lines) { + m.addRow(lines.indexOf(line), line); + } + String manipulation; + //m.otladka = true; + m.print(); + for (int i = 0; i < m.M; i++) { + while (true) + { + manipulation = m.rows[i].transform1(i); + if (!manipulation.equals("")) { + System.out.println(manipulation); + m.print(); + if (!manipulation.contains("<->")) break; + } else break; + }; + + for (int j = i+1; j < m.N; j++) { + manipulation = m.rows[j].transform0(i, m.rows[i]); + if (!manipulation.equals("")) { + System.out.println(manipulation); + m.print(); + } + } + } + int check = m.checkSolution(); + + String result=""; + switch (check) { + case 0: { + result = "Infinite solutions"; + System.out.println(result); + break; + } + case -1: { + result = "No solutions"; + System.out.println(result); + break; + } + case 1: { + System.out.println("-------"); + for (int i = m.N - 1; i >= 0; i--) { + for (int j = i - 1; j >= 0; j--) { + manipulation = m.rows[j].transform0(i, m.rows[i]); + if (!manipulation.equals("")) { + System.out.println(manipulation); + m.print(); + } + } + } + result = m.printSolution(); + break; + } + default:break; + } + File file = new File(outFile); + try (FileWriter writer = new FileWriter(file)) { + writer.write(result); + System.out.printf("Saved to file %s\n",outFile); + } catch (IOException e) { + System.out.printf("File writing error %s", e.getMessage()); + } + + } + catch (IOException e){ + System.out.println("File reading error: " + inFile); + } + catch (InvalidDataFileFormat | ParsingArrayException e){ + System.out.println(e.getMessage()); + } } } \ No newline at end of file diff --git a/src/solver/Matrix.java b/src/solver/Matrix.java new file mode 100644 index 0000000..5ef9a70 --- /dev/null +++ b/src/solver/Matrix.java @@ -0,0 +1,176 @@ +package solver; + +import java.util.Arrays; + +//import java.util.Arrays; +class Pos{ + int x; + int y; + + public Pos(int x, int y) { + this.x = x; + this.y = y; + } + public String asString(){ + return this.x+","+this.y; + } +} + +public class Matrix { + public static final double threshold = 0.000001; + int N;//количество строк + int M;//количество столбцов + Row[] rows; + int[] colMove; + int iteration = 0; + private boolean colsSwaped = false; + + public boolean otladka = false; + + public static boolean compareDouble(double d1, double d2){ + return (Math.abs(d1 - d2) < threshold); + } + + public Matrix(int n, int m) { + N = n; + M = m; + rows = new Row[n]; + colMove = new int[m];//сюда будем записывать перемещения столбов + for (int i = 0; i < M; i++) + this.colMove[i] = i; + } + + public void addRow(int index, String str) throws ParsingArrayException { + this.rows[index] = new Row(index+1, M+1, this); + this.rows[index].fillRow(str); + } + + public void print(){ + if (!otladka) return; + for (Row r:this.rows) { + System.out.println(r.asString()); + } + } + + public String printSolution(){ + if (colsSwaped) return printSolution1(); + String sOut = "The solution is: ("; + String result=""; + double s; + for (Row r:this.rows) { + s =r.getCol(this.M); + result+=s+"\n"; + sOut += s+", "; + } + System.out.println(sOut.replaceAll(",\\s$", ")")); + return result; + } + + private String printSolution1(){ + String sOut = "The solution is: ("; + double[] d= new double[colMove.length]; + String result=""; + for (int i = 0; i < colMove.length; i++) { + d[colMove[i]]=this.rows[i].getCol(this.M); + } + for (int i = 0; i < colMove.length; i++) { + result+=d[i]+"\n"; + sOut += d[i]+", "; + } + System.out.println(sOut.replaceAll(",\\s$", ")")); + return result; + } + + public String swapRows(int src, int dest){ + try { + Row r1 = this.rows[src]; + Row r2 = this.rows[dest]; + iteration++; + String result = String.format("%d: %s<->%s", iteration, r1.getName(), r2.getName()); + Row tmp; + tmp = this.rows[dest]; + this.rows[dest] = this.rows[src]; + this.rows[src] = tmp; + this.rows[src].setMoved(true, src + 1); + this.rows[dest].setMoved(true, dest + 1); + return result; + } + catch (Exception e){ + System.out.println(String.format("SwapRow(src=%d,dest=%d)",src,dest)); + throw e; + } + } + + + public String swapColumns(int src, int dest){ + for (Row r:this.rows) { + r.swapCells(src,dest); + } + int tmp =this.colMove[dest]; + this.colMove[dest] = this.colMove[src]; + this.colMove[src] = tmp; + iteration++; + colsSwaped = true; + return String.format("%d; C%d<->C%d",iteration, src,dest); + } + + public int findNonZeroBelow(int col, Row rowBelow){ + for (int i = rowBelow.getRowNum()/*-1+1*/; i < N; i++) { + //if (this.rows[i].getCol(col)!=0) return i; + if (!compareDouble(this.rows[i].getCol(col),0)) return i; + } + return -1; + } + + public int findNonZeroRighter(int colAfterIndex, Row row){ + for (int j = colAfterIndex+1; j < M; j++) { +// if (row.getCol(j)!=0) return j; + if (!compareDouble(row.getCol(j),0)) return j; + } + return -1; + } + + public Pos findNonZeroEverywhere(int colAfterIndex, Row rowBelow){ + int fromRow=0; + if (rowBelow == null) fromRow = 0; + else fromRow = rowBelow.getRowNum(); + for (int i = fromRow; i < N; i++) { + for (int j = colAfterIndex+1; j < M; j++) { +// if (this.rows[i].getCol(j)!=0) return new Pos(i,j); + if (!compareDouble(this.rows[i].getCol(j),0)) return new Pos(i,j); + } + } + return null; + } + + public Pos getNumberOfZeroRows(){ + //в ч запишем количество нулевых строк, + //в y количество нулевых строк с ненулевой правой частью + Pos result= new Pos(0,0); + int cnt0 =0, cnt1 =0; + for (Row r:this.rows) { + cnt0=0; + for (int i = 0; i < M+1; i++) { + //if (r.getCol(i)!=0) { + if (!compareDouble(r.getCol(i),0)){ + if (i==M) result.y++; + break; + } + cnt0++; + } + if (cnt0==M+1) result.x++; + } + return result; + } + + public int checkSolution(){ + Pos zeroRowsCnt = this.getNumberOfZeroRows(); + if (otladka) + System.out.println(zeroRowsCnt.asString()); + if (zeroRowsCnt.y>0) return -1;//no solutions + int cntEq = this.N-zeroRowsCnt.x;//количество ненулевых уравнений + if (cntEq==this.M) return 1;//one solution + if (cntEq -1) + return this.owner.swapRows(this.rowNum-1,nonZeroInd); + nonZeroInd = this.owner.findNonZeroRighter(index,this); + if (nonZeroInd > -1) + return this.owner.swapColumns(index,nonZeroInd); + //поиск по всей матрице + Pos pos = this.owner.findNonZeroEverywhere(index,this); + if (pos == null) return ""; + return this.owner.swapColumns(index,pos.y)+";"+ + this.owner.swapRows(this.rowNum-1, pos.x); + } + double koef = 1/this.data[index]; + this.mult(koef); + this.data[index] = 1; +// return String.format("%-8.2f %n",koef) +" * "+this.getName()+" -> "+this.getName(); + return String.format("%d: %.2f * %s -> %s",++this.owner.iteration, koef,this.getName(),this.getName()); + } + + public String transform0(int index, Row r){ + if (Matrix.compareDouble(this.data[index],0)) return "";//"already transformed0"; + double koef = -1 * this.data[index]; + this.add1(r,koef); + this.data[index] = 0; + this.owner.iteration++; +// return koef + " * "+ r.getName() + " + "+ this.getName()+" -> "+this.getName(); + return String.format("%d: %.3f * %s -> %s",this.owner.iteration, koef,r.getName(),this.getName()); + } + + public double getCol(int index){ + return this.data[index]; + } + + public void fillRow(String s) throws ParsingArrayException { + String[] str = s.split("\\s+"); + try { + if (str.length!=this.colCnt) + throw new ParsingArrayException( + String.format("The string '%s' cannot be parsed correctly. It has to contain %d numbers", s, this.colCnt+1), + null); + for (int i = 0; i < str.length; i++) { + this.data[i] = Double.parseDouble(str[i]); + } + } catch(NumberFormatException e){ + throw new ParsingArrayException( + String.format("The string '%s' cannot be parsed as an array of numbers", s), + e); + } + catch (ArrayIndexOutOfBoundsException e){ + throw new ParsingArrayException( + String.format("The string '%s' cannot be parsed correctly, count of numbers is incorrect", s), + e); + } + } + + public String asString() { + String s=""; + for (double d:this.data) { + s +=String.format("%-10.3f ",d); + //s +=String.format("%.3f ",d); + }; + return this.getName()+((this.getOldRowNum()!=this.getRowNum())?String.format("(%d)",this.getOldRowNum()):"")+": "+ s.trim(); + } + + public boolean isMoved() { + return isMoved; + } + + public void setMoved(boolean moved, int newRow) { + isMoved = moved; + if (moved) { + /*if (this.oldRowNum == this.rowNum) { + this.oldRowNum = rowNum; + }*/ + this.rowNum = newRow; + } + if (this.rowNum == this.oldRowNum) isMoved = false; + } + public void swapCells(int src, int dest){ + double tmp; + tmp = this.data[dest]; + this.data[dest] = this.data[src]; + this.data[src] = tmp; + } +}