Quadratwurzeln über Newtons Methode in Java

Nov 06 2020

Dieses Mal gibt eines meiner Labors an, dass ich ein vollständiges Java-Programm schreiben muss, das die Quadratwurzel einer Zahl nach Newtons Methode berechnet. Der Grund für die Verwendung der Newton-Methode im Gegensatz zu Math.sqrt(x)ist, dass ich die Verwendung einfacher E / A, bedingter Ausdrücke, Schleifen und verschachtelter Schleifen üben kann.

Die vollständigen Anweisungen lauten wie folgt:

Angenommen, Sie möchten die Quadratwurzel von x berechnen .

Erstens: Wir immer mit einer Vermutung / Angleichung beginnen , daß die Quadratwurzel von Wert für x ist y = 1,0

Als nächstes berechnen wir den Durchschnitt dieses y-Wertes plus den x-Wert geteilt durch den y-Wert .

Diese Gleichung → (y + (x / y)) / 2.

Das Ergebnis der Lösung dieser Gleichung wird dann zur neuen Approximation der Quadratwurzel (dem neuen y- Wert). Dieser neue y- Wert liegt näher am tatsächlichen Wert für die Quadratwurzel von x als die ursprüngliche y- Schätzung von 1,0

Wiederholen Sie den obigen Schritt mit jedem neuen berechneten Wert für y als neue Schätzung für die Quadratwurzel von x, bis der y-Wert nahe genug ist .

Beispiel: Angenommen, wir möchten die Quadratwurzel von 2 berechnen, dann würde die Berechnung wie folgt ablaufen (alle Werte als doppelt): x = 2,0 und y = 1,0

Iteration     Guess   Quotient                 Average
Number        for y   x/y                      ( y + (x/y) ) / 2

        1       1.0   2.0/1.0 => 2.0           (1.0 + 2.0/1.0)/2.0 => 1.5
        2       1.5   2.0/1.5 => 1.33333       (1.5 + 1.33333)/2.0 => 1.41666
        3   1.41666   2.0/1.41666 => 1.41176   (1.41666 + 1.41176)/2.0 => 1.41421
        4   1.41421   etc...

Die Lösung wird erreicht, wenn das Quadrat der Schätzung „gleich“ ist wie der Wert von x (die Definition der Quadratwurzel). Leider kann für die meisten Zahlen die genaue Lösung niemals auf diese Weise erreicht werden, so dass der Prozess für immer fortgesetzt wird. Um sicherzustellen, dass der Berechnungsprozess tatsächlich stoppt, müssen wir die Stoppbedingung der Schleife so ändern, dass sie " nah genug " ist. Nah genug ist die Toleranz (eine kleine Zahl), die wir als gute Annäherung an den genauen Wert akzeptieren.

Nah genug tritt auf, wenn die absolute Differenz zwischen x und (y * y) geringer ist als die Toleranz (vom Benutzer definiert).

Um zu sehen, wie Ihr Programm voranschreitet, drucken Sie die Iterationsnummer und den Schätzwert aus. Verwenden Sie mindestens 8 Stellen nach dem Dezimalwert und 2 Stellen vor der Dezimalstelle. In dem unten gezeigten Beispiellauf werden nur 5 Stellen nach der Dezimalstelle verwendet.

Beispiellauf:

Square Root approximation program

Enter the value           : 2.0
Enter the tolerance       : 0.00001

Iteration     Guess      Guess     Absolute value
Number        value      Squared       Difference

        1   1.00000      1.00000          1.00000 
        2   1.50000      2.25000          0.25000 
        3   1.41666      2.00693          0.00693 
        4   1.41421      1.99999          0.00001 

Approximated Square root of 2.0 = 1.41422 

Der resultierende Code, den ich geschrieben habe, um dieses Problem zu lösen, lautet wie folgt:

import java.util.Scanner;

/**
 * SRN: 507-147-9
 */
public class Lab8_1 {

    public static void main(String[] args) {

        // new input
        Scanner input = new Scanner(System.in);

        // define vars
        int iteration = 1;
        double x, y, guessSquared = 1, quotient, average, tolerance, absValDiff = 1;

        y = 1.0;

        // program name
        System.out.println("Square Root approximation program");
        System.out.println();

        // prompt variables
        System.out.print("Enter the value           : "); // prompt x value
        x = input.nextDouble(); // store input as "x"
        System.out.print("Enter the tolerance       : "); // prompt tolerance
        tolerance = input.nextDouble(); // store input as "tolerance"
        System.out.println();

        // print formatted header
        System.out.println("Iteration         Guess          Guess         Absolute value");
        System.out.println("Number            value          Squared           Difference");
        System.out.println();

        // print first calculation
        System.out.printf("%9d   %11.8f      %11.8f          %11.8f \n", iteration, y, guessSquared, absValDiff);

        // increment the value of "iteration" by 1
        iteration++;

        while (absValDiff > tolerance) {

            // looped calculations
            quotient = x / y;
            average = (y + quotient) / 2;
            y = average;
            guessSquared = y * y;
            absValDiff = Math.abs(x - guessSquared);

            // print results per iteration
            System.out.printf("%9d   %11.8f      %11.8f          %11.8f \n", iteration, y, guessSquared, absValDiff);

            // increment the value of "iteration" by 1
            iteration++;
        }
        // print results
        System.out.println();
        System.out.printf("Approximated Square root of " + x + " = %11.8f \n", y);
    }
}

Damit habe ich eine Frage zur Richtigkeit meiner Antworten im Gegensatz zu denen im Beispiel. Obwohl mathematisch gesehen die richtigen Gleitkommazahlen in meinen Variablen gespeichert sind, wird beim Drucken der Ergebnisse auf der Konsole immer die letzte Ziffer meines Gleitkommas aufgerundet. Aus meiner Sicht ähnelt dies den Rundungsfehlern, die bei einem Taschenrechner auftreten würden, bei dem das Ergebnis der durchgeführten Berechnungen nur ungefähre Darstellungen der tatsächlichen Zahlen sind, da nur rationale Festkommazahlen genau in der Maschine dargestellt werden können.

Ein Beispiel für diese Art von Fehler wäre: 2/3 = 0,666666667

Gibt es eine Möglichkeit, eine festgelegte Länge für meine Variablen mithilfe des printfFormats zu erfassen, ohne zuzulassen, dass die Zahl beim Drucken auf der Konsole aufgerundet wird?

Antworten

7 GilbertLeBlanc Nov 06 2020 at 19:46

Gibt es eine Möglichkeit, eine festgelegte Länge für meine Variablen im printf-Format zu erfassen, ohne zuzulassen, dass die Zahl beim Drucken auf der Konsole aufgerundet wird?

Sie können einen "Trick" verwenden

String output = String.format("%12.9f", doubleValue);
System.out.println(output.substring(0, output.length() - 1);
6 htl Nov 06 2020 at 22:01

Ihr Programm sieht bisher ziemlich gut aus. Hier sind einige Dinge, die mir aufgefallen sind:

  • Es gibt einen logischen Fehler für Ihre erste Iteration bei der Berechnung absValDiff. Sie initialisieren immer absValDiffmit 1 und berechnen es nicht für die erste Iteration neu. Wenn Sie also einen Wert wie x = 5 eingeben, wird der Unterschied zwischen Ihrer ersten Schätzung (y = 1) im Quadrat und x nur 1 angegeben. wenn es tatsächlich 4 sein sollte.

  • Infolgedessen bedeutet dies auch, dass das Anfordern einer Toleranz größer oder gleich 1 nicht korrekt funktioniert und das Programm sofort beendet wird. Zum Beispiel nach einer Toleranz von 2 zu fragen, funktioniert nicht richtig.

  • Möglicherweise können Sie Ihre Kommentare schreiben, um für andere Leser nützlicher zu sein. Sofern Ihnen in Ihrem Kurs nichts anderes gesagt wurde, können Sie davon ausgehen, dass die Person, die Ihren Code liest, versteht, was die Sprache tut, und sich mehr dafür interessiert, warum Ihr Code die Schritte unternimmt, die er tut. Beispielsweise:

    // define vars
    int iteration = 1;
    double x, y, guessSquared = 1, quotient, average, tolerance, absValDiff = 1;
    

    Es ist klar, dass Sie hier Variablen definieren, sodass Sie versuchen können, stattdessen zu erklären, was Sie tun. Warum nicht versuchen, dem Leser zu sagen, wozu die Variablen dienen?

  • Normalerweise ist es einfacher, Ihre Variablen nach Bedarf zu deklarieren, als alle oben in der Methode. Eine weitere Referenz finden Sie im Abschnitt über Erklärungen hier . Wie in dieser Quelle erwähnt, war es in älteren Programmiersprachen Tradition, alle Ihre Variablen im Voraus zu deklarieren, aber es wird im Allgemeinen nicht als bewährte Methode angesehen, dies jetzt zu tun.

Wenn Sie all dies ändern, sieht Ihr Code möglicherweise folgendermaßen aus:

import java.util.Scanner;

/**
 * SRN: 507-147-9
 */
public class Lab8_1 {

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        int iteration = 1; // Counts the current iteration number at each step.
        double y = 1.0; // Initial guess for the square root of the number x.
        double guessSquared = y * y; // The value y^2 which we want to be close to x.

        System.out.println("Square Root approximation program");
        System.out.println();

        System.out.print("Enter the value           : ");
        // The number we intend to approximate the square root of.
        double x = input.nextDouble(); 
        System.out.print("Enter the tolerance       : ");
        // The tolerance so that we terminate if |y^2 - x| < tolerance.
        double tolerance = input.nextDouble();
        System.out.println();
        
        // Compute the initial difference between x and the square of
        // the approximate square root.
        double absValDiff = Math.abs(x - guessSquared);

        // Print formatted header.
        System.out.println("Iteration         Guess          Guess         Absolute value");
        System.out.println("Number            value          Squared           Difference");
        System.out.println();

        // Print the results for the first iterate
        System.out.printf("%9d   %11.8f      %11.8f          %11.8f \n", iteration, y, guessSquared, absValDiff);
        iteration++;

        // Iterate using Newton's method until we obtain a value within tolerance of
        // the true square root.
        while (absValDiff > tolerance) {
            // Calculate the new approximation for x using 
            // Newton's method, where given the previous approximation y,
            // the next approximation is given by (y + (x / y)) / 2.
            double quotient = x / y;
            y = (y + quotient) / 2;
            guessSquared = y * y;
            // Compute the new difference between the square of our approximation and x.
            absValDiff = Math.abs(x - guessSquared);

            // Print results per iteration.
            System.out.printf("%9d   %11.8f      %11.8f          %11.8f \n", iteration, y, guessSquared, absValDiff);
            iteration++;
        }
        // Print results for the final iteration which was in tolerance.
        System.out.println();
        System.out.printf("Approximated Square root of " + x + " = %11.8f \n", y);
    }
}

Um die Rundung (und stattdessen zu vermeiden kürzen die Zahl), könnten Sie DecimalFormat , wie erläutert hier , wenn Sie einen alternativen Ansatz zu Gilbert Le Blanc Methode wollte. Nach dem Importieren von java.text.DecimalFormatund java.math.RoundingModekönnen Sie es wie folgt verwenden:

DecimalFormat df = new DecimalFormat("#.##");
df.setRoundingMode(RoundingMode.DOWN);
System.out.println(df.format(0.66999f));

was ausgeben würde 0.66.