コインを使用して均一に分布した乱数を生成する


25

コインが1つあります。何度でも反転できます。

ような乱数を生成します。ここで、です。rar<br,a,bZ+

数字の分布は均一でなければなりません。

あれば簡単です:ba=2n

r = a + binary2dec(flip n times write 0 for heads and 1 for tails) 

どのような場合?ba2n


Han-Hoshiアルゴリズムを使用する-基本的に間隔を2つに分割し、ランダムビット(コインフリップ)を使用して2つの間隔のいずれかをランダムに選択し、ビットがなくなるまで選択した側でこのプロセスを繰り返します。これにより、実回線のパーティションから均一に分散された間隔が得られます。フリップが多いほど、間隔はより正確になります。
ゼンナ

回答:


13

探しているのは、リジェクションサンプリングまたはaccept-rejectメソッドに基づいています(Wikiページは少し技術的であることに注意してください)。

このメソッドは、このような状況で役立ちます。セットからランダムなオブジェクト(場合によってはセットランダムな整数)を選択したいのですが、その方法はわかりませんが、ことができる(あなたの場合の第1のセットを含むより大きなセットからいくつかのランダムなオブジェクトを選択し、[ 2 K + A ]一部のKように2 K + B ;この対応するk個のコインが反転)。[a,b][a,2k+a]k2k+abk

このようなシナリオでは、小さいセットの要素をランダムに選択するまで、大きいセットから要素を選択し続けるだけです。小さいセットが大きいセットに比べて十分に大きい場合(あなたの場合、[ a b ]の最大2倍の整数が含まれます)、これは効率的です。[a,2k+a][a,b]

別の例:半径1の円の中のランダムな点を選択したいとします。今、このための直接的な方法を考えるのは本当に簡単ではありません。accept-rejectメソッドを使用します。円を囲む1x1の正方形でポイントをサンプリングし、描画する数値が円の内側にあるかどうかをテストします。


3
Bの分布を取得するためにからサンプルを拒否する場合、予想される反復回数は| A |AB(幾何分布を使用して実験を行うため)。|A||B|
ラファエル

範囲が2の累乗でない限り、これを正確に実行できないことをどこかで見たことを思い出します(理由として、たとえば、1/3には終了するバイナリ展開がない)。
フォンブランド

7

(専門的:答えは数の選択をフィットaバツ<b

あなたが望むようあなたが何回として、あなたのコインを投げるために許可されているので、近い-AS-あなた-願いとして均一に分数選ぶことによって、あなたの確率を得ることができあなたが反転:(バイナリ基数を使用して点後の各桁のコイン)と乗算RによってBA 0及び[BA-1]との間の数(整数に切り捨て)を取得します。この番号をaに追加すれば完了です。r[01]rbaa

と言います。バイナリの1/3は0.0101010101 ....そして、フリップが0〜0.010101 ...の場合、ピックはbです。それはbeween 0.010101 ...と0.10101010であれば...あなたのピックになります+ 1、及びそれ以外の場合は次のようになります+ 2ba=3ba+1a+2

コインを回フリップすると、abの間の各数字が確率1で選択されます1tab1ba±2t+1


1
これは均一な分布を与えません。一部のアプリケーション(例:暗号など)では、これは非常に悪い場合があります。
ジル 'SO-悪であるのをやめる

3
@Gilles:結果が変更できなくなるまで反転させることにより、完全に均一な分布を与えるように修正できます。これが最も効率的な答えです。
ニールG

@NeilG修正できることはわかっていますが、修正することが答えの重要な部分です。
ジル 'SO-悪であるのをやめる

2
@Gilles:そのとおりです。彼はあなたが一方で反転する場合は、完全に均一な分布を作り出すことができると言うことで答えを修正することができ。最高の平均ケースと最悪のケース時間については私から+1。baf+2t1baf2t1
ニールG

@NeilG、「固定」することはできません。終端の小数部分を持たないかなり大きな整数のセットがあるためです。
フォンブランド

7

2範囲の次に大きい力、およびより大きい廃棄回答に数選択ba

n = b-a;
N = round_to_next_larger_power_of_2(n)
while (1) {
  x = random(0 included to N excluded);
  if (x < n) break;
}
r = a + x;

4
そして、なぜこれが機能するのですか?
ラファエル

@Raphaelは懐疑的ですか、それともポスターにもっと詳しく説明してほしいですか?
-Suresh

1
@シュレシュ:後者。擬似コードは少し洗練される可能性がありますが、他の回答者が説明する内容を実装します。正当化がなければ、この答えはそれだけでは価値がありません。
ラファエル

6

DDkDdDdA/2kAA/2k1/|D|

nDnログ2|D|HnH


3

baが2のべき乗でない場合、結果を得るために多くのコインを裏返す必要があります。結果が得られないこともありますが、極端な場合はほとんどありません。

方法

最も単純な方法は、[a、b)に着地するまで、[a、a + 2 ^ n)の数を生成することです(2 ^ n> = ba)。この方法で多くのエントロピーを捨てます。

より高価な方法では、すべてのエントロピーを維持することができますが、コインフリップ/サイコロの数が増えると計算量が非常に高くなります。直観的には、コインフリップを小数点の右側の2進数の数字として扱い、その数値を2から2からabに変換し、「スタック」したときにその数字の数字を返すようなものです。

次のコードは、フェアnサイドダイスのロールをフェアmサイドダイスのロール(あなたの場合はn = 2、m = ab)に変換します。ロールの数が増えると、限界コストが増加します。任意の精度のRational数値型の必要性に注意してください。良い特性の1つは、数字がスタックしなければならないために数回ロールすることで遅延する可能性がありますが、nサイドからmサイドに変換し、nサイドに戻すことです。

public static IEnumerable<BigInteger> DigitConversion(this IEnumerable<BigInteger> inputStream, BigInteger modIn, BigInteger modOut) {
    //note: values are implicitly scaled so the first unfixed digit of the output ranges from 0 to 1
    Rational b = 0; //offset of the chosen range
    Rational d = 1; //size of the chosen range
    foreach (var r in inputStream) {
        //narrow the chosen range towards the real value represented by the input
        d /= modIn;
        b += d * r;
        //check for output digits that have become fixed
        while (true) {
            var i1 = (b * modOut).Floor();
            var i2 = ((b + d) * modOut).Floor(); //note: ideally b+d-epsilon, but another iteration makes that correction unnecessary
            if (i1 != i2) break; //digit became fixed?
            //fix the next output digit (rescale the range to make next digit range from 0 to 1)
            d *= modOut;
            b *= modOut;
            b -= i1;
            yield return i1;
        }
    }
}

0

2

2進数の10進数を生成します。明示的に保存する代わりに、可能な最小値と最大値を追跡します。それらの値が同じ整数内に収まったら、その整数を返します。コードのスケッチは次のとおりです。

(編集)完全な説明:1/3の確率でそれぞれ1から3までのランダムな整数を生成するとします。これを行うには、範囲(0、1)のランダムな2進10進実数xを生成します。x <1/3の場合、1を返します。x<2/3の場合、2を返します。3を返します。xの数字を明示的に生成する代わりに、xの最小値と最大値を追跡します。最初は、xの最小値は0で、最大は1です。最初に頭を反転させると、小数点の後ろのxの最初の桁(バイナリ)は1です。xの最小値(バイナリ)は0.100000になります。 = 1/2で、最大値は0.111111111 = 1です。次のフリップがテールの場合、xは0.10から始まります。可能な最小値は0.1000000 = 1/2で、最大値は0.1011111 = 3/4です。xの可能な最小値は1/2なので、そこにあることがわかります s x <1/3が必要なため、1を返す可能性はありません。xが1/2 <x <2/3の場合は2を返し、2/3 <x <3/4の場合は3を返します。次に、3番目のフリップがテールであるとします。次に、xは0.100で始まる必要があります。最小= 0.10000000 = 1/2および最大= 0.100111111 = 5/8。1/3 <1/2 <5/8 <2/3であるため、xが間隔(1 / 3、2 / 3)に収まらなければならないことがわかっているため、xの数字の生成を停止し、2を返すだけです。

コードは、0と1の間でxを生成する代わりに、aとbの間でxを生成することを除いて、本質的にこれを行いますが、原理は同じです。

def gen(a, b):
  min_possible = a
  max_possible = b

  while True:
    floor_min_possible = floor(min_possible)
    floor_max_possible = floor(max_possible)
    if max_possible.is_integer():
      floor_max_possible -= 1

    if floor_max_possible == floor_min_possible:
      return floor_max_possible

    mid = (min_possible + max_possible)/2
    if coin_flip():
      min_possible = mid
    else:
      max_possible = mid

注:このコードを受け入れ/拒否方法に対してテストしたところ、両方とも均一な分布が得られました。このコードは、b-aが次の2のべき乗に近い場合を除いて、拒否を受け入れるよりもコインフリップが少なくて済みます。このコードで使用できるコインフリップは、受け入れ/拒否よりも平均で2つまでしか使用できないことを証明できました。私の読書から、Knuth and Yao(1976)がこの問題を解決する方法を提供し、それらの方法が予想されるコインフリップの回数で最適であることを証明したようです。さらに、予想されるフリップの数は、分布のシャノンエントロピーよりも大きくなければならないことを証明しました。しかし、私は論文のテキストのコピーを見つけることができず、彼らの方法が何であるかを知りたいと思うでしょう。(更新:1976年にKnuth Yaoの博覧会を見つけました:http://www.nrbook.com/devroye/Devroye_files/chapter_fifteen_1.pdfです が、まだ読んでいません。誰かがこのスレッドでHan Hoshiに言及しましたが、これはより一般的であるようで、偏ったコインを使用して解決します。文献のよい議論については、Pae(2009)によるhttp://paper.ijcsns.org/07_book/200909/20090930.pdfも参照してください。



1

これは、b-aが2 ^ kに等しくない場合の提案された解決策です。一定のステップ数で動作することになっています(予想範囲外の候補者を捨てる必要はありません)。

ただし、これが正しいかどうかはわかりません。この乱数ジェネレーター(存在する場合)の正確な不均一性と、それを測定/定量化する方法を批判し、説明してください。

まず、範囲[0、z-1]で一様に分布した乱数を生成する同等の問題に変換します。ここで、z = b-aです。

また、m = 2 ^ kを2> = zの最小乗とします。

上記の解決策に従って、範囲[0、m-1]に一様に分布した乱数ジェネレーターR(m)が既にあります(各ビットに1つずつkコインを投げることで実行できます)。

    Keep a random seed s and initialize with s = R(m).   

    function random [0, z-1] :
        x = R(m) + s 
        while x >= z:
            x -= z
        s = x
        return x

whileループは最大3回実行され、次の乱数を一定のステップ数で与えます(ベストケース=ワーストケース)。

数字[0,2]のテストプログラムはこちらをご覧ください:http : //pastebin.com/zuDD2V6H


z=3m=41/21/41/4

擬似コードとリンクされたコードをより詳しく見てください。ほぼ同じ頻度で0、1、および2を放出します
...-vpathak

01/21/4

関数全体を1行で置き換えることができます。returns =(s + R(m))%z;
ユヴァルフィルマス

1

理論的に最適なアルゴリズム

ここに私が投稿した他の回答の改善点があります。もう1つの答えには、1つの離散分布を別の分布から生成するより一般的な場合に簡単に拡張できるという利点があります。実際、もう1つの答えは、HanとHoshiによるアルゴリズムの特殊なケースです。

ここで説明するアルゴリズムは、Knuth and Yao(1976)に基づいています。彼らの論文では、彼らはまた、このアルゴリズムがコインフリップの可能な最小数を達成することを証明しました。

それを説明するために、他の回答で説明されている拒否サンプリング方法を検討してください。例として、5つの数値のいずれかを一様に生成するとします[0、4]。次の2の累乗は8なので、コインを3回フリップし、最大8個の乱数を生成します。数字が0〜4の場合、それを返します。それ以外の場合は、それを破棄して最大8個の別の番号を生成し、成功するまで再試行します。しかし、数字を捨てると、エントロピーが無駄になります。代わりに、投げた数字を条件にして、将来必要になるコインフリップの数を減らすことができます。具体的には、数値[0、7]を生成したら、それが[0、4]であれば戻ります。それ以外の場合、5、6、または7であり、それぞれの場合に異なることを行います。5の場合、コインを再度フリップし、フリップに基づいて0または1を返します。6の場合 コインを裏返し、2または3を返します。7の場合、コインを裏返します。頭の場合は4を返し、尾の場合は最初からやり直します。

最初に失敗した試行からの残りのエントロピーにより、3つのケース(5、6、または7)が得られました。これを捨てるだけなら、log2(3)コインフリップを捨てます。代わりにそれを保持し、別のフリップの結果と組み合わせて、6つの可能なケース(5H、5T、6H、6T、7H、7T)を生成します。 。

コードは次のとおりです。

# returns an int from [0, b)
def __gen(b):
  rand_num = 0
  num_choices = 1

  while True:
    num_choices *= 2
    rand_num *= 2
    if coin.flip():
      rand_num += 1

    if num_choices >= b:
      if rand_num < b:
        return rand_num
      num_choices -= b
      rand_num -= b

# returns an int from [a, b)
def gen(a, b):
  return a + __gen(b - a)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.