典型的な擬似乱数ジェネレーターは、以前の数値に基づいて新しい数値を計算するため、理論的には完全に決定論的です。適切なシード (乱数生成アルゴリズムの初期化) を提供することによって、唯一のランダム性が保証されます。乱数がセキュリティ上それほど重要でない限り (これには「実際の」乱数が必要です)、このような再帰的な乱数ジェネレーターは多くの場合、ニーズを満たします。
再帰的生成は、シードが提供されると、「外部」関数なしで表現できます。この問題を解決するアルゴリズムがいくつかあります。良い例はLinear Congruential Generatorです。
疑似コードの実装は次のようになります。
long a = 25214903917; // These Values for a and c are the actual values found
long c = 11; // in the implementation of java.util.Random(), see link
long previous = 0;
void rseed(long seed) {
previous = seed;
}
long rand() {
long r = a * previous + c;
// Note: typically, one chooses only a couple of bits of this value, see link
previous = r;
return r;
}
このジェネレータに初期値をシードする必要があります。これは、次のいずれかを実行することで実行できます。
- 現在の時刻のようなものを使用する (ゲームなど、セキュリティが重要でないほとんどの場合に適しています)
- ハードウェア ノイズの使用 (セキュリティ クリティカルなランダム性に適しています)
- 定数を使用する (常に同じシーケンスを取得するため、デバッグに適しています)
- 関数を使用できず、定数シードを使用したくない場合、およびこれを許可する言語を使用している場合は、初期化されていないメモリを使用することもできます。たとえば、C および C++ では、新しい変数を定義し、それに何かを割り当てず、その値を使用してジェネレーターをシードします。ただし、これは「良いシード」ではなく、要件を満たすための単なるハックであることに注意してください。これを実際のコードで使用しないでください。
システム環境などの外部ソースにアクセスせずに、同じ入力で異なる実行に対して異なる値を生成できるアルゴリズムはないことに注意してください。適切にシードされたすべての乱数ジェネレーターは、いくつかの外部ソースを利用します。