作ってみたプログラムが動作しないです

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
レモン

作ってみたプログラムが動作しないです

#1

投稿記事 by レモン » 5年前

ここの掲示板は初利用なのでテンプレを使わせていただきます。なので何か足りない情報があれば追記します。
[1] 質問文
 [1.1] 自分が今行いたい事は何か

ー1から100までの乱数をー1が出るまで表示し続けるプログラムを作りたいです。

 [1.2] どのように取り組んだか(プログラムコードがある場合記載)

まず初めに配列を使って(図1)

コード:

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

int getrand(int min,int max);

int main (void)
{
	int random[],i = 0;
	do {
		random[i] = getrand(-1,100);
		printf ("%d\n",rand[i]);
		i++;
	}while (rand[i] != -1);
	return 0;
}

int getrand(min,max)
{
	static int d = 0;
	if (d == 0) {
		srand ((unsigned int) time(NULL));
		d = 1;
	}
	return min + (int) (rand() * (max - min + 1)/(RAND_MAX + 1));
}
次にポインタ変数を使って(図2)

コード:

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

int getrand(int min,int max);

int main (void)
{
	int i = 0;
	do {
		int *p;
		p = &getrand(-1,100);
		printf ("%d\n",*P);
	}while (*p != -1);
	return 0;
}

int getrand(int min,int max)
{
	static int d = 0;
	if (d == 0) {
		srand ((unsigned int) time(NULL));
		d = 1;
	}
	return min + (int) (rand() * (max - min + 1)/(RAND_MAX + 1));
}
 [1.3] どのようなエラーやトラブルで困っているか(エラーメッセージが解る場合は記載)

最初に図1を実行すると、

「9行目」で記述エラーを発見しました。
unknown type size

と出たので適当にrandom[]の中に100を入れてみて実行すると

「12行目」で記述エラーを発見しました。
「pointer」を付け忘れています。

と出てわからなくなりました。
そこで、ポインタ変数でも使ってみようと思い図2を実行すると

「12行目」で記述エラーを発見しました。
「lvalue」を付け忘れています。

と出て調べてみると左辺がいけないかなんからしいですがいまいちピンときません。

 [1.4] 今何がわからないのか、知りたいのか
今回知りたいのは
 ・どうすれば配列の個数を定数ではなくー1が出るまでの個数に設定できるのか。
 ・図1,図2の12行目では一体何のエラーが出てどう改善すればいいのか
 ・これらを直した後に発生するだろう問題点
 ・他に「こういう質問する人はここ勘違いしてるだろうな」ということについてのアドバイスなどあると嬉しいです。
(できれば)実は乱数の書き方は見様見真似でやっていてそこに関する質問です
 ・式はある程度理解できるのですがrand()にはどんな引数が入りますか?
 ・srand()の使い方とその中に入ってる引数について、unsigned intが符号の付かない変数の宣言などに使われることは知ってるのですがtime()とはどういう働きの関数ですか?
[2] 環境  
 [2.1] OS : Windows10
 [2.2] コンパイラ名 : よくわからなくて申し訳ないですがc言語の「学習用c言語開発環境」というものを使ってます

[3] その他
 ・どの程度C言語を理解しているか
  始めて三週間程度苦しんで覚えるc言語というところで独学でやってます。
  http://9cguide.appspot.com/index.html
  ここで二十章までと乱数の項目は一通り読みました。
 ・ライブラリを使っている場合は何を使っているか
  すいませんライブラリが何かわかりません。

答えられる範囲でどれか一つでもいいので教えていただけると幸いです。

box
記事: 2002
登録日時: 13年前

Re: 作ってみたプログラムが動作しないです

#2

投稿記事 by box » 5年前

コード:

		random[i] = getrand(-1,100);
		printf ("%d\n",rand[i]);
random
という変数名を使いたいのですか?それとも
rand
という変数名を使いたいのですか?
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

レモン

Re: 作ってみたプログラムが動作しないです

#3

投稿記事 by レモン » 5年前

すいません。randomという変数が使いたいです。
rand→randomに変えました。
whileの所も変えました。
変えて実行したところやっぱり9行目でエラーが出たのでまたrandom[]に100を
入れて何回も実行したところエラーはでなかったんですがー1が出ても動作が続いて最後には強制終了しました。
それと表示されてく乱数はー1から100に収まってるんですが、最初に表示される数字が約一分ごとに1増えていく感じで乱数になりませんでした。(疑似乱数の弊害ですかね?だとしたら対処法も教えてほしいです。)
<一番最初に表示された数字>
1回目  62
2回目  62
3回目  62
4回目  62
5回目  62
6回目  62
7回目  63
8回目  63
9回目  63
10回目 63
という具合です。2番目以降はぱっと見規則性はなかったです。
random[]に10を入れても同じ現象が起きました。
この時表示された数字の数は10を超えていました。

レモン

Re: 作ってみたプログラムが動作しないです

#4

投稿記事 by レモン » 5年前

図1の書き換え後です。

コード:

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

int getrand(int min,int max);

int main (void)
{
	int random[100],i = 0;
	do {
		random[i] = getrand(-1,100);
		printf ("%d\n",random[i]);
		i++;
	}while (random[i] != -1);
	return 0;
}

int getrand(min,max)
{
	static int d = 0;
	if (d == 0) {
		srand ((unsigned int) time(NULL));
		d = 1;
	}
	return min + (int) (rand() * (max - min + 1)/(RAND_MAX + 1));
}

box
記事: 2002
登録日時: 13年前

Re: 作ってみたプログラムが動作しないです

#5

投稿記事 by box » 5年前

srand()は、乱数を発生させる前に、「必ず1回だけ」実行(とりあえずmain()で)すればよいです。
少なくともmain()のdoループの中で何度も実行するgetrand()の中に
srand()はない方がよいです。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

かずま

Re: 作ってみたプログラムが動作しないです

#6

投稿記事 by かずま » 5年前

random[0] に最初の乱数が入った後、
random[1] が -1 かどうかチェックしても無意味です。
random[1] に 2番目の乱数が入った後、
random[2] が -1 かどうかチェックしても無意味です。

かずま

Re: 作ってみたプログラムが動作しないです

#7

投稿記事 by かずま » 5年前

box さんが書きました:
5年前
srand()は、乱数を発生させる前に、「必ず1回だけ」実行(とりあえずmain()で)すればよいです。
少なくともmain()のdoループの中で何度も実行するgetrand()の中に
srand()はない方がよいです。
getrand() の中では static int d により、srand() は 1回だけしか実行されません。

レモン

Re: 作ってみたプログラムが動作しないです

#8

投稿記事 by レモン » 5年前

boxさん、かずまさんありがとうございます。かずまさんの指摘に合わせてwhile(random != -1)をwhile(random[i-1] != -1)に変えた所ー1が表示されたらプログラムが終了するようになりました。
ただ、これだと数字が100個を超えたとき強制終了しちゃうので何とか-1が表示される分だけの配列のサイズの設定の仕方を知りませんか?大きく10000とか入れてもいいんですが定数を入れるとその回数超えてもー1が出なかったとき怖いですしメモリの無駄になる気がします。(メモリは解放できなくもない気はしますが)

レモン

Re: 作ってみたプログラムが動作しないです

#9

投稿記事 by レモン » 5年前

自分が作りたいプログラムはできました。
でもプログラム中で配列の個数を決める方法には興味があるので回答いただけると嬉しいです。
それとなぜ乱数の一番初めの数字には規則性があるのかも気になります。

コード:

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

int getrand(int min,int max);

int main (void)
{
	int p;
	do {
		p = getrand(-1,100);
		printf ("%d\n",p);
	}while(p != -1);
	return 0;
}

int getrand(min,max)
{
	static int d = 0;
	if (d == 0) {
		srand ((unsigned int) time(NULL));
		d = 1;
	}
	return min + (int) (rand() * (max - min + 1)/(RAND_MAX + 1));
}

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 作ってみたプログラムが動作しないです

#10

投稿記事 by みけCAT » 5年前

レモン さんが書きました:
5年前
でもプログラム中で配列の個数を決める方法には興味があるので回答いただけると嬉しいです。
realloc()を用いて動的確保する方法があります。

コード:

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

int getrand(int min,int max);

int main (void)
{
	int* random = NULL, i = 0;
	do {
		int* random_next = realloc(random, sizeof(int) * (i + 1));
		if (random_next == NULL) {
			perror("realloc() failed");
			free(random);
			return 1;
		}
		random = random_next;
		random[i] = getrand(-1,100);
		printf ("%d\n",random[i]);
		i++;
	}while(random[i - 1] != -1);
	free(random);
	return 0;
}

int getrand(min,max)
{
	static int d = 0;
	if (d == 0) {
		srand ((unsigned int) time(NULL));
		d = 1;
	}
	return min + (int) (rand() * (max - min + 1)/(RAND_MAX + 1));
}
  • realloc(確保し直すもとのポインタ(NULLなら新しく確保), 新しいサイズ(1要素のサイズ * 要素数))
  • 成功すると新しいオブジェクトへのポインタを返します。
    これはもとのポインタと同じかもしれませんし、違うかもしれません。
  • 失敗するともとのポインタが指すオブジェクトは開放せずにNULLを返すので、
    一旦別の変数に入れてチェックする方が行儀が良いとされます。
レモン さんが書きました:
5年前
それとなぜ乱数の一番初めの数字には規則性があるのかも気になります。
規則性が出る原因ではなさそうだとは思いますが、Wandboxで試したところ、
RAND_MAXが大きかったので、オーバーフローしてしまい上手く動きませんでした。

コード:

int getrand(min,max)
{
	static int d = 0;
	if (d == 0) {
		srand ((unsigned int) time(NULL));
		d = 1;
	}
	return min + (int) (rand() * (max - min + 1.0)/(RAND_MAX + 1.0));
}
のように、乱数として返す値を求める式中の1を1.0にしてdouble型で計算するほうが、動く環境が増えるでしょう。
また、引数の型も明示的に指定したほうがいいと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

かずま

Re: 作ってみたプログラムが動作しないです

#11

投稿記事 by かずま » 5年前

レモン さんが書きました:
5年前
それと表示されてく乱数はー1から100に収まってるんですが、最初に表示される数字が約一分ごとに1増えていく感じで乱数になりませんでした。(疑似乱数の弊害ですかね?だとしたら対処法も教えてほしいです。)
<一番最初に表示された数字>
1回目  62
2回目  62
3回目  62
4回目  62
5回目  62
6回目  62
7回目  63
8回目  63
9回目  63
10回目 63
という具合です。2番目以降はぱっと見規則性はなかったです。
time() の値を乱数の種(seed) として、srand() で設定しています。
time() の値は、1970年1月1日からの秒数なので、現在は 15億4千万を
超えた値ですが、プログラムを 10秒に 1回実行しても、乱数の種は
10 しか変わりません。

したがって、rand() による最初の 乱数の値は、あまり差のない値になります。

種を 10ずつ変えて、乱数を 5個ずつ発生させてみましょう。

コード:

#include <stdio.h>   // printf, putchar
#include <stdlib.h>  // srand, rand
#include <time.h>    // time

int main (void)
{
	int i, j;
	unsigned t = time(NULL);
	printf("RAND_MAX = %d\n", RAND_MAX);
	for (i = 0; i < 10; i++) {
		printf("%10d:", t);
		srand(t);
		for (j = 0; j < 5; j++) printf("%10d", rand());
		putchar('\n');
		t += 10;  // 10秒進める
	}
}
実行結果

コード:

RAND_MAX = 32767
1541390473:      7494     14104     28540      5362     22556
1541390483:      7527     23285     10574     16619     27398
1541390493:      7560     32465     25376     27877     32240
1541390503:      7592      8877      7409      6366      4313
1541390513:      7625     18057     22211     17623      9155
1541390523:      7658     27237      4245     28880     13997
1541390533:      7690      3649     19046      7369     18839
1541390543:      7723     12829      1080     18626     23680
1541390553:      7756     22009     15882     29883     28522
1541390563:      7788     31189     30684      8373       596
次に getrand(-1, 100) の返す値ですが、それは、
-1 + (int) (rand() * 102 / (RAND_MAX + 1)) です。

「学習用c言語開発環境」は Visual C++ と同じで RAND_MAX が 32767
なので、rand() が返す値は 0~32367 です。

種の値がほとんど同じなので、最初の乱数は
7494~7788 と狭い範囲の値にしかなっていません。
102倍しても、764388~794376。(RAND_MAX + 1) は 32768。
この値で割ると、23.327~24.242。整数ですから、23~24。

これが、最初の乱数が同じ値になる理由です。

32768 は 2の 15乗ですから、32768 で割ることは、
15ビット右シフトするのと同じで、元の数の下位15ビットを
捨てることになっています。

乱数として、rand() % (max - min + 1) + min にしたほうが、
下位ビットの値を利用できて、もっと良くなると思います。

ただ、% による乱数の生成も問題があります。
32768 = 102 * 321 + 26 なので、rand() % 102 の結果、
0~25 の出る確率が 322/32768、26~101 の出る確率が 321/32768
となって一様ではないということです。
でも、乱数の最初の値は同じにはなりません。

レモン

Re: 作ってみたプログラムが動作しないです

#12

投稿記事 by レモン » 5年前

返信遅れてすみません。みけcatさんありがとうございます。私がやりたかったのはまさにこういうことです。
double型の方がメモリを大きく使えるからオーバーフローしにくくなるってことですかね。
どうしてdouble型の方がいいのかあまり理解できてません。

かずまさんありがとうございます。要は商を使うのではなく余りで区別することでrand()で返ってきた値が近くてもバラバラな数値を得られるということですね。ただしRAND_MAX - 1の約数じゃないと同様に確からしい確率で乱数がえられないと。非常に勉強になります。

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 作ってみたプログラムが動作しないです

#13

投稿記事 by みけCAT » 5年前

(rand関数からは0~RAND_MAXの整数が全て等確率で返るすると)
商を使うにしろ余りを使うにしろ別の何かを使うにしろ、
0~RAND_MAXの整数から欲しい範囲の整数にマップすることになるので、
(RAND_MAX+1)が欲しい整数の種類数で割り切れない場合は、必ず等確率ではなくなります。
等確率にしたければ、割り切れない端の部分の数が出たら捨ててやり直すのがいいでしょう。

参考:いつからその方法で偏りのない乱数が得られると錯覚していた? - アスペ日記
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

返信

“C言語何でも質問掲示板” へ戻る