あなたのサイコロは正しいか?

ホームC#入門講座第2章:疑似サイコロ > あなたのサイコロは正しいか?

あなたのサイコロは正しいか?

素朴な疑問

疑似サイコロを作ってみて素朴に思うのが「この疑似サイコロはほんとに正確(精確)にできているのだろうか」ということです。もし正確でないのならば、より正確に近づける余地があります。それを確かめるためには、何回もサイコロを振ってみなくてはいけません。しかし、それは現実的に無理でしょう。あなたは1000回もプログラムを実行しつづけ、結果を記録する根気がありますか? 少なくとも私にはないです。

ですから、すべてをプログラムの中で完結させ、人間は「十分に正しいか」を判断するだけにしたいものです。今回はその判断を下すために、繰り返し構文を学びます。

(例題)

サイコロをふるプログラムを1000回実行し、その統計を元に精度を判定せよ。

ここで注意しなくてはならないのが、「1回実行すること」と「1000回連続で実行すること」は意味が違うということです。なぜなら、1回の処理が1ミリ秒よりも速い場合、連続で同じ数字を元にサイコロを振ってしまうからです。その場合は、実行する回数を多くすることで解決できるのできるのではないか、と考えられます。

ソースコード

using System;

namespace NewWorld
{
	class MainClass
	{
		public static void Main(string[] args)
		{
			int a = 0;
			int b = 0;
			int c = 0;
			int d = 0;
			int e = 0;
			int f = 0;
			
			for(int i = 0;i<1000;i++)
			{
				DateTime dt = DateTime.Now;
				int x = dt.Millisecond;
			
				int y = x % 6;
				if (y == 0)
					y = 6;
			
				switch (y)
				{
					case 1:
						a++;
						break;
					case 2:
						b++;
						break;
					case 3:
						c++;
						break;
					case 4:
						d++;
						break;
					case 5:
						e++;
						break;
					case 6:
						f++;
						break;
						
				}
			}
			Console.WriteLine("1:{0} times",a);
			Console.WriteLine("2:{0} times",b);
			Console.WriteLine("3:{0} times",c);
			Console.WriteLine("4:{0} times",d);
			Console.WriteLine("5:{0} times",e);
			Console.WriteLine("6:{0} times",f);
			
			
		}
	}
}

配列という概念を身につければ、このプログラムはよりスマートに記述できます。ですがここではそれをまだ習っていないので、aやbなどの変数を用いました。配列を習うときに、このプログラムを改良してみましょう。

さて、実行結果はどのようになるでしょうか。for構文の解説は後回しにします。私自身、結果が気になります。

チェック

1000回で計算したところ、以下のようになりました。

1:500 times
2:21 times
3:0 times
4:0 times
5:0 times
6:479 times

この結果はまったくダメですね。では一気に、1000000回に増やしてみます。

1:166271 times
2:166605 times
3:168428 times
4:165223 times
5:166650 times
6:166823 times

どうでしょうか。極めて精確なサイコロになっていることが分かります。ちなみに、コンピュータがしばらく止まってしまうのでみなさんは止めた方がいいと思いますが、もう一桁増やした結果がこれです。

1:1675831 times
2:1661733 times
3:1676612 times
4:1648896 times
5:1664367 times
6:1672561 times

実感として、もうあまり精度的に変化はありません。

いずれにせよ、この疑似サイコロは十分によいサイコロでした。回数が少ない場合はコンピュータの処理が早すぎて理論的な結果と大きく違ってしまいますが、回数が十分に多いと理論的な結果に近づきます。よって、この疑似サイコロは十分に精確なサイコロであると判定します。

なお、サイコロとは関係ありませんが、簡易的にベンチマークを測定した結果、100万回で約600ミリ秒でした。私のパソコンはノートですがCore2Duoなので、結構速いと思います。ベンチマークの測定方法はここでは脇道にそれすぎるので、自分で調べてみてください。DateTimeを使って簡易的に測定できます。

そろそろ本題に

疑似サイコロが十分によいと分かったところで、for構文の確認をします。forはifに比べて複雑ですが、覚えてしまえば簡単です。

for(初期化;判別式;加算・減算;)

forを使う際には回数を数えるための変数が必要です。上のプログラムではiがそれに当たります。「初期化」の部分ではその変数の最初の値を決定し、代入します。次に「判別式」を評価します。判別式はifのかっこの中の部分です。そして判別式が成立していたら、forの内容を実行します。その後、「加算・減算」処理を行い、再び判別式を評価して、というのを繰り返します。

iは普通は回数ですが、飛び飛びで繰り返す場合や減算する場合は実行回数とは異なります。例えば、「iが0から1000まで1ずつ増やせ」だったら、1001回繰り返されますが、「2ずつ増やせ」だったらその半分500回になります。ですが同じようにiは1000まで0,2,4,6という風にしてカウントされます。

上で用いている i++ は i = i + 1 という意味です。このような特殊な書き方はほかにもあります。

i = i + 1;
  (同じ)
i += 1;
  (同じ)
i++;

「+=」には「追加」の意味が含まれており、応用的なオブジェクト指向の機能の中でも使えるように拡張されていますが、ここでは数値の追加の意味で覚えてください。「++」はインクリメントと呼ばれ、iの前におくか後ろにおくかで少し違うのですが、その違いは自分で調べてみてください。「前置 後置 インクリメント」として検索すれば、きっと出てきます。

今回のプログラムに関する解説は以上です。次回はサイコロからちょっと離れて、while構文という繰り返し構文の実験をするために、ファイルの読み込みに挑戦することにします。