Java'da Newton yöntemi ile karekökler
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
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);
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 zamanabsValDiff1'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.