前提・実現したいこと
( 実現したいこと )
javaでandroidアプリの開発をしています。
アプリの中で「コインを5回投げた時に表が2回出る確率」のような「確率pをn回試行した時にk回当たる確率」を求めるプログラムを作りたいと考えています。
下記の前提条件を満たした上で高速に計算できるようにするにはどのようなコードを組めば良いのでしょうか。
( 作成したいアプリでの前提条件 )
- 試行回数nは最大で10,000程度になる
- 確率pは1/4〜1/65536の値をとる
- 一度に最大で50パターンの計算をしたいため1パターンあたりの処理速度は0.1秒以下にしたい
発生している問題・エラーメッセージ
調べたところ二項分布を使えば目的の計算ができることが分かりましたので以下のようなプログラムを組もうと考えいます
- nCrの組み合わせを求める
- 二項分布の計算式
P(X=k)= nCr * (p**k) * (1-p)**(n-k)
に当てははめて確率を計算する
組み合わせの計算には、nが大きいことから処理速度の観点で「パスカルの三角形」を利用した計算をしようとしましたが、そこで困っています。
nが大きい場合、いくつかのサイトでオーバーフロー対策として計算結果を素数1000000007で割った余りを取ることを推奨していましたが、1000000007で割った余りでは上記二項分布の計算式に当てはめられず上手く計算できません。
かと言って、オーバーフロー対策をしないと正しい結果が得られない状態で困っています。
該当のソースコード
こちらの記事を参考に以下のようなコードを書きました。
https://qiita.com/naru7sh/items/7f3f47fbf2e415c0ec86
米nは最大で10,000となりますが、まずは試しにURL先のサンプルコード通りに書いてみたところです。
java
1public class Pascal { 2 public static void main(String[] args) { 3 // 配列の準備 4 int c[][] = new int [2005][2005];//n=2000段作りきれるように、少し余裕を持って作成します。 5 6 //パスカルの三角形作成 7 int mod = 1000000007;// オーバーフロー対策:問題で指定されます。 8 c[0][0]=1;// 初期値として、最上部の1を与えます。 9 for(int i=0;i<2003;i++) {//2000段作りきるようにループを回します。細かい事を考えないで済むように少し余裕を持たせて回しています。 10 for(int j=0;j<2003;j++) { 11 long tmp = c[i][j]%mod;// オーバーフロー対策あり 12 //long tmp = c[i][j];// オーバーフロー対策なし 13 c[i+1][j]+=tmp; 14 c[i+1][j+1]+=tmp; 15 } 16 } 17 System.out.println(c[5][2]); 18 System.out.println(c[1000][10]); 19 } 20}
処理結果
//オーバーフロー対策ありの結果 10 ← 正しい5C2 302505679 ← 1000000007で割った余りらしいがこれをどう使えばいいのかが分からない //オーバーフロー対策なしの結果 10 ← 正しい5C2 (nが小さいと正しい答えになる) 1774896272 ← 正しくない1000C10 (nが大きいと値がおかしい。本来は263,409,560,461,970,212,832,400)
試したこと
他の方法がないか以下のことを試しましたがともに上手く行きませんでした。
- nの値が大きいために単純なforループでの計算速度が遅く使えませんでした。
- Apacheのmath3ライブラリの
CombinatoricsUtils.binomialCoefficient
を試しましたが、こちらも計算速度が遅い上にnが大きすぎるために上手く計算できませんでした。
環境
- java
- android8以上
質問
- 上記パスカルの三角形を利用したコードで
1000000007
で割った余りを利用して、二項分布を使い「確率pをn回試行した時にk回当たる確率」を求めるにはどうすれば良いでしょうか - そもそものやり方がよくない場合、別の手法(計算方法、コードの書き方、別のライブラリetc)があればそれを教えて頂きたくお願いします。
※正しく、かつ、高速であれば今試しているパスカルの三角形を利用したやり方にはこだわりません。
よろしくお願いします。
回答2件
あなたの回答
tips
プレビュー