コードで数え上げる
確率の問題を考える
コインを50回投げて表が連続8回、裏が連続8回と続けて出る確率は幾らか?
まず、問題の規模を小さくして考え易くする
コインを6回投げて表表裏裏と続けて出る確率は幾らか?
と言った風に考える
PCとプログラムコードを利用して問題解決を試みる
まず実際にその事象の数を「数え上げて」みる
表をHead、'H'、裏をTail、'T'の一文字に抽象化して、その順列をコードで出力する
この出力に対して希望する条件に見合った事象を検索に掛け、その数を数え上げる
連続で変化する試行回数に沿って、その数の変化を観察し、その性質を見抜く
その上で、見抜いた性質に対し未来予測を試みて、実際にプログラムコードでシミュレーションし
その考えが正しいか否かを確認する。この一連の工程を通して計算した確率が「確からしい」事を確認する
(アプローチとしては理科の実験のように実際に体験していく形になっている)
問題を小さくして考える
まず、コインを4回投げた状態の順列を出力する。この場合、表表裏裏と言う事象は"HHTT"で表され、その順列は16事象中1事象となる筈だ。それを確認してみる。全体の順列を出力するプログラムコードは以下となる。このコードは帰納の考えを利用している。つまり数学的帰納による証明が行い易く、微積分に考えを転用しやすい。そのために帰納で作ったとも言えるし、運用性の点から帰納で作ったとも言える
Perm.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
-
|
-
|
|
|
|
|
-
!
|
|
!
|
-
!
-
|
|
|
|
-
|
|
|
!
|
|
!
|
-
!
-
|
|
-
|
-
|
|
|
!
!
-
|
!
!
|
-
|
!
-
!
-
|
-
|
-
|
!
!
!
!
| using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
public class Perm : MonoBehaviour
{
void Start()
{
int length = 4;
char[] set = { 'H', 'T' };
List<string> strList = Root(set, length);
string folder = Application.dataPath; SaveText(folder, @"\順列.txt", strList.ToArray());
}
List<string> Root(char[] set, int length)
{
List<string> strList = new List<string>();
int level = 0;
for (int i = 0; i < set.Length; i++)
{
char[] dat = new char[length];
dat[0] = set[i];
Loop(dat, level, set, strList);
}
return strList;
}
void Loop(char[] dat, int level, char[] set, List<string> strList)
{
level++;
if (level < dat.Length) {
for (int i = 0; i < set.Length; i++)
{
char[] cloneDat = (char[])dat.Clone(); cloneDat[level] = set[i];
Loop(cloneDat, level, set, strList); }
}
else {
strList.Add(new string(dat));
}
}
public void SaveText(string fileFolder, string filename, string[] dataStr)
{
using (StreamWriter w = new StreamWriter(fileFolder + filename, false, System.Text.Encoding.GetEncoding("shift_jis")))
{
foreach (var item in dataStr)
{
w.WriteLine(item);
}
}
}
}
|
これに対する出力ファイルの内容は以下となっている。VisualStudioでこのテキストファイルを開く。Ctrl+fで検索ウインドウを開き正規表現を使用するモードにして「HHTT」を検索すると、一つ見つける事が確認できる(正規表現(regex)モードにすると複数のラインが条件を満たした場合、複数のラインをマークする)
HHHH
HHHT
HHTH
HHTT
HTHH
HTHT
HTTH
HTTT
THHH
THHT
THTH
THTT
TTHH
TTHT
TTTH
TTTT
ここでコインを4回連続で投げた場合、全体16、表表裏裏(HHTT)が出る事象は1という事で確率が1/16である事が分かる。続けてコインを投げる回数を増やして、それぞれの回数でHHTTが出てくる回数を記録していく。投げる回数はコード内11行目、Start関数内のlengthの値を変更することで出来る
この4のところを5,6,7,...と変更して検索を掛け回数を記録する。5を指定すると以下になる
ある程度の回数まで進むと手動で検索するのが面倒になってくる
そこでコードを書いてプログラムに検索させる
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
-
|
|
|
|
|
-
!
|
|
-
!
|
|
|
|
!
| void Start()
{
int length = 7;
char[] set = { 'H', 'T' };
List<string> strList = Root(set, length);
string folder = Application.dataPath; SaveText(folder, @"\順列.txt", strList.ToArray());
var queary = strList.AsQueryable().Where(item => item.Contains("HHTT"));
var queList = queary.ToList();
queList.Add("検索前総数" + strList.Count().ToString());
queList.Add("カウント数" + (queList.Count-1).ToString());
SaveText(folder, @"\HHTT抜き出し.txt", queList.ToArray());
}
|
これを16回ぐらいまで記録して以下のような表を作った
4回 | 5回 | 6回 | 7回 | 8回 | 9回 | 10回 | 11回 | 12回 | 13回 | 14回 | 15回 | 16回 |
1/16 | 4/32 | 12/64 | 32/128 | 79/256 | 186/512 | 424/1024 | 944/2048 | 2065/4096 | 4456/8192 | 9512/16384 | 20128/32768 | 42287/65536 |
これを分母を揃えて数列にすると
\(\frac { 4096 }{ 65536 } ,\frac { 8192 }{ 65536 } ,\frac { 12288 }{ 65536 } ,\frac { 16384 }{ 65536 } ,\frac { 20224 }{ 65536 } ,\frac { 23808 }{ 65536 } ,\frac { 27136 }{ 65536 } ,\frac { 30208 }{ 65536 } ,\frac { 33040 }{ 65536 } ,\frac { 35648 }{ 65536 } ,\frac { 38048 }{ 65536 } ,\frac { 40256 }{ 65536 } ,\frac { 42287 }{ 65536 } \)
となる。この分子の変化の様子を観察すると4~7回目まで4096の等差数列だが、8回目から妙なズレが生じ始める。この性質を見抜きたいが、その前にこの値が正しい確率を導くものなのか、その根拠を確かめておく
実際にコンピューター内で50%ずつ表裏がランダムに出るコインを指定回数投げてHHTTが出た回数を数える。このコードは以下になる
niji_prob.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
-
|
|
-
|
|
|
|
|
|
|
|
-
|
|
|
-
|
|
!
|
!
!
|
-
!
-
|
|
|
!
|
-
!
-
|
-
|
!
|
!
|
-
!
-
|
|
!
|
!
| using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class niji_prob : MonoBehaviour
{
void Start()
{
int sampling = 10000;
int tossCount = 4;
float headPercentage = 0.5f;
string hitString = "HHTT";
char[] coinList = new char[tossCount];
for (int j = 0; j < 5; j++)
{
int successCount = 0;
for (int i = 0; i < sampling; i++)
{
coinList = GetCoinTossTest(coinList, headPercentage);
if (new string(coinList).Contains(hitString)) successCount++; }
print((double)successCount / (double)sampling);
}
}
private void SimpleMethod(float headPercentage, string hitString, char[] coinList)
{
coinList = GetCoinTossTest(coinList, headPercentage);
print(new string(coinList));
print(new string(coinList).Contains(hitString));
}
public char[] GetCoinTossTest(char[] coinList, float headPercentage)
{
for (int i = 0; i < coinList.Length; i++)
{
coinList[i] = TossCoin(headPercentage);
}
return coinList;
}
public char TossCoin(float headPercent)
{
if (Random.value < headPercent) return 'H';
return 'T';
}
}
|
計算通りであるならばコインを4回投げた場合、1/16=0.065となる筈。おおよそその辺り周辺の値をフラフラしていれば、ほぼ正しいと考えられるだろう。出力結果は以下となった
ほぼ予想通り
0.0642
0.0652
0.06
0.0619
0.0606
続けて12行目の
を「5」に変更してコインを投げる回数を変えて、確率があっているかを確認する。同様に回数を例えば32や72などユニークな値にして値が合うかどうかを確認する
問題ない様だ
性質
数列を観察し以下の性質を推理した
\(\begin{matrix} 4回目 & \frac { 1 }{ 16 } \\ 5回目 & \frac { 2 }{ 16 } \\ 6回目 & \frac { 3 }{ 16 } \\ 7回目 & \frac { 4 }{ 16 } \\ 8回目 & \frac { 5 }{ 16 } -\frac { 1 }{ 256 } \\ 9回目 & \frac { 6 }{ 16 } -\frac { 3 }{ 256 } \\ 10回目 & \frac { 7 }{ 16 } -\frac { 6 }{ 256 } \\ 11回目 & \frac { 8 }{ 16 } -\frac { 10 }{ 256 } \\ 12回目 & \frac { 9 }{ 16 } -\frac { 15 }{ 256 } +\frac { 1 }{ 4096 } \\ 13回目 & \frac { 10 }{ 16 } -\frac { 21 }{ 256 } +\frac { 4 }{ 4096 } \\ 14回目 & \frac { 11 }{ 16 } -\frac { 28 }{ 256 } +\frac { 10 }{ 4096 } \\ 15回目 & \frac { 12 }{ 16 } -\frac { 36 }{ 256 } +\frac { 20 }{ 4096 } \\ 16回目 & \frac { 13 }{ 16 } -\frac { 45 }{ 256 } +\frac { 35 }{ 4096 } -\frac { 1 }{ 65536 } \\ \vdots & \vdots \end{matrix}\)
性質を見出すいくつかの方法
- グラフを視覚的に確認して、その描く線の軌跡が「なんの関数グラフに似ているか」で推測を立てる
- 階差数列を計算して、その値の変異を観察し推測する
ここでgoogleスプレッドシートを利用して4重に階差数列を導き出し、その値の変化を観察して、その性質を見出した
以下のリンクを参照してほしい
googleスプレッドシートを利用して作った階差数列の表
左隣のひとつ前のセルと次のセルを関数で引算して差を計算して等差数列を導き出している。それを4層に渡り繰り返している
このようにマイクロソフトのエクセルやグーグルのスプレッドシートの機能を利用することで比較的短時間に数列の性質を見抜くことができる
ここから数式に直したり、その仕組みを調べて考える行動に移すことができる
では、これを数式に直し「この考えが正しいかどうか」を検証する