前提
- 10面ダイスをX個振る
- それぞれのダイスについて、出目が1だったら2、10だったら-1、それ以外の場合はY以下なら1、Yより大きいなら0をポイントとする
- Yが1~9のそれぞれの場合において、ポイントの合計の出現率を求めたい
例えば、
X=1, Y=5の場合
ポイント | 出現率 |
---|---|
-1 | 10% |
0 | 40% |
1 | 40% |
2 | 10% |
X=3, Y=7の場合
ポイント | 出現率 |
---|---|
-3 | 0.1% |
-2 | 0.6% |
-1 | 3% |
0 | 8.3% |
1 | 19.2% |
2 | 26.4% |
3 | 29.1% |
4 | 11.4% |
5 | 1.8% |
6 | 0.1% |
となります。
Xを1~30程度、Yを1~9の範囲での全ての結果を求めたいと思っています。
試したこと
まずは高速に動作するであろうC++で愚直に実装してみました。
C++
1#include <iostream> 2#include <array> 3#include <cmath> 4 5int main() 6{ 7 // ダイスの出目によるポイントのテーブル 8 const char point_table[10][10] = { 9 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10 {2, 0, 0, 0, 0, 0, 0, 0, 0, -1}, 11 {2, 1, 0, 0, 0, 0, 0, 0, 0, -1}, 12 {2, 1, 1, 0, 0, 0, 0, 0, 0, -1}, 13 {2, 1, 1, 1, 0, 0, 0, 0, 0, -1}, 14 {2, 1, 1, 1, 1, 0, 0, 0, 0, -1}, 15 {2, 1, 1, 1, 1, 1, 0, 0, 0, -1}, 16 {2, 1, 1, 1, 1, 1, 1, 0, 0, -1}, 17 {2, 1, 1, 1, 1, 1, 1, 1, 0, -1}, 18 {2, 1, 1, 1, 1, 1, 1, 1, 1, -1}, 19 }; 20 21 const int X = 8; 22 23 // X個のダイスを振った時の全ての組み合わせにおいて、合計ポイントが出現する回数を記録する配列 24 // ポイントの合計は-X~2Xの範囲で出現するのでそれが収まる範囲 25 // point_sum[X]が合計ポイント0の出現回数で、X-1なら合計ポイント-1、X+2なら合計ポイント2の出現回数を指す 26 std::array<int, X*3+1> point_sum = {0}; 27 28 for (int Y = 1; Y <= 9; Y++) { 29 for (size_t i = 0; i < point_sum.size(); i++) { 30 point_sum[i] = 0; 31 } 32 for (int i = 0; i < 10; i++) { 33 int p1 = point_table[Y][i]; 34 for (int j = 0; j < 10; j++) { 35 int p2 = point_table[Y][j]; 36 for (int k = 0; k < 10; k++) { 37 int p3 = point_table[Y][k]; 38 for (int l = 0; l < 10; l++) { 39 int p4 = point_table[Y][l]; 40 for (int m = 0; m < 10; m++) { 41 int p5 = point_table[Y][m]; 42 for (int n = 0; n < 10; n++) { 43 int p6 = point_table[Y][n]; 44 for (int o = 0; o < 10; o++) { 45 int p7 = point_table[Y][o]; 46 for (int p = 0; p < 10; p++) { 47 int p8 = point_table[Y][p]; 48 // p1~p8はそのダイスによるポイント 49 // その合計は-X~2Xの範囲で出現するので、配列に収まるようそれにXを加算したものを配列のインデックスにしている 50 point_sum[p1+p2+p3+p4+p5+p6+p7+p8+X]++; 51 } 52 } 53 } 54 } 55 } 56 } 57 } 58 } 59 for (size_t i = 0; i < point_sum.size(); i++) { 60 std::cout << "X = " << X << " / Y = " << Y << " / " << (int)i-X << " = " << point_sum[i]/std::pow(10, X-2) << "%" << std::endl; 61 } 62 } 63 64 return 0; 65}
発生している問題
このプログラムはX=8の場合の、出現する全てのダイス目をカウントする事で確率を求めています。
この愚直な方法でも確かに確率は求められるのですが、当然ですがダイスの数が1つ増えるごとに実行時間がおよそ10倍になり、こちらの環境ではX=10の段階で計算終了まで20分ほどかかってしまいました。
また、あるXの時に計算した内容を保持しておき、X+1の計算に流用する事で計算時間を減らせないかとも検討しました。
上記のX=8の例であれば、i, j, k, l, m, n, o, pの全ての組み合わせにおいてp1+p2+p3+p4+p5+p6+p7+p8の値を記録しておき、X=9の計算の際にその値を利用する方法も試したのですが、僅かに実行時間は減らせるものの結局はXが増えるたびに指数的に実行時間が伸びるのであまり意味はなく、メモリ的な限界もあり難しいと判断しました。
実現したいこと
ダイスの数が30個程度(できれば、少なくとも20個)までの結果は求めたいと思っているのですが、この方法で求められるのはせいぜい12個程度までで、それ以上は実行時間が非現実的です。
なにか良い方法はありますでしょうか?
目的は確率を求める事なので、実行時間は現実的な範囲であれば長くなっても問題ありませんし、プログラミング以外の手段でそれが求められるのならそれでも構いません。
言語、ライブラリ等も問いません。
回答4件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/09/07 07:39
2021/09/07 08:21
2021/09/07 09:25