各サイドkに確率pがあるnサイドのロードされたダイがあるとしますk 転がすときに出てくるダイのランダムなロールを効率的にシミュレートできるように、この情報を静的に(つまり、一定の確率のセット用に)保存するための適切なアルゴリズムがあるかどうか興味があります。
現在、この問題に対するO(lg n)ソリューションがあります。アイデアは、すべてのkについて最初のk辺の累積確率のテーブルを保存し、範囲[0、1)のランダムな実数を生成し、テーブルでバイナリ検索を実行して、その累積の最大インデックスを取得することです値は選択した値以下です。私はこのソリューションが好きですが、ランタイムが確率を考慮に入れていないのは奇妙に思えます。特に、片側が常に立ち上がっているか、値が均一に分布している極端な場合、単純なアプローチを使用してO(1)でロールの結果を生成することができますが、解決策は、まだ多くのステップを対数的に行います。
この問題をランタイムで何らかの形で「適応」する方法で解決する方法についての提案はありますか?
[〜#〜] edit [〜#〜]:この質問に対する答えに基づいて、私は書きましたこの問題に対する多くのアプローチを説明する記事、およびその分析。 Voseのエイリアスメソッドの実装は、Θ(n)の前処理時間と、ダイロールごとのO(1)時間を与えます。これは本当に印象的です。答えに!
固定離散確率分布を生成するためのO(1)メソッドを提供する alias method を探しています1回限りのO(n)セットアップで、長さnの配列内のエントリにアクセスできます。) chapter 3(PDF)に文書化されています。 of "Non-Uniform Random Variate Generation" by Luc Devroye。
考えは、確率pの配列を取ることですk 3つの新しいn要素配列qを生成しますk、k、およびbk。各qk は0〜1の確率であり、それぞれak およびbk 1〜nの整数です。
0と1の間の2つの乱数rとsを生成することにより、1とnの間の乱数を生成します。i= floor(r * N)+1とします。 qの場合私 <sを返します私 それ以外の場合はbを返します私。エイリアスメソッドの作業は、qの生成方法を見つけることです。k、k およびbk。
バランスの取れたバイナリ検索ツリー(または配列内のバイナリ検索)を使用して、O(log n)の複雑度を取得します。ダイの結果ごとに1つのノードがあり、キーがその結果をトリガーする間隔になります。
function get_result(node, seed):
if seed < node.interval.start:
return get_result(node.left_child, seed)
else if seed < node.interval.end:
// start <= seed < end
return node.result
else:
return get_result(node.right_child, seed)
このソリューションの良い点は、実装が非常に簡単であるにもかかわらず、依然として複雑であることです。
テーブルを粒状化することを考えています。
各ダイ値の累積値を持つテーブルを作成する代わりに、長さxNの整数配列を作成できます。ここで、xは確率の精度を高めるために理想的には大きい数値です。
累積値として(xNで正規化された)インデックスを使用してこの配列を作成し、配列内の各「スロット」に、このインデックスが出現した場合のダイスロールを格納します。
例で簡単に説明できるかもしれません:
3つのサイコロを使用:P(1) = 0.2、P(2) = 0.5、P(3) = 0.3
配列を作成します。この場合、単純な長さ、たとえば10を選択します(つまり、x = 3.33333)。
arr[0] = 1,
arr[1] = 1,
arr[2] = 2,
arr[3] = 2,
arr[4] = 2,
arr[5] = 2,
arr[6] = 2,
arr[7] = 3,
arr[8] = 3,
arr[9] = 3
次に、確率を取得するには、0〜10の数値をランダム化し、そのインデックスにアクセスするだけです。
この方法では精度が低下する場合がありますが、xを大きくすると精度は十分になります。