特定の平方根アルゴリズムの時間を計るプログラムを書きました
import java.math.BigDecimal;
public class ScienceFairTwo {
public static final BigDecimal TWO = new BigDecimal(2);
public static final BigDecimal SQRT_TWO = new BigDecimal("1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727");
public static final BigDecimal TOLERANCE = BigDecimal.ONE.scaleByPowerOfTen(-100);
public static long[] NewtonMethod() {
int iterations = 0; // so far, we haven't done any iterations
BigDecimal a = BigDecimal.ONE; // set a to be one
long start = System.nanoTime(); // start the timer
while(a.subtract(SQRT_TWO).abs().compareTo(TOLERANCE) >= 0) { // while our decimal isn't close enough to the square root of two
a = a.add(TWO.divide(a, 100, BigDecimal.ROUND_HALF_UP)).divide(TWO); // set a to (a + 2/a)/2
iterations++; // add one to our iteration counter
}
return new long[] {System.nanoTime() - start, iterations}; // return the time taken and the iterations taken
}
public static long[] BhaskaraBrounckerAlgorithm() {
int iterations = 0; // so far, we haven't done any iterations
BigDecimal a = BigDecimal.ONE; // set a to be one
long start = System.nanoTime(); // start the timer
while(a.subtract(SQRT_TWO).abs().compareTo(TOLERANCE) >= 0) { // while our decimal isn't close enough to the square root of two
a = a.add(TWO).divide(a.add(BigDecimal.ONE), 100, BigDecimal.ROUND_HALF_UP); // set a to (a+2)/(a+1)
iterations++; // add one to our iteration counter
}
return new long[] {System.nanoTime() - start, iterations}; // return the time taken and the iterations taken
}
public static long[] MidpointMethod()
{
int iterations = 0; // so far, we haven't done any iterations
BigDecimal a = BigDecimal.ONE; // set a to be one
BigDecimal b = TWO; // set b to be two
long start = System.nanoTime(); // start the timer
while(a.add(b).divide(TWO).subtract(SQRT_TWO).abs().compareTo(TOLERANCE) >= 0) { // while our decimals aren't close enough to the square root of two
if(a.multiply(a).subtract(TWO).abs().compareTo(b.multiply(b).subtract(TWO).abs()) == 1) // if a is farther from the square root of two than b
a = a.add(b).divide(TWO); // set a to be the average of a and b
else // if a is closer to the square root of two than b
b = a.add(b).divide(TWO); // set b to be the average of a and b
iterations++; // add one to our iteration counter
}
return new long[] {System.nanoTime() - start, iterations}; // return the time taken and the iterations taken
}
public static long[] SecantMethod()
{
BigDecimal a = BigDecimal.ONE; // set a to be one
BigDecimal b = TWO; // set b to be two
BigDecimal b_old = TWO; // set b_old to be two (this is a transferring variable)
long start = System.nanoTime(); // start the timer
int iterations = 0; // so far, we haven't done any iterations
while(a.add(b).divide(TWO).subtract(SQRT_TWO).abs().compareTo(TOLERANCE) >= 0) { // // while our decimals aren't close enough to the square root of two
b_old = b; // set b_old to be b
b = a.multiply(b).add(TWO).divide(a.add(b), 100, BigDecimal.ROUND_HALF_UP); // set b to be (ab + 2)/(a+b)
a = b_old; // set a to be the previous value of b
iterations++; // add one to our iterations counter
}
return new long[] {System.nanoTime() - start, iterations}; // return the time taken and the iterations taken
}
public static void main(String[] args) {
System.out.printf("Newton Iteration: %f milliseconds (%d iterations taken)\n", NewtonMethod()[0] / 10e6, NewtonMethod()[1]); // print the results
System.out.printf("Midpoint Method: %f milliseconds (%d iterations taken)\n", MidpointMethod()[0] / 10e6, MidpointMethod()[1]); // print the results
System.out.printf("Secant Method: %f milliseconds (%d iterations taken)\n", SecantMethod()[0] / 10e6, SecantMethod()[1]); // print the results
System.out.printf("Bhaskara-Brouncker Algorithm: %f milliseconds (%d iterations taken)\n", BhaskaraBrounckerAlgorithm()[0] / 10e6, BhaskaraBrounckerAlgorithm()[1]); // print the results
}
}
このプログラムをサイエンス フェアのボードに載せる必要があるため、100/1000 の試行はしたくないので、ベンチマークなどを使用してプログラムを複雑にしたくありません。大きく変化します。たとえば、一度私が得た
Newton Iteration: 0.466200 ミリ秒 (8 回の繰り返し) Midpoint Method: 21.090700 ミリ秒 (330 回の繰り返し) Secant Method: 0.134500 ミリ秒 (11 回の繰り返し)
別の時間に、ニュートンの反復を取得しました: 0.550700 ミリ秒 (8 回の反復が行われました) 中間点法: 23.168400 ミリ秒 (330 回の反復が行われました) セカント法: 0.130400 ミリ秒 (11 回の反復が行われました)
今、私はニュートン反復を取得しています: 0.469500 ミリ秒 (8 回の反復が行われました) 中間点法: 22.437700 ミリ秒 (330 回の反復が行われました) セカント法: 0.189200 ミリ秒 (11 回の反復が行われました)
彼らは単にあまり近くにありません。これはなぜですか?私は毎回同じことをしていますが、実行するたびに1回しか実行していないため、Javaは最適化できない可能性があります。
では、どのように進めるのが最善でしょうか? 考えられる解決策がいくつかあります。
トライアルを1回だけ行う
- 利点: Java は最適化できません
- 短所: あまり一貫性がない (上記のように)
1000回の試行を行う
- 利点: 一貫性が高い
- 欠点: Java は最適化するため、結果が不正確になります。
1000回試行しますが、randを使用して毎回平方根を近似する値を変更します
- 利点: かなり正確、最適化なし
- 短所:ランダムのため、答えは毎回異なります
969 回試行しますが、毎回 2、3、...、1000 の平方根を計算します (1、4、..、961 を除く)。
- 利点: かなり正確、最適化なし
- 欠点:
私は最後の方に傾いています。これは良い考えですか?