arctanのBigDecimal計算


前回のπの記事で、
@zacky1972　様にコメントを頂きました。


https://xn--w6q13e505b.jp/formula/arctan.html

上記の、arctan系公式　を教えてもらいました。

## ストマーの公式

44 * arctan( 1/57) + 7 * arctan( 1/239 ) - 12 * arctan( 1/682 ) + 24 * arctan( 1/12943 )


前回の記事では、ストマーの公式の一番左の項を気にしていましたが、
上記の「arctan系公式」ので、解決策が見えたので、第２項の
arctan( 1/239 )で、桁数を増やした時の性能を測ってみました。


## 計測に使ったプログラム

```Java
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) の値を、１万、２万、３万、４万と変えて計測してみました。

PC は CPUがM1 の Mac Book Air で、メモリは8GBです。
Javaは17です。

## 測定結果

| 桁数 | iの表示値 | 秒数 |
|:-----------|------------:|:------------:|
| １万桁       | 2,100        |    3.7        |
| ２万桁     | 4,203      |   18.6     |
| ３万桁      | 6,305        |  47.9         |
| ４万桁      | 8,407          | 107.1           |


「iの表示値」というのは、プログラム中の(3)の表示で、arctanの計算を何項まで計算したか（ループ回数）です。


## メモリ使用量

VisualVMというツールで、表示してみました。
（このツールは、最近使い始めたので、使い方がよくわかっていません）

![Arctan.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1339875/f0f3bd14-0764-71b8-76fe-40eff298512f.jpeg)

右上のグラフを見ると、ガーベージコレクションを何回もしているように、思えます。

プログラム中に Thread.sleepと書いているのは、この VisualVMというツールを使うためです。
（プログラムの本質には関係ありません）



## わかった事

BigDecimalの割り算は結構時間がかかる。（かなり遅い）


```Java
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の中にも１箇所、divideを使っていますので、
ここをうまく書き換えると、もっとスピードが上がると思われます。

```Java
    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のプログラムを発展させて、 πの桁数を増やすのは、難しそう、との印象を持ちました）

以上です。