[解決済み】java.util.Randomは本当にランダムなのか?どうやったら52! (階乗)可能な配列を生成することができますか?
質問
私はこれまで
Random (java.util.Random)
を使って、52枚のカードのデッキをシャッフルしています。その数、なんと52枚 (8.0658175e+67) の可能性があります。それなのに、種明かしをすると
java.util.Random
は
long
となり、2^64 (1.8446744e+19)とかなり小さくなります。
ここから、怪しいのは
java.util.Random
は本当にランダムなのか
52の可能性をすべて生み出すことができるのでしょうか?
そうでない場合、どうすればより良い乱数列を確実に生成し、52!通りの可能性を生み出すことができるでしょうか?
どのように解決するのですか?
ランダムな並べ替えを選択するためには、質問者が示唆するよりも多くのランダム性と少ないランダム性が同時に必要です。説明しましょう。
悪い知らせ:もっとランダム性が必要。
あなたのアプローチの根本的な欠点は、~2つのうちどちらかを選ぼうとしていることです。 226 64ビットのエントロピー(乱数種)を使って、可能性があります。2つの可能性を公平に選択するために 226 64ビットの代わりに226ビットのエントロピーを生成する方法を見つけなければならないのです。
ランダムビットを生成する方法はいくつかあります。 専用ハードウェア , CPU命令 , OSインターフェース , オンラインサービス . あなたの質問には、どうにかして64ビットを生成できるという暗黙の前提がすでにあるので、あなたがしようとしていたことを4回だけ行い、余ったビットを慈善団体に寄付しましょう。)
良い点:ランダム性を少なくする必要がある。
この226個のランダムビットがあれば、あとは決定論的に行うことができますので
のプロパティは
java.util.Random
は無関係にすることができる
. 以下はその方法です。
52!の順列をすべて生成し(我慢してください)、辞書順に並べたとしましょう。
順列の1つを選択するために必要なのは、以下の間の1つのランダムな整数だけです。
0
と
52!-1
. この整数が226ビットのエントロピーです。これを並べ替えのインデックスとして使用する。ランダムインデックスが一様に分布していれば、すべての並べ換えが選択されることが保証されるだけでなく、選択されることになります。
等速
(これは質問の内容よりも強い保証です)。
さて、実際にはこれらの並べ換えをすべて生成する必要はありません。この仮想的な並べ替えリストの中でランダムに選ばれた位置から、直接並べ替えを生成することができます。これは、O(n 2 ) の時間を使って レーマー [1] コード (また ナンバリングパーミュテーション と ファクトリアディック数体系 ). ここでいうnはデッキの大きさ、つまり52です。
C言語の実装は、この
StackOverflowの回答
. そこには、n=52 でオーバーフローしてしまういくつかの整数変数がありますが、幸いにも Java では
java.math.BigInteger
. 残りの計算は、ほぼそのまま書き写すことができます。
public static int[] shuffle(int n, BigInteger random_index) {
int[] perm = new int[n];
BigInteger[] fact = new BigInteger[n];
fact[0] = BigInteger.ONE;
for (int k = 1; k < n; ++k) {
fact[k] = fact[k - 1].multiply(BigInteger.valueOf(k));
}
// compute factorial code
for (int k = 0; k < n; ++k) {
BigInteger[] divmod = random_index.divideAndRemainder(fact[n - 1 - k]);
perm[k] = divmod[0].intValue();
random_index = divmod[1];
}
// readjust values to obtain the permutation
// start from the end and check if preceding values are lower
for (int k = n - 1; k > 0; --k) {
for (int j = k - 1; j >= 0; --j) {
if (perm[j] <= perm[k]) {
perm[k]++;
}
}
}
return perm;
}
public static void main (String[] args) {
System.out.printf("%s\n", Arrays.toString(
shuffle(52, new BigInteger(
"7890123456789012345678901234567890123456789012345678901234567890"))));
}
[1] と混同しないように レーラー . :)
関連
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Java エラー報告 スレッド "main" での例外 java.util.NoSuchElementException
-
java の例外が発生しました java
-
Spring boot runs with Error creating bean with name 'entityManagerFactory' defined in class path resource
-
VMの初期化中にエラーが発生しました java/lang/NoClassDefFoundError: java/lang/Object
-
スレッド "main" で例外発生 java.lang.ArrayIndexOutOfBoundsException: 4 at text.Division.main(Divisi
-
javax.net.ssl.SSLException: 読み取りエラー: ssl=0xdeae5100: システムコール中の I/O エラー、接続 res
-
このラインで複数のマーカーを解決する方法
-
Maven Pluginの実行がライフサイクル設定の対象外であるエラーの解決
-
[解決済み] SecureRandomジェネレータが遅い場合の対処法は?
-
[解決済み] 20バイトのランダムな配列を作成するには?