ChatGPTに円周率を計算するプログラムをお願いしてみた話

こんにちは｜こんばんは。カエルのアイコンで活動しております @kyamaz :frog: です。

:::note info
本稿で活用したChatGPTは、ChatGPT-3.5 です。
:::

## はじめに
2022年のことですが、Googleの研究者が円周率を100兆桁目まで計算[^hist-1]しました。100兆桁目の数字は「0」だそうです。この情報を ChatGPT に答えて欲しくて、試しに聞いていました。残念ながら「現実的な時間内で計算することは不可能です。検索してね！」というような回答が返ってきました。
そこで ChatGPT は、

 * **どのくらいの桁数の円周率を知っているのでしょうか。**
 * **円周率を計算するアルゴリズムをどのくらい知っているのでしょうか。**

という素朴な疑問が湧いてきました。この２点について、本稿で調べてみましょう。
次のリンクにあるように、同じような課題を調べられている記事や動画が幾つか見つかると思います。興味がある方は他の情報もご確認ください。

https://pepeprism.hatenablog.com/entry/2023/02/21/012406

https://www.youtube.com/watch?v=WthnXrBbeLk


## ChatGPT が答えた円周率は1000桁

１つめの疑問「ChatGPTはどのくらいの桁数の円周率を知っているか」については、シンプルに試せました。数えてみると1,000桁まで表示されました。

:frog:〔 _円周率を可能な限り長く表示してください_ 〕
> 〔ChatGPTの回答〕
> 円周率は、無限の桁数を持つ無理数であり、通常は有限の桁数で表現されますが、いくつかの桁を表示することは可能です。円周率を可能な限り長く表示すると、以下のようになります。
<details><summary>表示された円周率</summary>

> 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198

</details>

# ChatGPT が答えた円周率を求めるプログラム

２つめの疑問「ChatGPTは、円周率を計算するアルゴリズムをどのくらい知っているか」について、次の順番で質問して試しました。

:frog:〔 _円周率を計算で求めるプログラムが知りたい_ 〕
:frog:〔 _円周率を計算するための無限級数で収束速度が最も速いものを教えてください_ 〕
:frog:〔 _ほかに最も速く収束する級数を教えてください_ 〕・・・★

※この最後の質問(★)を繰り返すと、異なるアルゴリズムを１つずつ教えてくれます。
※最初のプロンプトで円周率を計算するプログラムを聞いていますので、次のプロンプトに対してもプログラム付きで回答してくれました。但し、説明とプログラムが対応（アルゴリズムとプログラムが対応）できていないこともありました。

幾つかのアルゴリズムが提示されたあとは、これまでに回答された中にある回答をするようになります。つまり、この状態になるまで繰り返せば、ChatGPTが知っている”円周率を計算するためのアルゴリズム”のバリエーションが想定できると考えてもよいでしょう。

参考資料[^ref-2][^ref-3][^ref-4]を調べてみると、ChatGPTの回答に間違いが含まれておりましたが、それを正しながら整理してこのあと列挙していきます。


### ライプニッツの公式

$\displaystyle \begin{equation} \frac{\pi}{4}=\frac{1}{1}-\frac{1}{3}+\frac{1}{5}-\frac{1}{7}+\frac{1}{9}-\frac{1}{11}+\cdots \end{equation}$

これは、グレゴリー・ライプニッツ級数（逆正接関数 arctan x のマクローリン展開）である次式(A)に $x=1$ を代入したときの級数です。

$\displaystyle \begin{align} \arctan{x} &= \sum^{\infty}_{n=0} \frac{(-1)^n}{2n+1} x^{2n+1} = x - \frac{1}{3}x^{3} + \frac{1}{5}x^{5} - \frac{1}{7}x^{7} + \frac{1}{9}x^{9} - \cdots \tag{A} \\\\
\mathrm{arccot}{x} &= \frac{\pi}{2}-\arctan{x} \tag{B} \end{align}$

<details><summary>提示されたプログラム「ライプニッツの公式」</summary>

```python:
import math                    # `math`ライブラリは必要ない

def gauss_arccot(precision):   # ガウスの公式ではなく、ライプニッツの公式の実装
    pi_over_2 = 0.0
    sign = 1
    for n in range(0, precision):
        pi_over_2 += sign / (2 * n + 1)
        sign = -sign
    return pi_over_2

def pi_from_gauss_arccot(precision):
    return 4 * gauss_arccot(precision)   # 出力されたプログラムは、"2*"でしたが"4*"が正しい

# 任意の桁数で円周率を計算
num_digits = int(input("計算したい円周率の桁数を入力してください: "))
precision = int(10 ** (num_digits / 2))
pi = pi_from_gauss_arccot(precision)
print("円周率:", pi)
```
</details>

### マチンの公式

$\displaystyle \begin{equation} \frac{\pi}{4}= 4 \arctan{\frac{1}{5}} - \arctan{\frac{1}{239}} \end{equation}$

[マチンの公式のWikiPedia](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%81%E3%83%B3%E3%81%AE%E5%85%AC%E5%BC%8F)によると、$\arctan{x}$の級数展開の式(A)を用いた方式は1970年代まで利用されていた方式だったようです。なお、2002年に日立のスーパーコンピューターを用いた式[^hist-2]も$\arctan{x}$の級数展開を使っていますが、ChatGPTはこの式を知らないようです。

<details><summary>提示されたプログラム「マチンの公式」</summary>

:::note warn
ChatGPTから「マチンの公式」の名称と説明はありましたが、プログラムはアルゴリズム通りの処理ではなく、対応するプログラムは提示されませんでした。
:::

</details>

### ベイリー＝ボールウェイン＝プラウフ（BBP）の公式

[BBP（Bailey–Borwein–Plouffe formula）公式](https://ja.wikipedia.org/wiki/%E3%83%99%E3%82%A4%E3%83%AA%E3%83%BC%EF%BC%9D%E3%83%9C%E3%83%BC%E3%83%AB%E3%82%A6%E3%82%A7%E3%82%A4%E3%83%B3%EF%BC%9D%E3%83%97%E3%83%A9%E3%82%A6%E3%83%95%E3%81%AE%E5%85%AC%E5%BC%8F)は次式で表されます。
$\displaystyle \pi = \sum^{\infty}_{k=0}(\frac{1}{16^{k}}(\frac{4}{8k+1}-\frac{2}{8k+4}-\frac{1}{8k+5}-\frac{1}{8k+6}))$
このアルゴリズムは、先行する桁を計算せずに$\pi$の十六進法で $n$ 桁目（二進法で $4n$ 桁目）の数を直接求めることが特徴です。

<details><summary>提示されたプログラム「BBPアルゴリズム」</summary>

```python:
import math

def bbp_algorithm(digits):
    pi = 0
    for k in range(digits):
        pi += 1 / 16 ** k * (4 / (8 * k + 1) - 2 / (8 * k + 4) - 1 / (8 * k + 5) - 1 / (8 * k + 6))
    return pi

# 求めたい円周率の桁数を入力
digits = int(input("計算したい円周率の桁数を入力してください: "))
pi = bbp_algorithm(digits)
print("円周率:", pi)
```
:frog:〔 _`math`ライブラリではなく本来は`decimal`ライブラリを使って以下のように実装するのがよいです_ 〕

```python:
from decimal import *

def bbp_algorithm(digits):
    getcontext().prec = digits + 1
    pi = Decimal(0)
    for k in range(digits):
        pi += Decimal(1) / 16 ** k * (Decimal(4) / (8 * k + 1) - Decimal(2) / (8 * k + 4) - Decimal(1) / (8 * k + 5) - Decimal(1) / (8 * k + 6))
    return pi

# 求めたい円周率の桁数を入力
digits = int(input("計算したい円周率の桁数を入力してください: "))
pi = bbp_algorithm(digits)
print("円周率:", pi)
```
</details>

### ニュートン・ラフソン（Newton-Paphson）法
参考サイト『[Newton-Raphson法を用いた円周率近似](https://rabbitfoot141.hatenablog.com/entry/2017/04/28/123137)』にあるとおり、次の漸化式を計算していきます。

$ \displaystyle L_{n+1} = \sqrt{2-\sqrt{4-(L_n)^2}}$

<details><summary>提示されたプログラム「ニュートン・ラフソン法」</summary>

:::note warn
ChatGPTから「ニュートン法」の名称と説明はありましたが、プログラムはアルゴリズム通りの処理ではなく、対応するプログラムは提示されませんでした。アルゴリズムの説明も適切ではありませんでした。
:::

</details>

### 正多角形による方法（アルキメデスの方法）

円に内接・外接する正多角形を計算する方法[^ref-6]です。アルキメデスは、正 6 角形から出発して，辺の数が 2 倍にして，正 12 角形, 正 24 角形, 正 48 角形，正 96 角形，正 192 角形 について計算しました。

https://qiita.com/pi301415/items/1dd576a1748e7be37ed7

<details><summary>提示されたプログラム「正多角形による方法」</summary>

:::note warn
`decimal`ライブラリを使ったプログラムが提示されいましたが、処理が正しく実装されていませんでした。
:::

</details>

#### ヴィエトの公式
$\displaystyle \pi = 2 \cdot \frac{2}{\sqrt{2}} \cdot \frac{2}{\sqrt{2+\sqrt{2}}} \cdot \frac{2}{\sqrt{2+\sqrt{2+\sqrt{2}}}} \cdots$
の導出は正多角形によるものです。このヴィエトの公式[^ref-5]は ChatGPT では説明されませんでしたが、近しいプログラムが提案されましたので、取り上げました。

### ラマヌジャン（Ramanujan）のアルゴリズム
$\displaystyle \frac{1}{\pi}= \frac{2\sqrt{2}}{99^{2}} \sum^{\infty}_{k=0} \frac{(4k)!(26390k+1103)}{(k!)^{4}396^{4k}}$


<details><summary>提示されたプログラム「ラマヌジャンのアルゴリズム」</summary>

```python:
import math

def ramanujan(iterations):
    sum = 0
    for k in range(iterations):
        numerator = math.factorial(4 * k) * (1103 + 26390 * k)
        denominator = (math.factorial(k) ** 4) * (396 ** (4 * k))
        sum += numerator / denominator
    return 1 / ((2 * math.sqrt(2) / 9801) * sum)

# 収束の速さに応じて適切な反復回数を設定
iterations = int(input("計算したい円周率の桁数に応じた反復回数を入力してください: "))
pi = ramanujan(iterations)
print("円周率:", pi)
```

</details>

### チュドノフスキー（Chudnovsky）の公式
$\displaystyle \frac{1}{\pi} =12\sum^{\infty}_{k=0} \frac{(-1)^{k}(6k)!(545140134k+13591409)}{(k!)^{3}(3k)!(-640320)^{3k+3/2}}$

https://qiita.com/peria/items/c02ef9fc18fb0362fb89

<details><summary>提示されたプログラム「チュドノフスキーの公式」</summary>

```python:
def chudnovsky(iterations):
    sum = 0
    for k in range(iterations):
        numerator = (-1) ** k * factorial(6 * k) * (545140134 * k + 13591409)
        denominator = factorial(3 * k) * factorial(k) ** 3 * 640320 ** (3 * k + 1.5)
        sum += numerator / denominator
    return 1 / (12 * sum)

def factorial(n):
    result = 1
    for i in range(1, n + 1):
        result *= i
    return result

# 収束の速さに応じて適切な反復回数を設定
iterations = int(input("計算したい円周率の桁数に応じた反復回数を入力してください: "))
pi = chudnovsky(iterations)
print("円周率:", pi)
```

</details>

### 算術幾何平均による方法

#### ガウス＝ルジャンドル（Gauss-Legendre）のアルゴリズム

$a_0 = 1, b_0 = \frac{1}{\sqrt{2}}$に設定して、以下の値を$n = 1, 2, 3, \dots$と順に計算します。
$\displaystyle a_{n + 1} = \frac{a_n + b_n}{2}$（$a_n$と$b_n$の相加平均）
$b_{n + 1} = \sqrt{a_n \cdot b_n}$（$a_n$と$b_n$の相乗平均）

$T = (a_0 - a_1)^2 + 2(a_1 - a_2)^2 + 4(a_2 - a_3)^2 + \cdots + 2^{n-1}(a_{n-1} - a_n)^2$

とすると、円周率$\displaystyle \pi = \frac{(a_n + b_n)^2}{1 - 4T}$と近似される。

<details><summary>提示されたプログラム「ガウス＝ルジャンドルのアルゴリズム」</summary>

```python:
import math

def gauss_legendre(precision):
    a = 1.0
    b = 1.0 / math.sqrt(2)
    t = 1.0 / 4.0
    p = 1.0
    pi_old = 0

    for _ in range(precision):
        a_next = (a + b) / 2
        b = math.sqrt(a * b)
        t -= p * (a - a_next) ** 2
        a = a_next
        p *= 2
        pi = (a + b) ** 2 / (4 * t)
        
        # 収束判定
        if abs(pi - pi_old) < 10 ** -precision:
            break
        pi_old = pi

    return pi

# 任意の桁数で円周率を計算
num_digits = int(input("計算したい円周率の桁数を入力してください: "))
precision = int(math.log10(10 ** num_digits))
pi = gauss_legendre(precision)
print("円周率:", pi)
```

</details>

#### サラミン＝ブレント（Salamin–Brent）のアルゴリズム

$a_0 = 1, b_0 = \frac{1}{\sqrt{2}}, c_0^{2} = a_0^{2} - b_0^{2} $として、
$\displaystyle a_{n + 1} = \frac{a_n + b_n}{2}, b_{n + 1} = \sqrt{a_n b_n}, c_{n + 1} = \frac{1}{2}(a_n - b_n)$ とするとき、

$\displaystyle \pi = \lim_{n \to \infty} \frac{2 a_{n+1}^{2}}{1- \displaystyle \sum^{n}_{j=0}  2^{j} c_j^{2}}$ 

<details><summary>提示されたプログラム「サラミン＝ブレントのアルゴリズム」</summary>

:::note info
ガウス＝ルジャンドルのアルゴリズムが提示されました。
:::
</details>


### ガンマ関数を計算して求める方法
$\displaystyle \Gamma(x)= \lim_{n \to \infty} \frac{n! \cdot n^{x} }{x(x+1)(x+2)\cdots(x+n)}$

を計算するアルゴリズムを用いて、$\displaystyle \Gamma(\frac{1}{2}) = \sqrt{\pi}$　を使って計算します。

<details><summary>提示されたプログラム「ガンマ関数を用いる方法」</summary>

:::note warn
$\displaystyle \Gamma(\frac{1}{4}) = \sqrt{2 \varpi \sqrt{2\pi}}$（$\varpi$は[レムニスケート周率](https://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%A0%E3%83%8B%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%88%E5%91%A8%E7%8E%87)）を用いるプログラムが提示されました。
:::

</details>

### ゼータ関数を計算して求める方法
$\displaystyle \zeta(s)= \sum^{\infty}_{n=1} \frac{1}{n^{s}}$

を計算するアルゴリズムを用いて、$\displaystyle \zeta(2) = \frac{\pi^{2}}{6}$　を使って計算します。

<details><summary>提示されたプログラム「ゼータ関数を用いる方法」</summary>

```python:
import math

def zeta_function(s, terms):
    result = 0.0
    for k in range(1, terms + 1):
        result += 1.0 / (k ** s)
    return result

def pi_from_zeta_function(terms):
    zeta_2 = zeta_function(2, terms)
    return math.sqrt(6 * zeta_2)

# 任意の桁数で円周率を計算
num_digits = int(input("計算したい円周率の桁数を入力してください: "))
terms = int(10 ** (num_digits / 2))
pi = pi_from_zeta_function(terms)
print("円周率:", pi)
```

</details>


## ChatGPTの誤り

ChatGPTが誤っていた点で顕著だった内容を整理しました。

* アルゴリズムとプログラムの相違：アルゴリズムの説明と提案されたプログラムが合っておらず、異なる手法で処理が書かれているケースがありました。
* `import math`を使う：そもそも`math.pi`の値を途中の計算で利用していたり、$\sqrt{2}$の値やルート根の計算を`math`ライブラリの関数で使った例が多く提示されました。
* 用語の誤り：次のような間違いがありました。
  - ~~ガウス＝アーコットの公式~~
    ”アーコット”は恐らくarccotのことだと推測します。arctanとarccotには関係式(B)が成り立つため、次式を提示しているものと思われます。
    $\displaystyle \begin{equation} \frac{\pi}{4}= 12 \arctan{\frac{1}{18}} + 8 \arctan{\frac{1}{57}} - 5 \arctan{\frac{1}{239}}\end{equation}$
  - ~~チューリングの式~~
    Chudnovskyのアルゴリズムの一種として説明がされましたが、チューリングは円周率の計算分野には登場しなさそうです。
  - ボールウェインの公式
    "~~ボレルの加速法~~"という名称で説明がありましたが「ボールウェイン（Borwein）」であり、その説明には「リーマンのゼータ関数の無限和の値を使用」とあり、誤っているようです。
  - Brent-McMillanのアルゴリズム
    オイラー・マスケローニ定数$\displaystyle \gamma = \lim_{n \to \infty}(\sum^{n}_{k=1}\frac{1}{k} - \ln n) $を近似計算するプログラムが提示されました。

## おわりに
ChatGPT が知っているアルゴリズムは以上になります。このほかに「モンテカルロ法」「連分数を用いる方法」等がありますが、回答には出てきませんでした。
本稿のキッカケになった100兆桁の円周率を計算したスクリプトは、Googleが[GitHubに公開](https://github.com/GoogleCloudPlatform/pi-delivery/tree/main/pi-100t)しています。また、研究者の過去の動画がためになると思いました。最後にリンクを紹介して、本稿を締めることとします。

https://www.youtube.com/watch?v=mUy4dvttjtQ


（●）（●） Happy Hacking!
/""　＿＿""＼

[^ref-1]: 円周率を確認したサイト『[円周率(PI) 100万(1,000,000)桁](https://www.tstcl.jp/ja/randd/pi.php)』
[^ref-2]: [円周率.jp](https://xn--w6q13e505b.jp/)
[^ref-3]: [円周率公式のまとめ](https://mikan-alpha.hatenablog.com/entry/2019/09/05/151223)
[^ref-4]: [円周率を計算する](https://trap.jp/post/1961/)
[^ref-5]: https://www.kurims.kyoto-u.ac.jp/~ooura/pi04.pdf
[^ref-6]:https://www.juen.ac.jp/math/nakagawa/openh20pi.pdf
[^hist-0]: https://en.wikipedia.org/wiki/Chronology_of_computation_of_%CF%80 , https://ja.wikipedia.org/wiki/%E5%86%86%E5%91%A8%E7%8E%87%E3%81%AE%E6%AD%B4%E5%8F%B2
[^hist-00]: https://igakkai.kms-igakkai.com/wp/wp-content/uploads/2004ky/ippannkyouyou30)063-075.2004.pdf
[^hist-01]: https://www.kurims.kyoto-u.ac.jp/~ooura/pi04.pdf
[^hist-1]:Googleの研究者の記事『[円周率の100兆桁目を求めて](https://blog.google/intl/ja-jp/company-news/technology/2022_06_new-digit-pi-2022/)』
[^hist-2]: [高野喜久雄の公式](https://ja.wikipedia.org/wiki/%E9%AB%98%E9%87%8E%E5%96%9C%E4%B9%85%E9%9B%84)
$\displaystyle \begin{equation} \frac{\pi}{4}= 12 \arctan{\frac{1}{49}} + 32 \arctan{\frac{1}{57}} - 5 \arctan{\frac{1}{239}} + 12 \arctan{\frac{1}{110443}} \end{equation}$