rand() % n
理想的ではないことに関する用語
実行rand() % n
には不均一な分布があります。値の数は20の倍数ではないため、特定の値の不均衡な数が得られます
次に、rand()
通常は線形合同ジェネレーターです(他にも多数ありますが、これは実装される可能性が最も高いものであり、理想的なパラメーターではありません(パラメーターを選択する多くの方法があります))。これに関する最大の問題は、その中の下位ビット(% 20
型式で得られるもの)がランダムでないことが多いことです。呼び出しのたびにrand()
最下位ビットが1
to に切り替わった数年前のことを思い出します。これはそれほどランダムではありませんでした。0
rand()
以下からのrand(3)のmanページ:
Linux Cライブラリのrand()とsrand()のバージョンは同じものを使用します
random()およびsrandom()としての乱数ジェネレーターなので、低次
ビットは上位ビットと同じくらいランダムでなければなりません。ただし、古い
rand()実装、および現在の異なる実装
システムでは、下位ビットは上位ビットよりもはるかにランダムではありません
オーダービット。目的のアプリケーションではこの機能を使用しないでください
良好なランダム性が必要な場合はポータブルです。
これは今や歴史に追いやられているかもしれませんが、スタックのどこかに隠れている貧弱なrand()実装をまだ持っている可能性はかなりあります。その場合、それはまだかなり適用可能です。
やるべきことは、実際に適切な乱数ライブラリ(適切な乱数を提供する)を使用して、必要な範囲内の乱数を要求することです。
コードの適切な乱数ビットの例(リンクされたビデオの13:00から)
#include <iostream>
#include <random>
int main() {
std::mt19937 mt(1729); // yes, this is a fixed seed
std::uniform_int_distribution<int> dist(0, 99);
for (int i = 0; i < 10000; i++) {
std::cout << dist(mt) << " ";
}
std::cout << std::endl;
}
これと比較してください:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
srand(time(NULL));
for (int i = 0; i < 10000; i++) {
printf("%d ", rand() % 100);
}
printf("\n");
}
これらのプログラムの両方を実行し、その出力で特定の数値が出現する(または出現しない)頻度を比較します。
関連動画:rand()は有害と見なされます
Nethackでバグを引き起こしているrand()のいくつかの歴史的側面は、自分の実装で注意深く検討する必要があります。
Nethack RNGの問題
Rand()は、Nethackの乱数生成のための非常に基本的な関数です。Nethackの使用方法はバグが多いか、lrand48()が誤った疑似乱数を生成すると主張されている可能性があります。(ただし、lrand48()は定義済みのPRNGメソッドを使用するライブラリー関数であり、それを使用するプログラムはすべて、そのメソッドの弱点を考慮に入れる必要があります。)
バグは、Nethackがlrand48()の結果の下位ビットに依存している(場合によってはrn(2)の場合と同様に)ものです。このため、ゲーム全体のRNGはうまく機能しません。これは、ユーザーアクションがさらにランダム性を導入する前、つまり、キャラクターの生成と最初のレベルの作成で特に顕著です。
上記は2003年のものですが、意図したゲームを実行しているすべてのシステムが、適切なrand()関数を備えた最新のLinuxシステムであるとは限らない場合があるため、注意が必要です。
自分でこれを実行しているだけの場合は、コードを記述してentで出力をテストすることにより、乱数ジェネレータがどの程度優れているかをテストできます。
乱数の性質について
正確にランダムではない「ランダム」の他の解釈があります。データのランダムストリームでは、同じ数値を2回取得する可能性があります。コインを投げると(ランダム)、2つの表が連続する可能性が非常に高いです。または、サイコロを2回投げて、同じ数字を2回続けて取得します。または、ルーレットホイールを回転させて、そこで同じ数字を2回取得します。
数の分布
曲のリストを再生するとき、「ランダム」とは、同じ曲またはアーティストが2回続けて再生されないことを意味することを期待します。プレイリストに2回続けてビートルズを再生させることは、「ランダムではない」と見なされます(ランダムですが)。4曲のプレイリストで合計8回再生されたという認識:
1 3 2 4 1 2 4 3
より「ランダム」です:
1 3 3 2 1 4 4 2
曲の「シャッフル」の詳細:曲をシャッフルするには?
繰り返し値について
値を繰り返したくない場合は、考慮すべき別のアプローチがあります。可能なすべての値を生成し、それらをシャッフルします。
あなたがrand()
(または他の任意の乱数ジェネレータ)を呼び出している場合は、それを置き換えて呼び出しています。同じ番号をいつでも2回取得できます。1つのオプションは、要件を満たす値を選択するまで、値を繰り返し破棄することです。これは非決定論的なランタイムであり、より複雑なバックトレースを開始しない限り、無限ループが発生する可能性があることを指摘しておきます。
リストとピック
別のオプションは、考えられるすべての有効な状態のリストを生成し、そのリストからランダムな要素を選択することです。部屋のすべての空のスポット(いくつかのルールを満たす)を見つけ、そのリストからランダムに1つ選択します。そして、それが終わるまで何度も繰り返します。
シャッフル
もう1つの方法は、カードのデッキのようにシャッフルすることです。始め、すべての部屋の中の空のスポットや、空のスポットを求めて各ルール/プロセスに、空のスポットを1つずつ出て扱うことによって、それらを割り当てる開始。あなたはカードを使い果たすか、物事がそれらを求めるのをやめると完了します。