2017年10月20日金曜日

プログラムでオイラー数を求めてみる


オイラー数については良く知らないんだけど、プログラムする機会があったのでまとめてみる

 オイラー数とは
オイラー数は、双曲線正割関数のテイラー展開における展開係数として定義される \[ sech(z) = \frac{1}{sinh(z)}= \frac{2}{e^{z}+e^{-z}}= \sum_{k=0}^{\infty} \frac{E_{k}}{k!}z^{k} \] この$E_{k}$がオイラー数である
特徴として、この数列は整数であり、奇数項がすべて 0、偶数項の符号が交互に切り替わる
オイラー数$E_{k}$は$k$が偶数のとき以下の漸化式を満たす
\[ E_0 = 1 \ \ ,\ \ \sum_{i=0}^{n} {}_{2n} C _{2i} E_{2i} = 0 \] ほぼWikiのコピペだけど、前知識はこんなもん。漸化式が解ればプログラムは組めるので..



 プログラムでオイラー数を求める式をつくる
上記の漸化式のままでは、プログラムでオイラー数を求めにくいのでちょっと変形
数列和の末項を取り出すと \[ \sum_{k=0}^{n} {}_{2n} C _{2k} E_{2k} = \sum_{k=0}^{n-1} {}_{2n} C _{2k} E_{2k} + {}_{2n} C _{2n} E_{2n} = \sum_{k=0}^{n-1} {}_{2n} C _{2k} E_{2k} + E_{2n} = 0 \] 移行して \[ E_{2n} = - \sum_{k=0}^{n-1} {}_{2n} C _{2k} E_{2k} \] これで、偶数のときのオイラー数を求める式ができた!


 プログラム
Wiki見てる限り、オイラー数は非常にデカい数字になる
プリミティブ型だとすぐオーバーフローしそうなので、JavaのBigIntegerクラスを使って計算してみる
計算結果は
$n$$E_n$
01
2-1
45
6-61
81385
10-50521
122702765
14-199360981
1619391512145
18-2404879675441
20370371188237525
22-69348874393137901
2415514534163557086905
26-4087072509293123892361
281252259641403629865468285
30-441543893249023104553682821
32177519391579539289436664789665
34-80723299235887898062168247453281
3641222060339517702122347079671259045
38-23489580527043108252017828576198947741
4014851150718114980017877156781405826684425
42-10364622733519612119397957304745185976310201
447947579422597592703608040510088070619519273805
46-6667537516685544977435028474773748197524107684661
486096278645568542158691685742876843153976539044435185
50-6053285248188621896314383785111649088103498225146815121
※奇数番目は0なので省略


 プログラムの考察
40項目くらいからちょっと計算に時間がかかるなぁと感じた
そりゃ再帰関数で同じオイラー数を何回も計算させてるからねw
なので、一度計算したオイラー数は再計算しないように修正してみたら、結構早くなった
実装は、ハッシュマップで、オイラー数の計算結果を保存していく感じ
mainの呼び元はE(50, null);とかにしたらよいかな
本実装のように、テーブル作るのが目的なら、メインの呼び元にハッシュマップのインスタンスを持たせるのが最適

0 件のコメント:

コメントを投稿