ロードされたサイコロのデータ構造?


130

私がnサイドのロードされたサイコロを持っているとします。サイコロを振ったときに、各サイドkがp kになる確率があります。サイコロのランダムなロールを効率的にシミュレートできるように、この情報を静的に(つまり、確率の固定セットに対して)保存するための優れたアルゴリズムがあるかどうか知りたいです。

現在、私はこの問題のO(lg n)ソリューションを持っています。すべてのkの最初のk側の累積確率のテーブルを保存し、[0、1)の範囲でランダムな実数を生成し、テーブルに対してバイナリ検索を実行して、累積的な値は選択した値以下です。私はこのソリューションが好きですが、ランタイムが確率を考慮に入れていないのは奇妙に思えます。特に、片側が常に表示される、または値が均一に分布する極値の場合、素朴なアプローチを使用してO(1)のロールの結果を生成することが可能ですが、私の解決策はまだ多くのステップをとっています。

この問題をランタイムで何らかの形で「適応」させる方法でこの問題を解決する方法についての提案はありますか?

編集:この質問への回答に基づいて、私はこの問題への多くのアプローチとその分析を説明する記事を書きまし。エイリアスメソッドのVoseの実装は、サイコロごとにΘ(n)前処理時間とO(1)時間を与えるように見えます。これは本当に印象的です。うまくいけば、これは回答に含まれる情報への便利な追加です!


2
特定のケースごとに O(1)ソリューションが存在することは合理的です。
ティム

回答:


117

固定の離散確率分布を生成するためのO(1)メソッドを提供するエイリアスメソッドを探しています一定時間で長さnの配列のエントリにアクセスできる場合)。 。あなたはそれがで文書見つけることができる第3章(PDF)「非一様ランダム変量世代」ラック・デブロアイで。

アイデアは、確率の配列p kを取り、3つの新しいn要素配列q kak、およびb kを生成することです。各q kは0と1の間の確率で、各a kとb kは1からnの間の整数です。

0と1の間の2つの乱数rとsを生成することにより、1とnの間の乱数を生成します。i= floor(r * N)+1とします。Q場合は、私 <sが、その後返すI B他のリターン私を。エイリアスメソッドでの作業は、q k、a k、およびb kを生成する方法を理解することです。


そのような有用なアルゴリズムの場合、エイリアス法は意外にもあまり知られていない。
ムム

参考までに、エイリアスメソッドapps.jcns.fz-juelich.de/ransamplを使用したランダムサンプリング用の小さなCライブラリを公開しました。
Joachim W

1
別名方法の特定の実装は、より遅い次に、ルーレットホイールのような悪い時間複雑さの方法であってもよい 所与のためのn乱数の選択された数は、アルゴリズムの実装に関与する一定の要因による生成するため。
jfs

4

バランスのとれたバイナリ検索ツリー(または配列内のバイナリ検索)を使用して、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)

このソリューションの優れた点は、実装が非常に簡単でありながら、複雑さが非常に高いことです。


上記のような手作りのバイナリツリーは実装が簡単ですが、バランスが保証されるわけではありません
yusong

正しい順序で構築すれば、バランスが取れていることを保証できます。
hugomg 2018

3

テーブルを細かくすることを考えています。

各ダイ値の累積値を含むテーブルを作成する代わりに、長さ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を大きくすると精度は十分です。


1
完全な精度を得るには、最初のステップとして配列ルックアップを実行し、複数の辺に対応する配列間隔について、そこで検索を実行します。
aaz

1

カスタム分布(離散分布とも呼ばれます)でランダムな整数を生成する方法はたくさんあります。選択は、選択する整数の数、分布の形状、分布が時間の経過とともに変化するかどうかなど、多くの要素に依存します。

カスタムの重み関数を使用して整数を選択する最も簡単な方法の1つf(x)は、拒否サンプリング法です。以下は、の最大可能値がでfあることを前提としていますmax。拒否サンプリングの時間の複雑さは平均して一定ですが、分布の形状に大きく依存し、永久に実行される最悪のケースがあります。k拒否サンプリングを使用して[1、]の整数を選択するには:

  1. i[1、k] で一様なランダム整数を選択します。
  2. 確率でf(i)/max、戻りiます。それ以外の場合は、手順1に進みます。

他のアルゴリズムの平均サンプリング時間は、分布(通常は定数または対数)にそれほど依存しませんが、多くの場合、セットアップ手順で重みを事前計算してデータ構造に格納する必要があります。それらのいくつかは、平均して使用するランダムビット数の点でも経済的です。これらのアルゴリズムの多くは2011年以降に導入されたもので、次のものが含まれます。

  • ブリングマン・ラーセンの簡潔なデータ構造(「離散分布からの簡潔なサンプリング」、2012)、
  • Yunpeng Tangのマルチレベル検索(「離散分布を変更するためのランダムサンプリング法の実証的研究」、2019)、および
  • 高速ロードダイスローラー(2020)。

その他のアルゴリズムには、エイリアスメソッド(記事で既に説明)、Knuth–Yaoアルゴリズム、MVNデータ構造などがあります。調査については、「加重選択アルゴリズムに関する注意セクションを参照してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.