2

0 から 59 までの乱数を生成しようとしていますが、C の rand() 関数に満足していません。

#include <stdlib.h>
#include <time.h>

main()
{

int num;
srand(time(NULL));
num = rand();
num = num % 59;
printf("%d\n", num);
}

このコードで実行を繰り返したところ、生成される乱数が実際にはそれほどランダムに見えないことに気付きました。プログラムを実行するたびに、最初に戻るまで数値が徐々に大きくなるため、生成された数値は間違いなくパターンに従っています (つまり、2、17、21、29、38、47、54、59、4、 11....など)。

関数を再実行するたびに生成される確率が 1/60 の真の乱数を取得できるように、関数をシードする方法はありますか? または、C で rand() 関数を使用する代わりに、自分で実装できる代替方法はありますか?

4

3 に答える 3

7

関数を再実行するたびに真の乱数を取得するように関数をシードできる方法はありますか

いいえ、C 標準ライブラリは PRNG (疑似乱数発生器) を使用します。真の乱数を取得することはありません。

time()ただし、 POSIX などよりも頻繁に変更されるものをシードすることはできます。

struct timeval tm;
gettimeofday(&tm, NULL);
srandom(tm.tv_sec + tm.tv_usec * 1000000ul);

また、乱数の生成にモジュロ演算子を使用するのは適切な解決策ではありません (エントロピーが大幅に減少します)。BSD スタイルの libc 実装を使用している場合は、

uint32_t n = arc4random_uniform(60);

または、この関数がない場合:

// random() is guaranteed to return a number in the range [0 ... 2 ** 31)
#define MAX_RANDOM ((1 << 31) - 1)

long n;

do {
    n = random();
} while (n > (MAX_RANDOM - ((MAX_RANDOM % 60) + 1) % 60));
n %= 60;

の使用に注意してください- それは(低品質の実装がいくつかありましたrandom()) よりも優れています。rand()この関数は、 を使用してシードできますsrandom()

または、C で rand() 関数を使用する代わりに、自分で実装できる代替方法はありますか?

できます (もちろん、そうでなければ、C ライブラリ実装の作成者はどのようにそれを行うでしょうか?)、そうしない方がよいでしょう。いわば、優れた PRNG を作成することは別の科学です。

于 2013-08-15T14:10:15.763 に答える
0

プログラムを再実行するたびに、 で再シードしtime()、その関数は 1 秒に 1 回だけ進みます (プログラムを十分にすばやく再実行すると、同じ結果が得られます)。

ロールオーバーするまで増加するように見えるという事実は、 への最初の呼び出しがrand()変更されていないシード (1 秒に 1 回増加する数) を返していることを示唆しています。その場合、以下を実行した場合と同じ結果 (または非常に類似した結果) が得られます。

printf("%d\n", time(NULL) % 59);

私はあなたがそれで何が悪いのかを見ることができると確信しています。

この場合、rand() * 59 / RAND_MAX「よりランダムな」ビットからの値を優先することを意図した「より正しい」を使用すると、さらに悪い状況が発生します。結果は、おそらく 500 秒間まったく変化しません。以上。

基本的に、予測しにくいシードを見つける必要がありますが、使用する前に適切に混合されていることを確認することもできます。

からの読み取り/dev/urandomは良いシードを提供するはずです。その場合、混合について心配する必要はありませんが、それ以外の場合rand()は、最初に使用した低品質のシードの特に目立つアーティファクトを取り除くのに数回呼び出す必要があります (ただし、もちろん、1秒に1回しか変化しないという問題)。

于 2013-08-15T15:01:10.763 に答える