2011-09-08 11 views
61

この質問の回答について Recommended way to initialize srand?最初のコメントでは、srand()はアプリケーション内で1回しか呼び出されないと言われています。それはなぜそうですか?srand() - なぜそれを一度だけ呼び出すのですか?

+0

ループで、srandを呼び出してからrandを呼び出す –

+6

Dilbertの[Tour of Accounting](http://dilbert.com/strip/2001-10-25)も参照してください。 –

答えて

86

これは、達成しようとしている内容によって異なります。

ランダム化は、開始値、すなわちを持つ関数として実行されます。です。

同じシードの場合、常に同じ値のシーケンスが得られます。

ランダムな値が必要なときにシードを設定しようとすると、シードが同じ数になると、常に同じ「ランダム」な値になります。

シードは通常time(NULL)のように秒で表示されるため、乱数を取る前に常にシードを設定すると、srand/randを呼び出すと同じ番号が返されます複数回コンボする同じ秒に

この問題を回避するため、アプリケーションインスタンスの2つが同じ秒で実行されるのは疑わしいので、srandは1回だけ設定されるため、各インスタンスは異なる乱数シーケンスを持ちます。

しかし、あなたのアプリケーションを実行する可能性はわずかです(特に、短いものやコマンドラインツールなど)を何回も実行すると、何か他のものに頼らなければなりませんシードを選択する方法(異なるアプリケーションインスタンスで同じシーケンスがあなたによって正常でない限り)。しかし、私が言ったように、それはあなたのアプリケーションの使用状況に依存します。

また、あなたは(同じシードの可能性を最小限に抑える)マイクロ秒の精度を高めるために試してみたいことがあり、(sys/time.h)が必要です。

struct timeval t1; 
gettimeofday(&t1, NULL); 
srand(t1.tv_usec * t1.tv_sec); 
+0

@Kornelije ...詳しい説明。たくさんありがとう。遅れて申し訳ありません。最後の数日間は外出していました。 –

+3

サイドノート: 'gettimeofday'はPOSIX 2008では時代遅れです。代わりに' clock_gettime'を導入しました。これは '-lrt'でリンクする必要があります。しかし、まだ多くのプラットフォームで利用可能ではないかもしれません。 Linuxではこれは問題ありません。 Macではまだ利用できないと思う。 Windowsではおそらく決して利用できません。 – Shahbaz

+0

t1.tv_usecはlong intで、srandはunsigned intを入力としてとります。 (そして、私はちょうどそれが違いを生む問題に遭遇しました) – Jiminion

6

なぜなら、srand()はランダムジェネレータの初期状態を設定しており、ジェネレータが生成するすべての値は自分自身の状態に触れないと「十分にランダム」であるからです。あなたができる例えば

int getRandomValue() 
{ 
    srand(time(0)); 
    return rand(); 
} 

、その後time()は、あなただけの同じ値が生成されます隣接の呼び出しで同じ値を返すように、あなたは繰り返しその関数を呼び出す場合 - それはデザインによってです。

2

srandをシード擬似乱数ジェネレータ。それを複数回呼び出すと、RNGを再出荷します。同じ引数で呼び出すと、同じシーケンスが再開されます。

#include <cstdlib> 
#include <cstdio> 
int main() { 
for(int i = 0; i != 100; ++i) { 
     srand(0); 
     printf("%d\n", rand()); 
    } 
} 

あなたは同じ番号が100回印刷表示されます。あなたのようなシンプルな何かをする場合

は、それを証明するために。

+2

質問はCについてであり、C++に関するものではありません。 –

21

乱数は実際には擬似乱数です。最初にシードが設定され、そこからそれぞれrandの呼び出しが乱数を取得し、内部状態が変更され、この新しい状態が次のrandコールで使用されて別の番号を取得します。これらの「乱数」を生成するために特定の式が使用されるため、randを呼び出すたびにシードの特定の値を設定すると、コールから同じ番号が返されます。たとえば、srand (1234); rand();は同じ値を返します。最初の状態をシード値で初期化すると、内部状態をsrandに設定しないため、十分な乱数が生成され、乱数を生成する可能性が高くなります。

通常、シード値を初期化するときに返される秒の値はtime (NULL)です。 srand (time (NULL));がループしているとします。ループは1秒間に複数回反復することができます。したがって、ループ内のループが2回目のループ内で反復処理される回数は、同じ "乱数"を返すので望ましくありません。プログラムの開始時にそれを一度初期化するとシードが1回設定され、randが呼び出されるたびに新しい番号が生成され、内部状態が変更されるので、次の呼び出しrandは十分にランダムな番号を返します。例えば

http://linux.die.net/man/3/randからこのコードは:

static unsigned long next = 1; 
/* RAND_MAX assumed to be 32767 */ 
int myrand(void) { 
    next = next * 1103515245 + 12345; 
    return((unsigned)(next/65536) % 32768); 
} 
void mysrand(unsigned seed) { 
    next = seed; 
} 

内部状態nextグローバルとして宣言されています。各myrandコールは、内部状態を変更して更新し、乱数を返します。 myrandのすべての呼び出しは異なるnextの値を持つため、メソッドは呼び出しごとに異なる数値を返します。

mysrandの実装を見てください。渡すシード値を単にnextに設定します。したがって、randを呼び出す前に毎回同じnextの値を設定すると、同じランダムな値が返されます。これは、同じ式が適用されているためです。これは、関数がランダムになるため好ましくありません。

しかし、あなたのニーズに応じて、シードをいくつかの値に設定して、各ベンチマークなどで同じ「ランダムシーケンス」を生成することができます。

+3

あなたの答えはこの質問の最も詳細な答えだと言わなければなりません。どのように「ランド」と「サンド」がどれくらい動作するかについて完璧に説明されています。 – manty

+0

mysrand()のパラメータに(unsigned long seed)を使用していませんか? – Jiminion

+0

@Jiminionこれは 'man srand'のコードスニペットです。範囲は0〜32767(RAND_MAXと仮定)で、これは「long」の範囲よりもはるかに小さい値です。内部の乗算と加算が 'unsigned int 'の範囲を超えるので、状態変数' next'は 'long'になります。その後、結果は上記の指定された範囲内でスケーリングまたは変更されます。あなたは種を「長く」作ることができますが。 – phoxis

1

同じ秒で実行されるアプリケーションインスタンスの異なるシードを生成するためにsrandを使用するより簡単なソリューションが見られます。

srand(time(NULL)-getpid());

この方法では、スレッドがいつ開始されたかを推測する方法がなく、pidも異なるため、シードがランダムに非常に近くなります。

4

短い答え:srand()ではなく、乱数ジェネレータの「サイコロを振る」のようなものです。それはカードのデッキをシャッフルするようなものでもありません。何かがあれば、それはカードのデッキを切り取るようなものです。

このように考える。 rand()は大きなカードのデッキからのもので、あなたがそれを呼び出すたびに、デッキの一番上の次のカードを選んで値を与え、そのカードをデッキの一番下に戻します。 (はい、それは「ランダム」シーケンスは、しばらく後に繰り返されます意味それはしかし、非常に大きなデッキです:。。通常は4,294,967,296カード)さらに

、あなたのプログラムが実行されるたびに、カードのブランドの新しいパックゲームショップから購入された場合、カードのすべての新しいパックは常に同じシーケンスを持ちます。だから特別なことをしない限り、あなたのプログラムが実行されるたびに、まったく同じ "ランダム"の数字がrand()から返されます。

ここで、「さて、デッキをシャッフルするにはどうすればいいですか?答えは(少なくともrandsrandが関係している限り)、デッキをシャッフルする方法はありません。

srandとは何ですか?私がここに構築した類推に基づいて、srand(n)と呼ぶのは基本的には「デッキnのカードを上から切る」のようなものです。しかしもう一度待ってください:それは実際に別の真新しいデッキを取って、それをnカードを上からカットします。

あなたがsrand(n)rand()srand(n)rand()、...を呼び出す場合毎回同じnで、あなただけの、非常にランダムでないシーケンスを取得することはできませんので、あなたが実際に戻って同じ番号を取得します毎回rand()から(未srandにあなたが利き、必ずしも同じ数が、バック何度もrandから同じ数。)

だから、あなたができる最善ので、つまり、一度srand()を呼び出し、一度デッキをカットすることですあなたのプログラムの初めには、nが適度にランダムであるため、プログラムが実行されるたびに大きなデッキの別のランダムな場所から開始します。

[P.S.はい、私が知っている、現実の生活の中で、カードの新しいデッキを購入するとき、それは通常、順不同ではありません。ここでの類推のために、あなたがゲームショップから購入する各デッキは、一見無作為な順序であると考えていますが、同じ店から購入したカードの他のデッキと全く同じようにランダムな順序です。 (橋のトーナメントで同じようにシャッフルされたカードのように)

0

1 \ rand()が実行されるたびに、次のrand()の新しいシードが設定されます。 (NULL)が変更されない場合)、次のrand()がrand()と同じになる場合に問題が発生します。以前のsrand()の直後。

+0

主なポイントは、同じシードで 'srand()'を数回初期化すると、 'rand()'が返す値と同じ値になります。 –

関連する問題