// Autor: Martin Heinrich // MatNr: 0125222 // BspNr: 1118 // Titel: Gleichung mit drei Variablen // Beschreibung: Ein lineares inhomogenes Gleichungssystem von drei Gleichungen in drei Unbekannten soll gelöst werden. // Eingabe: 12 integer-Werte (je 4 Werte pro Gleichung: 3 Koeffizienten und eine Konstante als Ergebnis) // Ausgabe: 3 float-Werte // Fehlerbehandlung: alle Werte lesen, einmalige Meldung mit Zeilenvorschub, keine weitere Berechnung // Fehlermeldungen: "?" bei ungültigen Datentypen, "FALSCHE EINGABE" bei inhaltlichen Fehlern import eprog.*; // Verwendung des EPROG-Packages public class dreigl extends EprogIO // In der Spezifikation ist der Dateiname klein geschrieben! { // Methoden der EprogIO können ohne Klassenangabe verwendet werden //**************************************************************** public static boolean getEquations(byte count, int[][] matrix) // getEquations liest count*(count+1) integer-Werte in die int-Matrix ein. // Im Fall eines ungültigen Datentyps liefert sie false, im Erfolgsfall true. // count gibt die Zahl der Gleichungen an. // matrix enthält im Erfolgsfall die Werte (1. Dimension entspricht den Zeilen); muss initialisiert sein! // Der Datentyp float ist für die weitere Berechnung in der Klasse dreigl erforderlich. { boolean retVal = true; // Rückgabewert der Methode (false sollte der Ausnahmefall sein) for (byte line = 0; line < count; line++) // Zeilenweises Einlesen (count mal) for (byte col = 0; col <= count; col++) // Spalten einer Zeile (count+1 mal) { try // Fehlerbehandlung { matrix[line][col] = readInt(); } // try catch (EprogException e) {retVal = false;}; // Es wird trotzdem weitergelesen } // for (col) ; // for (line) return retVal; } // getEquations //**************************************************************** //**************************************************************** public static boolean isInhomogen(int[][] matrix, byte column) // isInhomogen überprüft, ob zumindest einer der Werte in der count-ten Spalte von 0 verschieden ist. // Falls alle Werte der count-ten Spalte (dh. 2. Dimension [count]) 0 sind, liefert sie false, sonst true // Die Methode verwendet keine Fehlerbehandlung, dh. das array sollte entsprechend groß sein! { int lines = matrix.length; // Length soll nur einmal aufgerufen werden for (int line = 0; line < lines; line++) { if (matrix[line][column] != 0) { return true; // Beim ersten Wert != 0 ist die Bedingung bereits erfüllt }; // if (matrix != 0) }; // for (line) return false; }; // isInhomogen //**************************************************************** //**************************************************************** public static int checkValues(int[][] matrix, byte rangeMin, byte rangeMax) // checkValues überprüft die Koeffizienten auf den vorgegebenen Wertebereich (Grenzen eingeschlossen) // Der Rückgabewert ist 0, wenn alle Koeffizienten in dem geschlossenen Intervall [rangeMin, rangeMax] liegen. // Falls rangeMin > rangeMax oder ein Fehler auftritt wird der Wert -1 ausgegeben. // Ansonsten wird die Nummer (zeilenweise, dh 1. Dimension ist äußere Schleife) des verletzenden Koeffizienten ausgegeben. // matrix muss ein zweidimensionales, rechteckiges (gleich lange Zeilen) Datenfeld enthalten. // rangeMin/-Max geben das geschlossene Intervall vor (rangeMin stellt die Untergrenze dar). { int lines; // Mächtigkeit der 1. Dimension (um ein wiederholtes Abfragen von length zu verhindern). int cols; // Mächtigkeit der 2. Dimension. try // Fehlerbehandlung { if (rangeMin > rangeMax) {return -1;}; // Im catch-Fall wird -1 zurückgegeben. lines = matrix.length; // Initialisierung im try-Block (das Array könnte leer sein! -> Fehler) cols = matrix[0].length; // Initialisierung nur über 1. Zeile, aber Fehler werden sowieso abgefangen. for (int line = 0; line < lines; line++) // Durchlaufen aller Zeilen for (int col = 0; col < cols; col++) // Durchlaufen aller Spalten { if ((matrix[line][col] < rangeMin) || (matrix[line][col] > rangeMax)) // "Nicht im Bereich" heißt "kleiner als Unter- oder größer als Obergrenze" { return (line * cols + col + 1); }; // if ("Nicht im Bereich") } // for (col) ; // for (line) } // try catch (Exception e) {return -1;}; return 0; } // checkValues //**************************************************************** //**************************************************************** public static boolean solveEquations(byte count, int[][] matrix, float[] solutions) // solveEquations löst das Gleichungssystem, das durch count und matrix angegeben wird. // Falls das System nicht eindeutig lösbar ist (oder ein Fehler auftritt), liefert sie false, im Erfolgsfall true. // count gibt die Zahl der Gleichungen an, muss >= 2 sein! // matrix muss ein zumindest count entsprechendes Array mit Koeffizienten sein. // matrix wird durch den Rechenvorgang verändert! // solutions enthält im Erfolgsfall die Lösungen; muss initialisiert sein (zumindest count Elemente) // ALGORITHMUS: Die Matrix wird auf Halbdiagonalform gebracht (näheres siehe dreigl.txt). { int[] tempVar; // 1. Referenz zum Tauschen zweier Zeilen // 2. Referenz um mehrfaches Berechnen des Index zu verhindern int factorBottom; // Erster Koeffizient der zu ersetzenden Zeile = Faktor für die subtrahierte Zeile int factorCurrent; // Erster Koeffizient der subtrahierten Zeile = Faktor für die zu ersetzende Zeile try // Fehlerbehandlung: Bei Unlösbarkeit wird ein Fehler erzeugt und alle anderen Anweisungen übersprungen. { for (byte countDown = count; countDown > 1; countDown--) // Zurückführen des Gleichungssystems auf eine Gleichung in einer Variable { if (matrix[countDown - 1][count - countDown] == 0) // Wenn der erste Koeffizient 0 ist, // muss eine andere Gleichung verwendet werden um das System zu reduzieren. { for (byte anyEqu = (byte)(countDown - 2); anyEqu >= -1; anyEqu--) // Suchen einer Gleichung mit erstem Koeffizienten != 0; Wenn keine gültige Zeile // gefunden wird tritt später ein Fehler auf (Zugriff auf matrix[-1]) -> unlösbar! { if (matrix[anyEqu][count - countDown] != 0) // In diesem Fall, kann die aktuelle Zeile mit der unbrauchbaren vertauscht werden. { tempVar = matrix[anyEqu]; matrix[anyEqu] = matrix[countDown - 1]; matrix[countDown - 1] = tempVar; break; // Es genügt, eine gültige Zeile zu finden }; // if (matrix != 0) }; // for (anyEqu) }; // if (matrix == 0) for (byte allEqu = (byte)(countDown - 2); allEqu >= 0; allEqu--) // Modifizieren ALLER anderen Gleichungen um erste Koeffizienten =0 zu erhalten { factorBottom = matrix[allEqu][count - countDown]; factorCurrent = matrix[countDown - 1][count - countDown]; matrix[allEqu][count - countDown] = 0; // Die Berechnung in der Schleife wäre überflüssig. tempVar = matrix[countDown - 1]; for (byte allCol = (byte)(count - countDown + 1); allCol <= count; allCol++) // ALLE Koeffizienten werden modifiziert. { matrix[allEqu][allCol] *= factorCurrent; matrix[allEqu][allCol] -= factorBottom * tempVar[allCol]; } // for (allCol) } // for (allEqu) }; // for (countDown) // Ab hier enthält jede (i-te) Zeile count-i führende Nullen. Die erste Variable kann in der ersten Zeile // abgelesen werden, durch Einsetzen können die restlichen ermittelt werden: if (matrix[0][count - 1] == 0) {return false;}; solutions[count - 1] = (float)matrix[0][count] / matrix[0][count - 1]; // erste Zeile for (byte getSol = (byte)(count - 2); getSol >= 0; getSol--) // alle Gleichungen // Zuerst alle schon vorhandenen Lösungen einsetzen und "auf die rechte Seite bringen": { tempVar = matrix[count - getSol - 1]; solutions[getSol] = tempVar[count]; // Beim Einsetzen der Lösungen kann der Typ integer verlassen werden -> // -> Berechnungen in solutions (Typ float) for (byte allCol = (byte)(getSol + 1); allCol < count; allCol++) // Alle Koeffizienten != 0 außer dem ersten werden durchlaufen { solutions[getSol] -= tempVar[allCol] * solutions[allCol]; }; // for (allCol) // Danach "rechte Seite durch linke Seite"; BEACHTE: 0*x=k -> nicht (eindeutig) lösbar -> Fehler if (tempVar[getSol] == 0) {return false;}; solutions[getSol] /= tempVar[getSol]; }; // for (getSol) } // try catch (Exception e) {return false;}; return true; } // solveEquations //**************************************************************** //**************************************************************** public static boolean printArrayFixedln(float[] data) // printArrayFixedln gibt die Werte von data mit EprogIO.printFixed durch je // ein Leerzeichen getrennt aus und fügt einen Zeilenvorschub an. // Falls ein Fehler auftritt, liefert sie false, im Erfolgsfall true. // Klassenintern (beim Aufruf in dreigl.main) kann vom Erfolgsfall ausgegangen werden. // data enthält eine beliebige Anzahl von float-Werten (zumindest 2, da sonst ein Fehler auftritt) { String spaceChar = " "; // Benannte Konstante, weil ich nicht weiß ob sonst in jeder Schleifen- // ausführung ein String (Klasse) erzeugt werden muss! int dataLength; // Anzahl der Elemente von data (um ein wiederholtes Abfragen von length zu verhindern). // dataLength wird um 1 verkleinert, weil nach dem letzten Wert kein Leerzeichen // sondern ein Zeilenvorschub folgt try // Fehlerbehandlung (klassenintern dürfte hier kein Fehler auftreten) { dataLength = data.length - 1; // Initialisierung im try-Block (das array könnte leer sein! -> Fehler) for (int loop = 0; loop < dataLength; loop++) // Läuft vom ersten zum vorletzten Element { printFixed(data[loop]); // Gibt den entsprechenden Wert aus. print(spaceChar); // Gibt das Leerzeichen zwischen den Werten aus. }; // for (loop) printFixed(data[dataLength]); println(); // Gibt den letzten Wert und einen Zeilenvorschub aus. // printFixedln hat nicht funktioniert! } // try catch (Exception e) {return false;}; return true; } // printArrayFixedln //**************************************************************** //**************************************************************** public static void main(String[] args) // Das Hauptprogramm entält nur die Aufrufe und die Ausgabe von Fehlermeldungen { final byte equCount = 3; // Anzahl der Gleichungen bzw. Variablen final byte rangeMin = -20; // Kleinster erlaubter Eingabewert final byte rangeMax = 20; // Größster erlaubter Eingabewert // ACHTUNG: Beim Vergrößern des erlaubten Bereichs und/oder des Gleichungssystems (equCount) sollten // mögliche Überläufe beim Berechnen zumindest dokumentiert werden. String msgTypeErr = "?"; // Fehlermeldung bei ungültigem Datentyp String msgValErr = "FALSCHE EINGABE"; // Fehlermeldung bei inhaltlich falscher Eingabe int[][] matrix = new int[equCount][equCount+1]; // Enthält die Gleichungen (Koeffizienten) // Das Feld hat in allen Prozeduren den selben Namen, auch wenn es unterschiedliche Referenzen sind. // Das array wird sofort erstellt. float[] solutions; // Enthält die Lösungen // Das Feld hat in allen Prozeduren den selben Namen, auch wenn es unterschiedliche Referenzen sind. if (getEquations(equCount, matrix)) // Die Werte sollen eingelesen werden, Berechnungen werden nur im Erfolgsfall durchgeführt. { solutions = new float[equCount]; // Das array muss außerhalb von solveEquations erstellt werden. if (isInhomogen(matrix, equCount) && (checkValues(matrix, rangeMin, rangeMax) == 0) && solveEquations(equCount, matrix, solutions)) // Die Ausgabe der Lösungen erfolgt, wenn das Gleichungssystem inhomogen ist UND // alle Koeffizienten im erlaubten Intervall liegen UND // der Lösungsalgorithmus zum Erfolg führt. // BEACHTE: solveEquations wird nur aufgerufen, wenn alle Werte erlaubt sind ("short circuiting"). { printArrayFixedln(solutions); // Ausgabe der Lösungen im Erfolgsfall } // if (isInhomogen) && (checkValues) && (solveEquations) else { // if !(isInhomogen && checkValues && solveEquations) println(msgValErr); // Ausgabe der Meldung bei inhaltlichen Fehlern/Unlösbarkeit }; // else (isInhomogen) && (checkValues) && (solveEquations) } // if (getEquations) else { // if !getEquations println(msgTypeErr); // Ausgabe der Meldung bei ungültigem Datentyp } // else (getEquations) } // main //**************************************************************** } // dreigl