- 締切済み
サイコロの合計値
サイコロで各目が等確率で出るとして、合計値が10000になるまでの回数(確率)を求たいのですが、Javaのサンプルコードをいただけませんでしょうか?
- みんなの回答 (9)
- 専門家の回答
みんなの回答
- KSOH
- ベストアンサー率93% (29/31)
先の私の回答で「合計がちょうどN」になる確率はすごく小さくなると思うと書きましたが 間違いですね。失礼しました!正しくはtitokaniさんがおっしゃる通りの値に収束すると 思います。 蛇足: ちょうどNになる確率を求めるアルゴリズムとしてtitokaniさんの再帰アルゴリズムが 直感的にわかりやすいですね! N=10000のときにメモリー不足になったとのことですが、Nが大きくなると再帰呼び出し の深さもN相当になるため、メソッド呼び出しのためのスタックがオーバーフロー したのではないでしょうか。 Nにいきなりおおきな値が指定されてもスタックオーバーフローしないように 配慮するには(あまりきれいじゃないですが)titokaniさんのプログラムのfuncを 小さい値から順に何度も呼び出すという方法が考えられます。こうして funcを直接呼び出すのではなく下記のprobabilityJustMatchを呼び出すように すればfuncの再帰呼び出しの深さが2以上になることもなく、HashMapがパンク しない限り与えられた値に概ね比例する計算時間(&消費メモリー)で結果が得られます。 static double probabilityJustMatch(int n) { for (int i = 1; i < n - 1; i++) { func(i); //return値は無視。func(1)~func(n-1)までの解をmapへ登録するのが目的 } return func(n); }
- titokani
- ベストアンサー率19% (341/1726)
#7です。 ごめんなさい。1/2じゃないですね。 およそ0.2857・・くらいに収束するようです。 まともに計算するとえらい時間がかかるので、mapでキャッシュにしたのですが、10000だとメモリが足りないようです。 private static Map<Integer,Double> map=new HashMap<Integer, Double>(); public static double func(int n) { if( n < 0 )return 0; if( n == 0 )return 1; if( map.containsKey(n) ){ return map.get(n); } double r=func(n-1)/6+func(n-2)/6+func(n-3)/6+func(n-4)/6+func(n-5)/6+func(n-6)/6; map.put(n, r); return r; }
- titokani
- ベストアンサー率19% (341/1726)
ぴったり10000になる確率ですが、およそ1/2ではないでしょうか? 再帰を使えば求めれそうな気がします。 double func(int n) { if( n < 0 )return 0; if( n == 0 )return 1; return func(n-1)/6+func(n-2)/6+func(n-3)/6+func(n-4)/6+func(n-5)/6+func(n-6)/6; } とか。
- KSOH
- ベストアンサー率93% (29/31)
すみません、自分の回答のプログラムに1行抜けがあります。プログラムの先頭に以下のimport文が必要です。おわかりかも知れませんが・・・ import java.util.Arrays;
- KSOH
- ベストアンサー率93% (29/31)
文面を読む限りちょうど10000になるという条件ではなく、10000に到達するまでに何回サイコロを振らなければならないかの回数の期待値が求めたいと思えました。合計値がちょうど10000になる確率だとすると浮動小数の精度限界を超えるぐらい小さな確率になってしまいそうです。 そう仮定しますと次のようなプログラムで理論値を計算できると思います。expectedRollsメソッドの引数に到達目標の目の合計(今回なら10000)を渡すと期待値が返ってくるというものです。実際に10000での回数の期待値を計算してみると2857.619048という値でした。 なお、到達するのに必要な回数ごとに確率を求めたいなら expectedRollOfReach += roll * reachedProbability; の行の直後に以下の行を挿入すれば表示できます。 System.out.format(" probability %f for %d rolls\n", reachedProbability, roll); --以下がプログラム-- public class DiceSolver { static final int MIN_DICE = 1; static final int MAX_DICE = 6; static final double PROBABILITY_PER_ROLL = 1D / (MAX_DICE - MIN_DICE + 1); public static double expectedRolls(int totalSum) { if (totalSum < 0) throw new IllegalArgumentException("total summation should be non negative!"); double[][] probabilities = new double[2][totalSum + MAX_DICE]; int curIndex = 0; probabilities[curIndex][0] = 1D; int minSum = 0; int maxSum = 0; double expectedRollOfReach = 0D; int maxRoll = (totalSum + MIN_DICE - 1) / MIN_DICE; for (int roll = 1; roll <= maxRoll; roll++, curIndex = 1 - curIndex) { double[] cur = probabilities[curIndex]; double[] next = probabilities[1 - curIndex]; Arrays.fill(next, 0D); for (int sum = minSum; sum <= maxSum; sum++) { for (int dice = MIN_DICE; dice <= MAX_DICE; dice++) { next[sum + dice] += cur[sum] * PROBABILITY_PER_ROLL; } } minSum = minSum + MIN_DICE; maxSum = maxSum + MAX_DICE; if (maxSum >= totalSum) { double reachedProbability = 0D; for (int sum = totalSum; sum <= maxSum; sum++) { reachedProbability += next[sum]; } expectedRollOfReach += roll * reachedProbability; maxSum = totalSum - 1; } } return expectedRollOfReach; } }
- asuncion
- ベストアンサー率33% (2127/6289)
>ぴったり10000になる確率なら、0ですね。 これは極論過ぎます。 確率が0ということはその事象は「絶対に起こらない」ということですが、 実際には、何回かプログラムを実行すれば、そのうち何回かは 合計値がちょうど10000になることもあるでしょう。 何回チャレンジすればよいかまではわかりませんが。
- amanojaku1
- ベストアンサー率54% (265/488)
Javaはスピードが遅いので下記プログラムでは合計値を100とし、50回繰り返した場合の確立を計算しています。 import java.util.Random; public class test { public static void main(String[] args) { int expectation = 100; int loop = 50; int shake; int dice; int total; int match = 0; double probability; Random rd = new Random(); for(int i=0;i<loop;i++){ total = 0; shake=0; do{ shake++; dice = rd.nextInt(6)+1; total = total+dice; }while(total<expectation); if(total==expectation){ match++; System.out.print("match:"); } System.out.println("total="+total); System.out.println("shake="+shake); } System.out.println("match="+match); probability = (double)match/loop*100; System.out.println("確率="+probability+"%"); } }
- amanojaku1
- ベストアンサー率54% (265/488)
単純に考えるとサイコロを振る回数の平均値は 10000/3.5=2857.1428571428571428571428571429
- maiko0333
- ベストアンサー率19% (839/4401)
合計はぴったり10000なの? ぴったり10000になる確率なら、0ですね。