前回のπの記事で、
@zacky1972 様にコメントを頂きました。
上記の、arctan系公式 を教えてもらいました。
ストマーの公式
44 * arctan( 1/57) + 7 * arctan( 1/239 ) - 12 * arctan( 1/682 ) + 24 * arctan( 1/12943 )
前回の記事では、ストマーの公式の一番左の項を気にしていましたが、
上記の「arctan系公式」ので、解決策が見えたので、第2項の
arctan( 1/239 )で、桁数を増やした時の性能を測ってみました。
計測に使ったプログラム
package calcpi;
import java.math.BigDecimal;
import java.math.MathContext;
public class Arctan1 {
static MathContext mi = new MathContext(40000); // (1)
static BigDecimal limit;
/*
* arctan の求め方
* https://math-fun.net/20211121/20366/#google_vignette
*/
static BigDecimal arctan(BigDecimal arg) {
BigDecimal accum = arg;
BigDecimal bunshi = arg;
BigDecimal square = arg.multiply(arg, mi);
BigDecimal bunbo = new BigDecimal("3");
BigDecimal two = new BigDecimal("2");
boolean minus = true;
int i = 0;
for (;; i++) {
bunshi = bunshi.multiply(square, mi);
BigDecimal kou = bunshi.divide(bunbo, mi);
if (kou.compareTo(limit) < 0) {
break;
}
if (minus) {
accum = accum.subtract(kou, mi);
} else {
accum = accum.add(kou, mi);
}
bunbo = bunbo.add(two, mi);
minus = !minus;
}
System.out.println("i = " + i ); // (3)
return accum;
}
public static void main(String[] args) throws InterruptedException {
BigDecimal stop = new BigDecimal("1");
for (int i = 0; i < 40000; i++) { // (2)
// stop = stop.divide(new BigDecimal("10"), mi);
stop = stop.multiply(new BigDecimal("0.1"), mi);
}
limit = stop;
System.out.println("----- sleep -----");
Thread.sleep(9000);
System.out.println("----- start -----");
BigDecimal one = new BigDecimal("1");
BigDecimal d239 = one.divide(new BigDecimal("239"), mi);
long start = System.currentTimeMillis();
BigDecimal atan239 = arctan(d239);
long end = System.currentTimeMillis();
System.out.println("time in millis =" + (end - start));
Thread.sleep(3000);
System.out.println("----- end -----");
}
}
(1) と (2) の値を、1万、2万、3万、4万と変えて計測してみました。
PC は CPUがM1 の Mac Book Air で、メモリは8GBです。
Javaは17です。
測定結果
桁数 | iの表示値 | 秒数 |
---|---|---|
1万桁 | 2,100 | 3.7 |
2万桁 | 4,203 | 18.6 |
3万桁 | 6,305 | 47.9 |
4万桁 | 8,407 | 107.1 |
「iの表示値」というのは、プログラム中の(3)の表示で、arctanの計算を何項まで計算したか(ループ回数)です。
メモリ使用量
VisualVMというツールで、表示してみました。
(このツールは、最近使い始めたので、使い方がよくわかっていません)
右上のグラフを見ると、ガーベージコレクションを何回もしているように、思えます。
プログラム中に Thread.sleepと書いているのは、この VisualVMというツールを使うためです。
(プログラムの本質には関係ありません)
わかった事
BigDecimalの割り算は結構時間がかかる。(かなり遅い)
public static void main(String[] args) throws InterruptedException {
BigDecimal stop = new BigDecimal("1");
for (int i = 0; i < 40000; i++) {
// stop = stop.divide(new BigDecimal("10"), mi); // (4)
stop = stop.multiply(new BigDecimal("0.1"), mi); //(5)
}
limit = stop;
前回のπの記事では、(4)のようにしていました。
しかし10で割っていく(divide)のは、めちゃくちゃ時間が、かかります。
仕方ないので、(5)のように、0.1を掛け算する事にしました。
これから類推すると、arctanの中にも1箇所、divideを使っていますので、
ここをうまく書き換えると、もっとスピードが上がると思われます。
static BigDecimal arctan(BigDecimal arg) {
BigDecimal accum = arg;
BigDecimal bunshi = arg;
BigDecimal square = arg.multiply(arg, mi);
BigDecimal bunbo = new BigDecimal("3");
BigDecimal two = new BigDecimal("2");
boolean minus = true;
int i = 0;
for (;; i++) {
bunshi = bunshi.multiply(square, mi);
BigDecimal kou = bunshi.divide(bunbo, mi);//(6)
if (kou.compareTo(limit) < 0) {
break;
}
if (minus) {
accum = accum.subtract(kou, mi);
} else {
accum = accum.add(kou, mi);
}
bunbo = bunbo.add(two, mi);
minus = !minus;
}
System.out.println("i = " + i );
return accum;
}
上記の(6)の行の divide命令です。
( このJavaのプログラムを発展させて、 πの桁数を増やすのは、難しそう、との印象を持ちました)
以上です。