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

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

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

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

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

16766閲覧

【C】srand(time(NULL))をしても同じ乱数が生成される

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

2グッド

1クリップ

投稿2021/12/27 09:57

編集2021/12/27 10:07

###問題点
0〜6の乱数を生成して6段階のおみくじを出力するプログラムを作成しています。srand(time(NULL))で乱数のシードを設定しているのですが毎回出力が6になってしまいます。srandが機能していないのはわかるのですがどう改善すればいいのか教えていただきたいです。

###該当コード

C

1//おみくじ 2 3#include <time.h> 4#include <stdlib.h> 5#include <stdio.h> 6#include <string.h> 7 8int Omikuji(void); //プロトタイプ宣言 9 10int main(void) 11{ 12 Omikuji(); 13 return 0; 14} 15 16int Omikuji(void) 17{ 18 int num; //乱数 19 char luck[20]; //吉凶 20 srand(time(NULL)); //乱数のシード 21 num = rand() % 7; //乱数生成 22 23 switch (num) 24 { 25 case 0: 26 strcpy(luck, "大吉"); 27 break; 28 case 1: 29 strcpy(luck, "中吉"); 30 break; 31 case 2: 32 strcpy(luck, "小吉"); 33 break; 34 case 3: 35 strcpy(luck, "吉"); 36 break; 37 case 4: 38 strcpy(luck, "末吉"); 39 break; 40 case 5: 41 strcpy(luck, "凶"); 42 break; 43 case 6: 44 strcpy(luck, "大凶"); 45 break; 46 } 47 48 printf("%d あなたの吉凶は:%s\n", num, luck); //結果の表示 49 50 return 0; 51} 52

###実行結果

6 あなたの吉凶は:大凶
equal-l2, yohhoy👍を押しています

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

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

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

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

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

itagagaki

2021/12/27 10:06

実行結果をどうやって確認しましたか? 提示のコードをそのままコンパイルし、1秒以上の間隔をあけて実行したら、そのたびに違う結果が出ましたよ。
退会済みユーザー

退会済みユーザー

2021/12/27 10:08

code runnerで実行をしました。すいません。実行結果を掲載し忘れていました。
itagagaki

2021/12/27 10:10

1秒以上の時間をあけて実行しましたか? time(NULL)の値は秒単位なので、1秒以上たたないと同じ値になります。
退会済みユーザー

退会済みユーザー

2021/12/27 10:17

はい空けています。乱数生成の簡単なプログラムを作ってみたのですが、そちらですと毎回ちがう乱数が生成されます。
itagagaki

2021/12/27 10:20 編集

不思議ですね。 timeの返値を確認してみては。 time_t t; t = time(NULL); srand(t); printf("%ld %d あなたの吉凶は:%s\n", t, num, luck);
退会済みユーザー

退会済みユーザー

2021/12/27 10:22

なぜかrand() % 5や6にすると違う乱数が生成されるのに7した途端6だけしか生成されません。不思議です。
退会済みユーザー

退会済みユーザー

2021/12/27 10:23

1640600587 6 あなたの吉凶は:大凶 1640600589 6 あなたの吉凶は:大凶 1640600593 6 あなたの吉凶は:大凶 確かにシードは違うのに6しか生成されていないみたいですね
itagagaki

2021/12/27 10:27

提示のコードにおかしなところは無いし、それを私の環境で実行してもそのような不思議なことは起こらなかったし、そちらの環境(コンパイラやライブラリ)がおかしいというのも考えにくいので、1つ考えられるのは、本当は提示のコードとは違うコードをコンパイルした結果を実行してしまっているのでは?ということです。そのへんを重点的に確認してみてください。
退会済みユーザー

退会済みユーザー

2021/12/27 10:31

おかしいですね。時間は確かに違いますし%6や%8にした瞬間に乱数が変わってきます。%7にした瞬間に6しか生成されないです。一回時間をおいてみます。
ak.n

2021/12/27 10:56

・まず、真っ先にやることは、 time(NULL) の値が毎回変わっているか、確かめる。 time_t t = time(NULL); printf("%ld\n", t); ・次に、srand の引数が、正しく引き渡されているのかどうか・・   srand((unsigned)time(NULL))  としてみたらどうなる? 32bit と 64bit で違うとかあるようです。  時々、不思議な現象に遭遇しますが、必ず原因があります。
jimbe

2021/12/27 11:36

乱数なのですから、同じ値が何度出ても不思議では無いとも言えます。
退会済みユーザー

退会済みユーザー

2021/12/27 12:19

ありがとうございます。
ak.n

2021/12/27 12:37

rand() の素の値は、毎回異なりますか? もし rand()の値は異なるのに rand() % 7 した結果が同じということであれば、rand() はシード値により値を変えているが 7 で割った余りが一致するような乱数が出力されている、としか考えられません。rand()の実装によっては、あり得ることですが、当方でコンパイルしてもそうはなりません・・・なんでしょうかね。
guest

回答3

0

一部の環境では、rand() % 7 を使わない方がよさそうです。

「C言語での乱数生成で常に同じ値となってしまうケースがある」でググると出てきます。

何が起きているかというと、srand() は正しく機能していて、 rand() も毎回異なる値を出してはいるが、「7 で割った余りの値」は、シード値の与え方を time() にしている限り 35.5 時間経たないと、変わらない、です。

7 のときだけ不具合が出ます。理由はこうです:

rand() の内部処理で、このような場所があります。

C

1 hi = *ctx / 127773; 2 lo = *ctx % 127773; 3 x = 16807 * lo - 2836 * hi;

*ctx がシード値、x が rand() の返り値だとお考え下さい。

16807 * lo の部分は常に 7 の倍数となるため、
x % 7 の値を決めるのは 2836 * hi の部分になります。

ところが hi は、*ctx が 127773 変化しない限り、値は一定です。

そのため、x % 7 の値はいつまでたっても変わりません。

シード値に time(NULL) を指定されているのであれば、
シード値が 127773 変わる、つまり 35.5時間後にならないと、
7 で割った余りの値が変わりません。

解決策として、たとえば rand() % 8 して結果 7 のときは、再度 rand() する(while)などして、0~6を取得するとか、なるべく 7 と互いに素な数で余りを出したほうがよさそう。

投稿2021/12/27 13:07

編集2021/12/27 13:25
ak.n

総合スコア291

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

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

退会済みユーザー

退会済みユーザー

2021/12/27 14:48

16807と2836はどこから出てきたのでしょうか?要するに35.5時間経たないとずっとあまりが6になってしまうってことですね。 他の方はなぜ同じような不具合が起こらないのでしょうか?
ak.n

2021/12/27 15:04

> 16807と2836はどこから出てきたのでしょうか? rand() のコードを書いた人が、この数に決めたのでしょうね。rand() と言いましても、でたらめな数を出しているように見えて、結局のところ、何かしらの計算をして、乱数っぽいものを出している、ということなのです。 今回、% 7 が、この rand() の中の 7 とシンクロしてしまった、というわけです。 他の方で不具合が起きないのは、rand() の中の計算方法が違うのではないかと思います。 この実装からするとおそらく % 49 にしても、毎回余りは 6 になるような気がします。
ak.n

2021/12/27 15:32

なるほど、乱数は奥が深いです。勉強になります。
fana

2021/12/28 01:53

てきとーに N bit ほどシフトするとかだと対処とはならないのでしょうか? int num = ( rand() >> N ) % 7; //Nの値を誰が決めるのか謎だが
退会済みユーザー

退会済みユーザー

2021/12/28 06:47

範囲乱数公式使った方が良いということですか??
ak.n

2021/12/28 09:35

私もよくわかりませんが、ひとまず、剰余(%)は使わないほうが良さそうです。一定のパターンが現れる可能性があります。ゲームなどですと、キー入力待ちのループの中で rand() を呼んで値を読み捨てる、とかするのでしょうか。 ここは乱数に詳しい方にぜひご登場いただきたいところです。
guest

0

C FAQ に載っているほど、多くの人が遭遇する問題で、対処方法も確立されています。

ある範囲の整数からなる乱数はどうやったら生成することができるか。

すぐに思い付く、rand() % N (これは0からN-1までの数を返そうとする)は乱数の質が低い。なぜな ら乱数発生器の多くで下位のビットは悲惨なほどランダムでない(質問13.18を参照のこと)。よりよい方法は以下のようなものである。
(int)((double)rand() / ((double)RAND_MAX + 1) * N)
浮動小数を使うことが気になるのなら、以下の方法を試せばよい。
rand() / (RAND_MAX / N + 1)

投稿2022/01/09 15:35

int32_t

総合スコア21207

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

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

0

ベストアンサー

https://teratail.com/questions/221781#reply-324973

srand((unsigned int)time(NULL) * (unsigned int)time(NULL)); //乱数のシード

こちらに変更することで対処できました。srand(time(NULL))でできない理由は不明のままです。

投稿2021/12/27 12:21

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

jimbe

2021/12/27 12:35

リンク先 221781 の BA 回答 episteme さんの > 標準ライブラリの乱数(srand()/rand())は決して質の良いものではないので(特にVisual C++のは) というのが主な理由ということになりそうですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.44%

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

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

質問する

関連した質問