特定の乱数ジェネレータを使用して1〜100を印刷する最も効率的なアルゴリズム


11

RandNum501〜50の範囲で一様にランダムな整数を生成する乱数ジェネレーターが提供されます。この乱数ジェネレータのみを使用して、1から100までのすべての整数をランダムな順序で生成および出力できます。すべての数は正確に1度来る必要があり、任意の場所で任意の数が発生する確率は等しくなければなりません。

これのための最も効率的なアルゴリズムは何ですか?


1
配列/ビットベクトルを使用して、すでに表示されている数値を記録し、カウンターを使用して、表示されている一意の数値の数を記録します。
デイブクラーク

@DaveClarkeどうすれば50より大きい数を生成できますか?それを複数回使用する場合、それらを使用して1をどのように生成しますか?
Raj Wadhwa

1
もちろん、課題は、すべての場所が同等の可能性で発生するようにすることです。使用できます RandNum100 = (RandNum50() * 2) - (RandNum50 > 25) ? 0 : 1)
デイブクラーク

2
@DaveClarke:では、反復拒否サンプリングを提案しますか?それは予期してのみ終了します。
ラファエル

私はただヒントを与えていました。
Dave Clarke

回答:


3

フィッシャーイェイツのシャッフルを使用するこのソリューションは間違っている可能性があるので:-)考えました。すべての反復で適切な近似(以下の編集セクションを参照)で均一な分布を維持するために、このトリックを使用して0k 1の間の値を生成できます。O(N2)krand0k1

 // return a random number in [0..k-1] with uniform distribution
 // using a uniform random generator in [1..50]
 funtion krand(k) {    
   sum = 0
   for i = 1 to k do sum = sum + RandNum50() - 1
   krand = sum mod k
 }

フィッシャーイェーツアルゴリズムは次のようになります。

arr : array[0..99]
for i = 0  to 99 do arr[i] = i+1; // store 1..100 in the array
for i = 99 downto 1 {
  r = krand(i+1)  // random value in [0..i]
  exchange the values of arr[i] and arr[r]
}
for i = 0 to 99 do print arr[i]

編集:

エリックによって指摘されたkrandように、上記の関数は真に均一な分布を返しません。より良い(任意に良い)とより速い近似を得るために使用できる他の方法があります。しかし(最大私の知る限り)真に均一な分布を得るための唯一の方法は使用することである棄却サンプリング:選択ランダムビットと個数場合R得未満であるKのリターンこと、そうでなければ生成します別の乱数; 可能な実装:m=log2(k)rk

function trulyrand(k) {
    if (k <= 1) return 0
    while (true) { // ... if you're really unlucky ...
      m = ceil(log_2 (k) ) // calculate m such that k < 2^m
      r = 0  // will hold the random value
      while (m >= 0) {  // ... will add m bits        
        if ( rand50() > 25 ) then b = 1 else b = 0   // random bit
        r = r * 2 + b  // shift and add the random bit
        m = m - 1
      }      
      if (r < k) then return r  // we have 0<=r<2^m ; accept it, if r < k
    }
}

1
リンク先のウィキペディアのページに、バリアントがあることが示されています。O(n)
デイブクラーク

1
「シャッフル」がここのキーワードです。
ラファエル

4
これは良い近似ですが、krand(k)のトリックは真に均一な分布を生成しません。k= 3の場合でも、33.333328%の確率で0を出力します。ここまでkまで合計する理由はありますか? ?近似値だけが必要な場合は、より小さな制限で十分だと思います。
エリックウォン

1
@ErickWong:あなたは正しいです。真の均一分布は、一定の時間で終了することが保証されていない除去サンプリング法を使用しないと到達できないと思います。他の近似スキーム(任意の近似に到達できる)がありますが、私が提案したのは最初に思いついたものです。
2012

2
r1..100rr

4

他の人々は不正確な数の逸脱をとることを含むおおよその解決策と解決策を与えたので、有限数のRandNum50()呼び出しのみを必要とすることが保証されているそのようなアルゴリズムがないことの証明はどうですか?

他の人が指摘したように、1〜100の数値をランダムな順序で印刷することは、これらの数値のランダムな順列を印刷することと同じです。100があります!これらの順列のうち、特定の順列は確率で出力する必要があります。1100!

kRandNum50kkRandNum50kkRandNum50(r1,r2,,rk)150kc50kc1100!100!50kk100!50k


2

nlogn+O(1)

if ( rand50() > 25 ) then b = 1 else b = 0   // random bit

1n!123n。これは実際、私が投稿で述べたように、私が提出した論文のトピックです!

その投稿で提案されているように、ランダムなビットからユニフォームを生成する方法がわからない場合は、ユニフォームの近似をこの方法で直接生成することもできます(これは、Vorの「トルイランド」と同等ですが、より高速です)。

P = (RandNum50()-1) + (RandNum50()-1)*50^1 + (RandNum50()-1)*50^2 + ...

P50PQ=Pmodnn=100!P>n


1

私は確認のための分析を行っていないか、均一(またはしない)これは次のようになり、真のシャッフルになるように調整することができますが、あなただけの開始配列から、選択することができますi= index番目i + 1(k + RandNum50() + RandNum50() - 1) mod (100 - k)と、インデックスを削除、k= 0..99?

これは、RandNum50() + RandNum50()分布のピークを均一に前方に「押し出し」ます。

最初の選択肢から0インデックス(1)を取得できず、0を生成する別の1..50 + 1..50調整をすぐに確認できないため、これはすでに述べたように、これはまったく正しくないと確信しています。 ..99。

更新

私が指摘した問題を修正するためRandNum100に、質問のコメントに記載されているように、最初のkオフセットをランダムに初期化するために効果的に使用しました。

これにより、前面に大きな波のある分布が生成されます。

1 RandNum50ずつ進める代わりに、もう1つ使用して最初に増加しましたk。これは、私にとって十分ランダムな結果を生成しますが、Kを2に変更すると簡単にわかるように、まだ「真の」ランダムではありません。

Kでも対応できるVB.NETコードをテストします。実際には、O(K)、6K + 2であることに注意してください。

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