前回の練習問題の解答例
#include <stdio.h>
int main(void)
{
int x, y, year;
double interest, rate;
printf("何年後までの金利を計算しますか?");
scanf("%d",&year);
//1行目
printf("+----");
for (x=2; x<=year; x++) {
printf("--------");
}
printf("+\n");
//2行目
printf("|年利|");
for (x=2; x<=year; x++) {
printf("%2d年後 |",x);
}
printf("\n");
//3行目
printf("|----");
for (x=2; x<=year; x++) {
printf("+-------");
}
printf("|\n");
//本体
for (y=1; y<=20; y++) {
printf("|%.1f%%|",(double)y/10.0);
rate=1.0+(double)y/1000.0;
interest=rate;
for (x=2; x<=year; x++) {
interest=interest*rate;
printf("%6.3f%%|",(interest-1.0)*100.0);
}
printf("\n");
}
//最終行
printf("+----");
for (x=2; x<=year; x++) {
printf("--------");
}
printf("+\n");
return 0;
}
前回の最後のプログラム例を書き換えればよい。
scanf関数で年数を変数yearに代入し、前回のプログラム例で繰り返し条件が10年後までとなっている部分を、yearまでに書き換える。
さらに、1行目と最終行の横線の長さを可変にする。
3行目の横線を書いている部分をコピーして、一部の記号を別の記号に置き換えればよい。
実行結果を見ながら修正すれば簡単である。
擬似乱数
数列x1,x2,x3,・・・が、x1からxiまでの値からxi+1を予測できず、
かつ、どの値になる確率も等しいとき、この数列を乱数という。
簡単に言えば、乱数とは完全にでたらめな数列である。
C言語では完全な乱数を作ることはできないが、乱数に近い擬似乱数を作ることはできる。
rand関数
rand関数は擬似乱数を生成する関数である。
この関数を使うにはstdlib.hをインクルードする必要がある。
関数の括弧()の中に記入するもののことを引数というが、rand関数には引数がない(したがって括弧の中に何も書かない)。
rand乱数は、呼び出されるたびに1つの整数を返す。
それらを並べると擬似乱数になるという仕組みである。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int counter, num;
for(counter=1; counter<=10; counter++) {
num=rand();
printf("%d ",num);
}
printf("\n");
return 0;
}
このプログラムを動かすと、でたらめな10個の整数が並ぶ。
しかし、これには欠点がある。
何度実行しても、同じ数列が作られるのである。
この欠点を解消するのが、次のsrand関数である。
srand関数
srand関数は擬似乱数の種を設定する関数である。
引数は種とする整数である。
rand関数は、ある数式にxiを代入することでxi+1を得ている。
そのため、数列を作るには最初の値x0が必要である。
このx0を「擬似乱数の種」という。
rand関数を単独で使う場合は1が種となるが、srand関数を使えば1以外の数値を種とすることができる。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int counter, num, seed;
printf("擬似乱数の種:");
scanf("%d",&seed);
srand(seed);
for(counter=1; counter<=10; counter++) {
num=rand();
printf("%d ",num);
}
printf("\n");
return 0;
}
様々な値を入力してみると、種の値によって異なる数列が生成されることが分かる。
プログラム例
種が1,2,…,10の場合の擬似乱数を並べて比較してみよう。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int counter, num, seed;
for (seed=1; seed<=10; seed++) {
srand(seed);
printf("種=%2d:",seed);
for(counter=1; counter<=10; counter++) {
num=rand();
printf("%5d ",num);
}
printf("\n");
}
return 0;
}
毎回異なる数列が作られるが、種の値が近いと後に続く数値の一部も近くなってしまう。
擬似乱数の限界である。
time関数
time関数はグリニッジ標準時1970年1月1日0時0分0秒からの経過時間を秒単位で返す関数である。
この関数を使うにはtime.hをインクルードする必要がある。
引数は「NULL」とする。NULLの意味は授業の範囲を超えるので説明を割愛する。
time関数の返却値を種として擬似乱数を生成するプログラム:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int counter, num;
srand(time(NULL));
for(counter=1; counter<=10; counter++) {
num=rand();
printf("%d ",num);
}
printf("\n");
return 0;
}
time関数の返却値を擬似乱数の種とすることで、実行のたびに異なる数列が生成される。
乱数の応用
乱数は、ゲームを作る時などによく利用する。
2つのサイコロの目の和が偶数(丁)か奇数(半)かを当てるゲーム(10回繰り返す):
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int counter, dice1, dice2, answer;
srand(time(NULL));
for (counter=1; counter<=10; counter++) {
dice1=rand()%6+1; //1個目のサイコロ
dice2=rand()%6+1; //2個目のサイコロ
//0か1を入力
printf("丁か半か(丁:0 半:1)?");
while (1) {
scanf("%d",&answer);
if (answer==0 || answer==1) {
break;
}
printf("0か1で答えてください:");
}
//勝敗判定
printf("サイコロの目:%d,%d ",dice1,dice2);
if(answer==(dice1+dice2)%2) {
printf("当たり!\n");
}
else {
printf("はずれ\n");
}
}
return 0;
}
rand()%6+1 が1〜6の範囲になることを利用してサイコロの目を作っている。
練習問題
ヒット&ブローは、ヒントに基づいて4桁の数を当てるゲームである。
- 重複しない4つの数字が「当たりの数値」として選ばれる
- 当たりの数値はユーザーに示されない
- ユーザーは当たりの数値を予想して答える
- ユーザーの数字の1つが当たりの数値に含まれていればブロー
- 含まれているだけでなく位置(桁)も合っていればブローではなくヒット
- ヒットとブローの個数がユーザーに示される
- ユーザーはこれに基づき予想し直して次の数値を答える
- 当たるまで繰り返す
例えば、当たりが「0275」でユーザーが「2015」と答えたら「1ヒット2ブロー」である
(0275と2015で同じ位置に5があるのでヒット1つ、0275と2015で両方に2と0が含まれるが位置が違うのでブロー2つ)。
下に示したのは、ヒット&ブローの数値を3桁に減らしたプログラムである。
このプログラムの動作を確認した上で、通常のヒット&ブロー(4桁)のプログラムに書き換えなさい。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int num1, num2, num3;
int user, user1, user2, user3;
int hit, blow, count;
//当たりの数値を決める
srand(time(NULL));
num1=rand()%10;
while (1) {
num2=rand()%10;
if (num2!=num1) {
break;
}
}
while (1) {
num3=rand()%10;
if (num3!=num1 && num3!=num2) {
break;
}
}
//printf("%d%d%d\n",num3,num2,num1); //動作確認用(本番は使わない)
//ゲーム開始
count=1;
printf("重複しない3つの数字:");
while(1) {
while (1) {
scanf("%d",&user);
user3=user/100;
user2=(user/10)%10;
user1=user%10;
if (user1!=user2 && user1!=user3 && user2!=user3) {
break;
}
printf("重複してます!重複しない3つの数字:");
}
//当たったら終了
if (user1==num1 && user2==num2 && user3==num3) {
break;
}
//はずれたらヒット数とブロー数を表示
hit=0;
blow=0;
if (user1==num1) {
hit++;
}
if (user2==num2) {
hit++;
}
if (user3==num3) {
hit++;
}
if (user1==num2 || user1==num3) {
blow++;
}
if (user2==num1 || user2==num3) {
blow++;
}
if (user3==num1 || user3==num2) {
blow++;
}
printf("%dヒット%dブロ−\n",hit,blow); //長音記号「ー」はダメ文字なので全角ハイフン「−」を使う
printf("次の数値:");
count++;
}
printf("%d回で当たりました!\n",count);
return 0;
}