1 Lesson 11 乱数を使う サイコロを何回も降って、出た目を延々と記録していけば、不規則に並ぶ数字の羅列が得られるだろう。こ の羅列の中には、1から6までの数字が同じ確率で現れているはずである1 。そのような数字の羅列を、「一 様分布乱数」という。ここでは単に乱数とよぼう。 さて、コンピュータは、人間が指定した計算を素早く行うだけの道具である。何らかの計算手続きによって 生成される数字の羅列には、かならず規則があるはずだ。なので、コンピューターに乱数を生成させるのは 不可能なのである。しかし様々な工夫がなされていて、「見かけ上限りなくデタラメに見える」乱数は生成 することができる。そのような乱数を「疑似乱数」と呼ぶ。 11-1 疑似乱数を使う 実際に乱数を生成するための計算は、なかなか難しい。ここでは出来合いのものをつかって、乱数の応用法 を学ぼう。まず、あらかじめstdlib.hをインクルードしておく。プログラムの冒頭に #include<stdlib.h> と書いておこう。あとは、 random() とかけば、この関数が0からRAND_MAXの間の疑似乱数を返してくれる2 。返り値はint型である。つぎのプログ ラムを試してみよ。 sample18.c #include<stdio.h> #include<stdlib.h> int main( void ) { int i; for(i=1; i<=20; i++){ printf("%d\t",random()); /*"\t"はTAB*/ if( i % 5 == 0){ /*五回ごとに改行*/ printf("\n"); } } } 実行結果は以下の通り。 [nagahiro@Tacoma]: ./a.out 1804289383 846930886 424238335 719885386 1025202362 1350490027 1967513926 1365180540 [nagahiro@Tacoma]: 1681692777 1649760492 783368690 1540383426 1714636915 596516649 1102520059 304089172 1957747793 1189641421 2044897763 1303455736 乱数を[0,1]の実数として得たいときは、 1 厳密に言えば、床の上を転がるサイコロの運動は、ある運動方程式を解くことで得られる。なのでサイコロのそれぞれの目が、完全 に問う確率で現れるかどうかは、分からない。 2 RAND_MAXの値はstdlib.hのなかで定義されていて、使用しているコンパイラによって異なる。printfで表示すれば、値を調べられ る。 2 random()/(1.0*RAND_MAX); [-0.5,0.5]の実数の乱数がほしいなら (random()-0.5*RANDMAX)/(1.0*RAND_MAX); random()/(1.0*RAND_MAX)-0.5 というようにして、状況に応じて工夫して使用する。 種を指定する 上のプログラムは、何回実行しても同じ順番で同じ数字の羅列が生成される3 。このような乱数の発生系列 は、異なる「種(seed)」を指定してあげることで変更できる。random()を呼ぶ前に一度 srandom(seed); を実行して種を指定する。seedの値は、int型の正の値と決められていて、プログラマーが適当な値を入れて あげればよい(何も指定しない場合は、自動的に1が種にされる)。次の例では、現在時刻の値を利用し て、種を指定している(いつも異なる乱数列を得るための、定番的方法)。 sample18.c #include<stdio.h> #include<stdlib.h> #include<time.h>/*関数time()を使うために必要*/ int main( void ) { int i; srandom( (unsigned int)time(NULL)); /*種を指定*/ /*符号を含まないint型(unsigned int)に型キャスト*/ for(i=1; i<=5; i++){ printf("%d\t",random()); } printf("\n"); return 0; } 実行結果(三回):それぞれ違う乱数列になっていることを確認してほしい。 [nagahiro@Tacoma]: ./a.out 1553507380 1924538679 [nagahiro@Tacoma]: ./a.out 660358393 310491072 [nagahiro@Tacoma]: ./a.out 521573006 1715615616 [nagahiro@Tacoma]: 3 1459691473 45911062 1167576258 1281804812 2131024924 1839954441 1739203232 590563184 204621463 コンピュータはある決められた計算をしているだけのだから、当然であろう。 3 11-2 じゃんけんプログラム 乱数がよく利用される分野の一つに、コンピュータゲームがある。ここでは単純なじゃんけんプログラムを 作ってみよう。コンピュータにじゃんけんをさせる場合は、グーチョキパーをランダムに出させるように る4 。 n=random()%3 とすると、nには、乱数を3で割った余りが格納され、n=0,1,2のいずれかになる。0,1,2をそれぞれグー チョキパーに対応させればよい。 【提出課題 1】キーボードからグーチョキパーを入力して、乱数を使ったじゃんけんプログラムを作成せ よ。 11-3 モンテカルロ法 乱数の重要な応用として、モンテカルロ法がある。モンテカルロはF1で有名なモナコの都市で、同市がカジ ノで有名なことから、この名が付けられた。最も簡単なモンテカルロ法として、面積の計算をしてみよう。 y (a,b) x O 面積計算の基本的な考え方を述べる。右図のグレーの部分の面積 S を求めたい。そのために、点(a,b)を頂 点とする長方形の範囲内に、ダーツを打ち込む。ダーツがデタラメに投げられるものとすれば、長方形の範 囲内に入るもののうち、グレーの部分にダーツが刺さる確率は、。 p=S/(ab) であることは直ぐに分かる。乱数を使って、 0<x<a, 0<y<b の範囲内のx, yの値を取得し、それをダーツの刺さった位置とする。その位置が、図のグレーの部分に入っ ているかどうかを判定し、この作業を数万回繰り返すことで、確率pを求めることができるだろう。pが分か れば、上の式からSも分かる寸法だ。 次の例では、f(x)=x2, a=b=1として、面積を計算している。ab=1だから、p=S。つまり、ダーツがグレーの 部分に刺さる確率が、そのまま面積に等しい例である。 4 参考までに:過去のパターンを解析するじゃんけんプログラムがある。http://www-fcs.acs.i.kyoto-u.ac.jp/ aoki/janken/を参照。 殆どの人が勝つことのできないプログラムが公開されている。 4 sample19.c #include<stdio.h> #include<stdlib.h> #include<time.h> double f(double); double f( double x ){ return x*x; } int main( void ) { int i; int sum = 0, n=10000; /*nはダーツを打ち込む回数*/ double x,y; srandom( (unsigned int)time(NULL)); for(i=0; i<n; i++){ x = random()/(1.0*RAND_MAX);/*[0,1]の乱数を取得*/ y = random()/(1.0*RAND_MAX); if( y < f(x) ){ /*もし、点(x,y)がf(x)よりも小さかったら*/ sum++; /*グレー部分にダーツが当たったので、その回数を1だけ加算*/ } } printf( "p=%lf\n",sum/(1.0*n)); return 0; } 実行結果は以下の通り。真値は0.3333…なので当たらずとも遠からず。nの値を増やしていろいろ試してみ よう。 [nagahiro@Tacoma]: ./a.out p=0.329600 [nagahiro@Tacoma]: 【提出課題 2】sample19.cにて、 f (x) = ! 1 − x2 とすれば、1/4の円の面積が求まる。その結果から円周率πを求めよ。
© Copyright 2024 Paperzz