Java'da Newton yöntemi ile karekökler

Nov 06 2020

Bu sefer laboratuvarlarımdan biri, Newton yöntemi ile bir sayının karekökünü hesaplayacak eksiksiz bir java programı yazmam gerektiğini belirtiyor. Bunun tersine, Newton'un yöntemini kullanmanın arkasındaki sebep Math.sqrt(x), basit GÇ, koşullu ifadeler, döngüler ve iç içe döngülerin kullanımını pratik yapabilmemdir.

Talimatların tamamı aşağıdaki gibidir:

X'in karekökünü hesaplamak istediğinizi varsayalım .

İlk: herhangi değerin karekökü o hep bir tahmin / yakınlaştırılması ile başlar x olan y = 1.0

Sonra, bu y değerinin ortalamasını artı x değerinin y değerine bölünmesini hesaplıyoruz .

Bu denklem → (y + (x / y)) / 2.

Bu denklemi çözmenin sonucu, karekökün yeni yaklaşımı (yeni y değeri) olur. Bu yeni y değeri, x'in karekökü için gerçek değere , 1.0 olan orijinal y tahmininden daha yakın olacaktır.

Y değeri yeterince yakın olana kadar x'in karekökü için yeni tahmin olarak y için her yeni hesaplanan değeri kullanarak yukarıdaki adımı tekrarlayın .

Örneğin: 2'nin karekökünü hesaplamak istediğimizi varsayalım, o zaman hesaplama şu şekilde ilerleyecektir (tüm değerler çift olarak): x = 2.0 ve 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...

Çözüm, tahminin karesi x'in değeriyle (karekök tanımı) "aynı" olduğunda elde edilir . Ne yazık ki, çoğu rakam için kesin çözüme asla bu şekilde ulaşılamaz, bu nedenle süreç sonsuza kadar devam eder. Hesaplama işleminin gerçekten durduğundan emin olmak için, döngünün durdurma koşulunu " yeterince yakın " olacak şekilde değiştirmeliyiz. Yeterince yakın , tam değerin iyi bir tahmini olarak kabul ettiğimiz toleranstır (küçük bir sayı).

X ve (y * y) arasındaki mutlak fark toleranstan (kullanıcı tarafından tanımlanan) az olduğunda yeterince yakın olur .

Programınızın nasıl ilerlediğini görmek için yineleme numarasını ve tahmin değerini yazdırın. Ondalık değerden sonra en az 8 rakam ve ondalık değerden önce 2 rakam kullanın . Aşağıda gösterilen örnek çalışmada, ondalıktan sonra yalnızca 5 rakam kullanılır.

Örnek Çalıştırma:

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 

Bu sorunu çözmek için yazdığım ortaya çıkan kod şu şekilde:

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);
    }
}

Bununla birlikte, örnektekilerin aksine yanıtlarımın doğruluğu ile ilgili bir sorum var. Matematiksel olarak doğru kayan nokta numaraları değişkenlerimde saklansa da sonuçlar konsola yazdırıldığında kayan noktamın son rakamı her zaman yukarı yuvarlanır. Benim bakış açıma göre, bu, bir hesap makinesinde elde edilebilecek yuvarlama hatalarına benziyor, burada yapılan hesaplamaların sonucu, gerçek sayıların yalnızca yaklaşık temsilleridir, çünkü yalnızca sabit nokta rasyonel sayılar tam olarak makine içinde gösterilebilir.

Bu tür bir hataya örnek olarak: 2/3 = 0.666666667

printfFormatı kullanarak , konsola yazdırıldığında sayının yuvarlanmasına izin vermeden değişkenlerim için belirli bir uzunluk yakalamanın bir yolu var mı ?

Yanıtlar

7 GilbertLeBlanc Nov 06 2020 at 19:46

Printf formatını kullanarak, konsola yazdırıldığında sayının yuvarlanmasına izin vermeden değişkenlerim için belirlenmiş bir uzunluk yakalamanın bir yolu var mı?

Bir "numara" kullanabilirsin

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

Programınız şu ana kadar oldukça iyi görünüyor. İşte fark ettiğim bazı şeyler:

  • Hesaplarken ilk yinelemeniz için mantıksal bir hata var absValDiff. Her zaman absValDiff1'i başlatırsınız ve ilk yineleme için yeniden hesaplamazsınız, bu nedenle x = 5 gibi bir değer girerseniz, ilk tahmininizin (y = 1) karesi ile x'in yalnızca 1 olduğunu söyler, aslında 4 olmalıdır.

  • Yukarıdakilerin bir sonucu olarak, bu aynı zamanda 1'e eşit veya daha büyük herhangi bir tolerans istemenin doğru çalışmadığı ve programın hemen sona erdiği anlamına gelir. Örneğin 2 tolerans istemek doğru çalışmıyor.

  • Diğer okuyuculara daha faydalı olması için yorumlarınızı yazabilirsiniz. Kursunuzda size aksi söylenmedikçe, kodunuzu okuyan kişinin dilin ne yaptığını anladığını ve kodunuzun neden yaptığı adımları neden attığıyla daha çok ilgilendiğini varsayabilirsiniz. Örneğin:

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

    Burada değişkenleri tanımladığınız açıktır, böylece bunun yerine ne yaptığınızı açıklamayı deneyebilirsiniz. Neden okuyucuya değişkenlerin ne işe yaradığını anlatmayı denemiyorsunuz?

  • Değişkenlerinizi ihtiyaç duyduğunuz şekilde bildirmek, yöntemin en üstünde olmaktansa genellikle daha kolaydır. Daha fazla referans için, buradaki Bildirimler bölümüne bakın . Bu kaynakta belirtildiği gibi, eski programlama dillerinde tüm değişkenlerinizi önceden bildirmek bir gelenek olsa da, genellikle bunu yapmak en iyi uygulama olarak kabul edilmez.

Tüm bunları değiştirdiğinizde, kodunuz şuna daha çok benzeyebilir:

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);
    }
}

Yuvarlama önlemek (ve bunun yerine amacıyla kesecek numarası) şunu kullanabilirsiniz DecimalFormat açıklandığı gibi, burada Gilbert Le Blanc yöntemine alternatif bir yaklaşım istedi. Alma işleminden sonra java.text.DecimalFormatve java.math.RoundingModeaşağıdaki gibi kullanabilirsiniz:

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

hangisi çıktı 0.66.