ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

Javaの課題丸投げコミュの(超入門レベルです!)自然対数eの近似値を求めるプログラム

  • mixiチェック
  • このエントリーをはてなブックマークに追加
はじめまして、PJと申します。今月からJavaの勉強を始めました。
他の言語の経験もなく、全くの初心者です。
methodやclass以前の超入門レベルの問題で申し訳ないのですが、
お時間ある方、アドバイスいただけますでしょうか?
自然対数eの近似値を計算するプログラムについてです。

問題:
Write a program that displays the approximate values of e for
i = 10000, 20000, ..., and 100000 using the following formula.

e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/i!


以下、私の書いてみたコードです。

public class ApproximationOfE
{
public static void main(String[] args)
{
System.out.println("e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/i!");
double e = 1;
double fraction = 1;
long denominator = 1;

for (int i = 1; i <= 100000; i++)
{
denominator *= i;
fraction = 1.0 / denominator;
e += fraction;

if (i % 10000 == 0)
System.out.println("For i = " + i + ", e = " + e);
}
}
}


求めているアウトプットは
e = 2.7182818284590452353603 に近い数値 (×10行) なのですが、
実際のアウトプットは e = Infinity (×10行)となってしまいます。
正しいコード(またはヒント)を下さると助かります。
よろしくお願いします。

コメント(10)

調べてみると 70! を計算した時点で, denominator が 0 になってます. そうなるとそれ以降は denominator はずっと 0 なので Infinity になってるのでしょう.

オイラー数を計算するときは Taylor 展開の後ろの方からやると誤差が少ないです.


package eulernumber;

public class EulerNumberCalc {
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
System.out.println("e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/" + (i * 10000) + "!");
System.out.println("e = " + calc(i * 10000));
}
}

public static double calc(int numberOfTerms) {
double e = 1;

for (int i = numberOfTerms; i > 0; i--) {
e = (e / i) + 1;
}
return e;
}
}

とやってみましたが, 以下のように 10000 項の計算でもかなり高い精度が出て, 100000 項までやっても変化ありませんでした.


e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/10000!
e = 2.718281828459045
e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/20000!
e = 2.718281828459045
e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/30000!
e = 2.718281828459045
e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/40000!
e = 2.718281828459045
e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/50000!
e = 2.718281828459045
e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/60000!
e = 2.718281828459045
e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/70000!
e = 2.718281828459045
e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/80000!
e = 2.718281828459045
e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/90000!
e = 2.718281828459045
e = 1 + 1/1! + 1/2! + 1/3! + ... + 1/100000!
e = 2.718281828459045

# さらなる精度は誰かが BigDecimal などでやってくれるのを待ちます...
genki さま、 杵搗き餅 さま

質問者です。貴重なアドバイスありがとうございます!

お二人のご指摘を受けて、denominatorの数を追ってみましたら、i = 20までは順調で,
denominator = 2432902008176640000(longの範囲内)なのに対し、
それ以降は数値が減ったり、突然マイナスが登場してきたりして、
i = 66に達した時点でdenominator = 0になってしまいました。

おっしゃるとおり、overflowを起こしていたようですね。
longでも足りないなんて、なんだかこの問題自体に問題がある気がしてきました。。
教科書のloopの章の章末問題なのですが、i= 10 ~ 100 の間違いでは、と思ってしまいます。

杵搗き餅 さん、解決策まで考えて下さってありがとうございます!
恥ずかしながら、どうして後ろから計算するとoverflawにならないのか
まだ理解できていないのですが、
教えていただいたコードを自分なりに良く勉強させていただきます。
後半部分は後ろから計算していくためのmethodですよね。
(すみません、教科書のloopをやっと読み終え、methodの章に入ったばかりで、
理解するのに必死ですが、、がんばります(苦笑))

本当にお時間割いてくださってありがとうございました!!
>3: PJ さん

説明無しに書いてしまいましたが, 数式にするとこんな計算を行っています.

Σ[k=0→10] 1/k! = (...(((1 / 10 + 1) / 9 + 1) / 8 + 1)...) / 2 + 1.

ご自身で検算してみてください.
>3: PJ さん

落ち着いてよく考えてみたら,

e - Σ[k=1→10000] 1/k!
= 1/10001! + 1/10002! + ...
= 1/10001!(1 + 1/10002 + 1/(10002 * 10003) + ...)
< e/10001!
〜 (10001 / e)^10001

となってだいたい小数点以下8000桁以上計算しないと e との差は見えてきませんね. きっと i = 10 〜 100 くらいなんでしょうね.

method の勉強頑張ってください.
もう解決してるみたいですが・・・
オーバーフローにより、denominator が 0になりますね。

オーバーフローが起こった時のCPUの動作にもよりますが、
仮に、上位ビットを単純に切り捨てて、下位ビットだけが残るとすると、
おそらく i = 86 の時点で、denominator が 0 になってるかと。(long が 64ビットだとして)
86 ! が、2の64乗で割り切れてしまうので。
>6: EUR/JPY さん
> i = 86 の時点で

最小を取るとしたら i = 66 では?
> 7
しまった、暗算ミスった(笑)
訂正どうもです。
テイラー展開をさらに有理数の計算に変換すると以下のような数列で表せます。

a(1)=2, b(1)=1
a(i+1) = a(i)*i+1
b(i+1) = b(i)*i
e(i)=a(i)/b(i)

BigDecimalでこれを計算してやれば、
理論上、どんな制度でも計算できるはずです。
アドバイスくださった皆様

たびたびありがとうございます!
入門者の私の質問にも親切に対応して下さって本当に感動です。
とてもいいコミュですね。


杵搗き餅 さま

丁寧な解説、ありがとうごさいます。
数学をみっちり復習する必要を感じました(汗)
メソッドも使いこなせるようにがんばります。
教えていただいたコードがとても参考になります!
本当にありがとうございました。


EUR/JPY さま

オーバーフローが起こったときにどうなるのか
(いずれ値が0になるにしても、どうしてすぐにはならないのか)
疑問だったのですが、上位部分が切り捨てになったりするのですね。
e = Infinityが出たときは焦りましたが、
おかがさまで謎が解けてきてすっきりです。
アドバイスありがとうございました。


のりねこ さま

すごいですね!
私は問題の出し方が悪いということにしてしまいましたが、
ちゃんと解決する方法があるのですね。
BigDecimal ...ググッてみました。
正直、今の段階ではほとんど理解できていないのですが、
このような状況のときに便利なクラスということをしっかり覚えておきますね。
本当にありがとうございました!

ログインすると、みんなのコメントがもっと見れるよ

mixiユーザー
ログインしてコメントしよう!

Javaの課題丸投げ 更新情報

Javaの課題丸投げのメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。

人気コミュニティランキング