質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

4回答

1125閲覧

ランダムな実数x,y(0 <= x <= 1, 0 <= y <= 1)の出力

k888

総合スコア123

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2020/11/06 14:23

編集2020/11/06 14:47

ランダムな実数x,y(0 <= x <= 1, 0 <= y <= 1)を出力する以下のプログラムを実行したところ、

c言語

1#include<stdio.h> 2#include <stdlib.h> 3#include <time.h> 4 5int main(void) { 6 double x, y, d; 7 srand((unsigned)time(NULL)); 8 x = (double)rand() / RAND_MAX; 9 y = (double)rand() / RAND_MAX; 10 d = x * x + y * y; 11 printf("(%lf, %lf)\n", x, y); 12}

次のようになった。

(x, y) = (0.755291, 0.182222) (x, y) = (0.755307, 0.445297) (x, y) = (0.755307, 0.445297) (x, y) = (0.755315, 0.576835) (x, y) = (0.755323, 0.708373) (x, y) = (0.755988, 0.889085) (x, y) = (0.755996, 0.020622) (x, y) = (0.756004, 0.152160) (x, y) = (0.756011, 0.283698) (x, y) = (0.756019, 0.415236)

x、y共にランダムになっているが、xの値は0.75から始まる実数しか出力されていない。なぜこのような結果になるのかわからない。
教えてください。

(補足)
プログラム全体を10回ほど数秒おきに実行した結果です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

C の標準ライブラリが提供する rand の性質について言語仕様では詳細が決められていないので各処理系 (に付随するライブラリ) によるのですが、伝統的に線形合同法が用いられることが多いです。

X[n+1] = (A*X[n]+B) mod M

一部の環境では定義通りの数式で処理した後に下位バイトを捨てる処理が入っています。 これによって乱数の最初の値はシードの差の影響が大幅に小さくなることになり、十秒ごと程度ではかなり近い値が出力されるわけです。

具体的には、 Windows の MSVCRT 内の rand は以下に相当する実装になっていました。

c

1#define RAND_MAX 0x7fff 2 3static unsigned long seed = 1; 4 5int rand(void) { 6 seed = seed * 214013 + 2531011; 7 return (seed>>16) & RAND_MAX; 8} 9 10void srand(unsigned int x) { 11 seed = x; 12}

線形合同法では下位バイトの数学的性質が悪いことが知られているのでそれに対応するために下位バイトを捨てているのでしょう。

初期値の影響の強さを避けるには最初のほうの百個くらいは捨てるといった工夫が知られています。


その他、線形合同法は前後を組にして扱う (多次元空間にプロットする) と露骨な規則性が見える場合があることが知られており、色々と問題もあります。 乱数列の数学的性質というのはなかなか複雑な事情があるので用途に照らして問題がないかよく検証する必要があります。

投稿2020/11/06 15:35

編集2020/11/06 15:53
SaitoAtsushi

総合スコア5684

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

k888

2020/11/06 15:55

回答ありがとうございます。参考にします。
guest

0

RAND_MAXが32767なC言語処理系で、srand((unsigned)time(NULL));でシードを設定すると、(秒が変わっても時刻が近いと)最初のrand()の値がよく似た値になってしまうようだ。

実際には、srandrandを毎回セットで実行するような間違ったコードを書かなければ、それほど問題にはならない。あるいは、最初の(あるいは数回の)randの値を捨ててから実際に使う値を取り出せばいい。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <time.h> 4 5int main(void) 6{ 7 int x; 8 printf("RAND_MAX: %d\n", RAND_MAX); 9 while (1) { 10 srand(time(NULL)); 11 printf("%d\n", rand()); 12 sleep(2); 13 } 14 return 0; 15}

result

1RAND_MAX: 32767 21945 31952 41958 51965 61971 71978 81984 91991 101997 112004 122010 132017 142024 152030

投稿2020/11/06 15:13

編集2020/11/06 15:16
Daregada

総合スコア11990

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

time(NULL)はある時刻(大抵は1970年1月1日午前0時)からの経過秒数を返します。
連続して実行した場合、srand()に渡される値はほとんどの場合下の数桁しか変化しません。
あなたの使用しているCコンパイラ付属のランタイム実装において
srand()にわたす値の下位の桁が、rand()の初回に返す値の上位の桁にあまり影響しないような乱数実装となっているのでしょう。

投稿2020/11/06 14:51

SHOMI

総合スコア4079

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

プログラム内に繰り返しはないので、このプログラム全体を何度も繰り返した場合ということでしょうか?

timeの返す値は秒単位なので、同じ秒の時に実行すると同じ値でsrandすることになり、同じ乱数が発生します。

対策としては、
・マイクロ秒単位の時刻を取得してそれを使う
・プロセスID等、実行ごとに異なる値を秒単位の時刻に足すなどして使う
など。

投稿2020/11/06 14:32

otn

総合スコア85901

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

k888

2020/11/06 14:53

回答ありがとうございます。プログラム全体を10回ほど数秒おきに実行した結果です。言葉足らずですみません。
otn

2020/11/06 15:26 編集

失礼。4つめ以降の出力を展開して見てませんでした。 そうすると、randサブルーチンの品質が良くないと言うことだと思います。 ・別の乱数サブルーチンがあればそれを使う ・2つめは大丈夫そうなので、何回かrandを空呼び出してから使う など。 回答に書いた方法も有効だと思います。PIDを使うなら足すのではなく掛ける方が良いかな。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問