RandNum50
1〜50の範囲で一様にランダムな整数を生成する乱数ジェネレーターが提供されます。この乱数ジェネレータのみを使用して、1から100までのすべての整数をランダムな順序で生成および出力できます。すべての数は正確に1度来る必要があり、任意の場所で任意の数が発生する確率は等しくなければなりません。
これのための最も効率的なアルゴリズムは何ですか?
RandNum100 = (RandNum50() * 2) - (RandNum50 > 25) ? 0 : 1)
。
RandNum50
1〜50の範囲で一様にランダムな整数を生成する乱数ジェネレーターが提供されます。この乱数ジェネレータのみを使用して、1から100までのすべての整数をランダムな順序で生成および出力できます。すべての数は正確に1度来る必要があり、任意の場所で任意の数が発生する確率は等しくなければなりません。
これのための最も効率的なアルゴリズムは何ですか?
RandNum100 = (RandNum50() * 2) - (RandNum50 > 25) ? 0 : 1)
。
回答:
フィッシャーイェイツのシャッフルを使用するこのソリューションは(間違っている可能性があるので:-)考えました。すべての反復で適切な近似(以下の編集セクションを参照)で均一な分布を維持するために、このトリックを使用して0とk − 1の間の値を生成できます。krand
// 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のリターンこと、そうでなければ生成します別の乱数; 可能な実装:
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
}
}
他の人々は不正確な数の逸脱をとることを含むおおよその解決策と解決策を与えたので、有限数のRandNum50()
呼び出しのみを必要とすることが保証されているそのようなアルゴリズムがないことの証明はどうですか?
他の人が指摘したように、1〜100の数値をランダムな順序で印刷することは、これらの数値のランダムな順列を印刷することと同じです。100があります!これらの順列のうち、特定の順列は確率で出力する必要があります。
RandNum50
RandNum50
RandNum50
私は確認のための分析を行っていないか、均一(またはしない)これは次のようになり、真のシャッフルになるように調整することができますが、あなただけの開始配列から、選択することができます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であることに注意してください。