円周率の求め方の公式は多数存在するが、ここでは以下の綺麗な連分数の公式を利用する。
$$
\frac{4}{\pi} = 1 + \dfrac{1^2}
{3 + \dfrac{2^2}
{5 + \dfrac{3^2}
{7 + \dfrac{4^2}
{9 + \dfrac{5^2}
{11 + \dfrac{6^2} {\ddots} }}}}}
$$
この書き方は美しくて良いのだが、どんどん縦にも横にも伸びてきて、書くのが困難である。
それで、以下のように書くことがある。
$$
\frac{4}{\pi} = 1 +
\dfrac{1^2}{3+}\:
\dfrac{2^2}{5+}\:
\dfrac{3^2}{7+}\:
\dfrac{4^2}{9+}\:
\dfrac{5^2}{11+}\:
$$
分子が$n^{2}$になっているが、これが全部1の場合の連分数を、正則連分数という。
ネット上の連分数の解説のほとんどは正則連分数であるが、ここではより一般的な、そして美しい連分数の話をする。
連分数は再帰的になっているから、再帰呼び出しすればできるが、それはエレガントでない。
せっかくJuliaを使うのだから、畳み込み関数を使おう。
1, 1/2, 1/3, 1/4, 1/5 の5項の間に – を入れて計算してみる。
julia> 1 - 1/2 - 1/3 - 1/4 - 1/5
-0.2833333333333333
これは、次のように、先頭から順番に () で括って計算するのと同じである。
julia> (((1 - 1/2) - 1/3) - 1/4) - 1/5
-0.2833333333333333
このように、先頭(左)から順に演算する関数がfoldlである。
julia> foldl(-,map(x->1/x,1:5))
-0.2833333333333333
今度は結合の順番を、右側から順に行ってみよう。
julia> 1 - (1/2 - (1/3 - (1/4 - 1/5)))
0.7833333333333333
これを、右結合というが、演算子が – という非対称な演算子の場合、右結合するか、左結合するかで結果が異なる。
これを、Juliaの右畳み込み関数 foldr を使うと、次のようになる。
julia> foldr(-,map(x->1/x,1:5))
0.7833333333333333
ということで、最初の円周率の連分数を、右畳み込み関数で、次のようにして円周率を求めることができる。
foldrの第1引数は、2項演算子または引数が2の関数を与える。
すると、次のようになる。
julia> 4/foldr((n, x) -> 2n-1 + n*n/x, 1:20)
3.1415926535897793
でも、これで終わってはつまらない。
連分数は分数なんだから、分数のままで求めてみよう。
Juliaは、浮動小数点数だけではなく、有理数(分数)にも対応している。
julia> 1//2 + 1//3
5//6
除算演算子(/)の場合には、結果が浮動小数点数になってしまうが、有理数除算演算子(//)の場合には、分数表示のままである。もちろん、約分できる場合には、約分してくれる。
では、/を//に変えて、円周率を求めてみよう。
julia> 4/foldr((n, x) -> 2n-1 + n*n//x, 1:20)
6759794961465344//2151709564809805
次に、項数を2から20まで増やしながら、円周率の近似値がどうなるか、見てみよう。
julia> for k in 2:20
my_pi = 4/foldr((n, x) -> 2n-1 + n*n//x, 1:k)
@printf( "%-20.15g\t%s\n", my_pi,my_pi)
end
2.66666666666667 8//3
3.25 13//4
3.12121212121212 103//33
3.14529914529915 368//117
3.14093376764387 14464//4605
3.14170854271357 3126//995
3.14157240608854 112484//35805
3.14159617607035 2297088//731185
3.14159204257068 52481536//16705395
3.1415927593578 345371//109935
3.14159263530947 1836284096//584507385
3.14159265674555 18431540224//5866941465
3.14159265304551 22207688704//7068926865
3.1415926536836 34020159616//10828953135
3.14159265357363 1864534365952//593499721815
3.14159265359258 24727311941632//7870947849765
3.14159265358931 240822550134784//76656198523905
3.14159265358988 2672379400192//850644782715
3.14159265358978 6759794961465344//2151709564809805
もっと精度を上げるために、110項までの計算をしてみよう。
julia> 4/foldr((n, x) -> 2n-1 + n^2//x, 1:110)
ERROR: OverflowError: 10201 * 21317595526195387 overflowed for type Int64
Stacktrace:
・・・以下省略
分子や分母が64ビット整数の範囲を超えてしまうとオーバーフローが発生しダメなようだ。
しかし、次のようにbig()を使うと計算できる。
julia> 4/foldr((n, x) -> 2n-1 + n^2//big(x), 1:110)
127429759805932607441365489711200209651957424850172829134567731871622154823776662448269808802257401020416
//40562152340254191526972571181283917950850886703397826665197980233035381516064301728895113563704773029205
でも、これでは分かりづらいので浮動小数点表示にしてみた。
julia> my_pi = 4.0/foldr((n, x) -> 2n-1 + n^2//big(x), 1:110)
3.141592653589793238462643383279502884197169399375105820974944592307816406286198
先頭の4(整数)を4.0(浮動小数点数)に変えただけである。
円周率は、もともと定数πとして用意されているので、それと比べてみよう。
julia> my_pi - π
0.0
完全に一致している、と喜んでよいのだろうか。
これは、現在の BigFloat型の精度の範囲で一致しているというだけである。
ちなみに、現在の精度は次で求まる。
julia> precision(BigFloat)
256
256は、256ビット浮動小数点数ということを意味していることに注意しよう。
超高精度演算をしたい場合には、BigFloatで精度指定をすることで、何万桁もの浮動小数点同士の演算もできるようだが、これ以上立ち入らないことにする。