スポンサーリンク
※サイト運営にサーバーは必須です※
~このサイトもエックスサーバーを使用しています~
目次
はじめに
※追記:完成したアプリの紹介→同じ誕生日のペアがどのくらいいるかシミュレーション
ひと昔前、同じ誕生日の組がいくついるか数えるプログラムの方針という記事で誕生日のペアを求めるプログラムの方針を書いた。
……で、その記事では方針だけ書いたのがプログラム自体は書いていなかったので、なんとなく片手落ちな気がして、落ち着かなかった。
祝日を使って、なんとかプログラムが組めたので、この記事では、そのプログラムの具体的に紹介する。
紹介するのは、以下の6つのプログラム
- ある人数の集団の誕生日の組を求める [1ループ]
- ある人数の集団の誕生日の組(期待値)を求める [2ループ]
- 2人~特定の人数までの誕生日の組(期待値)を求める [3ループ]
うるう年を考慮したバージョンとそうでないバーションの2通りがある。
結果として3×2の6種類。
プログラムではfor文を最大1+3回使っている。
全てに共通するbに加えて、i、k、pを使用している
[1ループ]はi
[2ループ]はiとk
[3ループ]はiとkとpを使用している
使用している言語はC++。コンパイルする時は、g++を使えば問題ない。
(全てを確かめてはいないが、C言語としてgccを使ってコンパイルしても通ると思う)
※この記事を書いている人間は、大学時代にプログラムを少しかじった程度しか勉強していない
ある人数の集団の誕生日の組を求める [1ループ](うるう年なし)
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 |
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> int main() { //乱数(時間による変化) srand((unsigned)time(NULL)); //ファイルの出力準備 FILE *sf; sf = fopen("simulation.dat","w");//出力するファイルの名前を指定 //エラー if(sf==NULL){ printf("ファイルオープンエラー\n"); return -1; } int p_max=100;//人数 int b;//配列用の変数(birthday) int b_max=365; int day[b_max]={0}; int i; int i_max; i_max=p_max; for(i=1;i<=i_max;i++) { int r;//乱数(rand) r=(int)365.0*rand()/(RAND_MAX+1.0);//0から364の乱数を出す day[r]+=1; }//for(i)文の終わり int pair;//ペアの数を数える pair=0; for(b=0;b<b_max;b++) { if (day[b]>=2){//同じ誕生日の人が二人以上の場合カウント pair +=1; } printf("%d日目の人は%d人いる\n", b,day[b]); fprintf(sf,"%d日目の人は%d人いる\n", b,day[b]); } printf("同じ誕生日の組は%d組\n", pair); fprintf(sf,"同じ誕生日の組は%d組\n", pair); //sfクローズ fclose(sf); return 0; }//プログラムの終わり |
最も単純に、うるう年を抜いて考えたプログラム。
p_maxで人数を指定する。
day[b_max]で365個の配列を用意する。
その後、0~364の乱数を発生させる。
例えば、5が出たら、day[b_max]の内、上から5番目に+1する。
プログラムの結果は0~364日目で表示される。
1~365ではないが、両者とも365日分あるので、やっていることは本質的に同じ。
配列の中身が2以上の場合、同じ誕生日のペアがあるとカウントする
結果は「simulation.dat」に出力される。
例えば、p_max=100(人数が100人の時)の結果は以下のようになった
結果は、
0日目の人は2人いる
1日目の人は0人いる
2日目の人は1人いる
~(省略)~
360日目の人は1人いる
361日目の人は0人いる
362日目の人は0人いる
363日目の人は0人いる
364日目の人は0人いる
同じ誕生日の組は12組
乱数を生み出すときに、時間を参照しているので、実行するタイミングで結果は変わる。
三回ほどプログラムを走らせてみた結果
10組
15組
16組
このように一回しか試行していないので当然ながら結果がバラつく。
その問題を解決するには、このプログラムを何度も走らせて期待値をとる必要がある。
ある人数の集団の誕生日の組(期待値)を求める [2ループ](うるう年なし)
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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> int main() { //乱数(時間による変化) srand((unsigned)time(NULL)); //ファイルの出力準備 FILE *sf; sf = fopen("simulation.dat","w");//出力するファイルの名前を指定 //エラー if(sf==NULL){ printf("ファイルオープンエラー\n"); return -1; } int p_max=100;//集団の人数 int k;//試行回数 int k_max=1000; int pair_sum;//ペアのカウント用 pair_sum=0;//初期化 for(k=1;k<=k_max;k++){ int b;//配列用の変数(birthday) int b_max=365; int day[b_max]={0}; int i; int i_max; i_max=p_max; for(i=1;i<=i_max;i++) { int r;//乱数(rand) r=(int)365.0*rand()/(RAND_MAX+1.0);//0から364の乱数を出す day[r]+=1; }//for(i)文の終わり int pair;//ペアの数を数える pair=0; for(b=0;b<b_max;b++) { if (day[b]>=2){//同じ誕生日の人が二人以上の場合カウント pair +=1; } } pair_sum+=pair; printf("%d番目で同じ誕生日の組は%d組\n", k,pair); fprintf(sf,"%d番目で同じ誕生日の組は%d組\n", k,pair); }//for(k)文の終わり double pair_exp=(double)pair_sum/(double)k_max;//期待値を出す(expected_value) printf("試行回数(k_max)は%d\n", k_max); fprintf(sf,"試行回数(k_max)は%d\n", k_max); printf("%d人いる時のpair_expは%lf\n", p_max, pair_exp); fprintf(sf,"%d人いる時のpair_expは%lf\n", p_max, pair_exp); //sfクローズ fclose(sf); return 0; }//プログラムの終わり |
実質的に、一番最初に紹介したプログラムを何度も走らせて平均をとるプログラム。
例えば、p_max=100(人数が100人の時)で試行回数を1000回にした時の結果を示す
結果
1番目で同じ誕生日の組は9組
2番目で同じ誕生日の組は14組
3番目で同じ誕生日の組は13組
~(省略)~
998番目で同じ誕生日の組は13組
999番目で同じ誕生日の組は12組
1000番目で同じ誕生日の組は9組
試行回数(k_max)は1000
100人いる時のpair_expは11.414000
100人いる時の同じ誕生日のペアの期待値(pair_exp)は11.414000組だとわかる。
2人~特定の人数までの誕生日の組(期待値)を求める [3ループ](うるう年なし)
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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> int main() { //乱数(時間による変化) srand((unsigned)time(NULL)); //ファイルの出力準備 FILE *sf; sf = fopen("simulation.dat","w");//出力するファイルの名前を指定 //エラー if(sf==NULL){ printf("ファイルオープンエラー\n"); return -1; } fprintf(sf,"#人数\t同じ誕生日のペアの期待値(pair_exp)\n"); int p;//集団の人数 int p_max=3000; for(p=2;p<=p_max;p++){ int k;//試行回数 int k_max=10000; int pair_sum;//ペアのカウント用 pair_sum=0;//初期化 for(k=1;k<=k_max;k++){ int b;//配列用の変数(birthday) int b_max=365; int day[b_max]={0}; int i; int i_max; i_max=p; for(i=1;i<=i_max;i++) { int r;//乱数(rand) r=(int)365.0*rand()/(RAND_MAX+1.0);//0から364の乱数を出す day[r]+=1; }//for(i)文の終わり int pair;//ペアの数を数える pair=0; for(b=0;b<b_max;b++) { if (day[b]>=2){//同じ誕生日の人が二人以上の場合カウント pair +=1; } } pair_sum+=pair; }//for(k)文の終わり double pair_exp=(double)pair_sum/(double)k_max;//期待値を出す(expected_value) printf("%d人いる時のpair_expは%lf\n", p, pair_exp); fprintf(sf,"%d\t%lf\n", p, pair_exp); }//for(p)文の終わり //sfクローズ fclose(sf); return 0; }//プログラムの終わり |
上までの例は、100人と固定して、その時の誕生日のペアを求めてきた。
最後はこの100人の部分を動かしたい。
人数とそれに対応する誕生日のペア(期待値)が求められる。
以下、試行回数10000で3000人まで調べた時の結果。
#人数 同じ誕生日のペアの期待値(pair_exp)
2 0.002500
3 0.008800
4 0.017100
~(省略)~
2998 364.102600
2999 364.095500
3000 364.095700
ちなみに、ここまでforによるループがあると計算にかなり時間がかかる。
(この結果を出すのに2~3分は少なくともかかっている)
ここの部分の結果を詳しく知りたい場合は、人数VS誕生日が同じペア(組):うるう年なしバージョンをみてください
ある人数の集団の誕生日の組を求める [1ループ](うるう年あり)
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 71 72 73 74 |
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> int main() { //乱数(時間による変化) srand((unsigned)time(NULL)); //ファイルの出力準備 FILE *sf; sf = fopen("simulation.dat","w");//出力するファイルの名前を指定 //エラー if(sf==NULL){ printf("ファイルオープンエラー\n"); return -1; } int p_max=100000;//人数 int b;//配列用の変数(birthday) int b_max=365*4+1; int day[b_max]={0}; int i; int i_max; i_max=p_max; for(i=1;i<=i_max;i++) { int r;//乱数(rand) r=(int)b_max*rand()/(RAND_MAX+1.0);//0から365*4の乱数を出す day[r]+=1; }//for(i)文の終わり int pair;//ペアの数を数える pair=0; for(b=0;b<=365;b++) { if (b!=0){//b=0がうるう年に相当day[b]=day[b]で変わりない day[b]=day[b]+day[b+365]+day[b+730]+day[b+1095]; } if (day[b]>=2){//同じ誕生日の人が二人以上の場合カウント pair +=1; } printf("%d日目の人は%d人いる\n", b,day[b]); fprintf(sf,"%d日目の人は%d人いる\n", b,day[b]); } printf("同じ誕生日の組は%d組\n", pair); fprintf(sf,"同じ誕生日の組は%d組\n", pair); //sfクローズ fclose(sf); return 0; }//プログラムの終わり |
うるう年がないバージョンとの主な変更点は、
用意する配列をint b_max=365から365×4+1に変更し、
乱数もこれに合わせて増やし、0から365×4までの乱数を出す。
※追記:よくよく考えたら、発生させる乱数を0~365でなく、0~365.25で振って、365~365.25をうるう年に対応させると、用意する配列が少なく済む。
方針は0日目をうるう年と考えている。
それ以外の番号の時は
day[b]=day[b]+day[b+365]+day[b+730]+day[b+1095]のように考えている。
そして、
うるう年:day[0]=day[0]
それ以外:day[b]=day[b]+day[b+365]+day[b+730]+day[b+1095]
と割り当てることで、うるう年が4年に1くることに対応させる。
p_max=100000人いる時の結果を以下示す
0日目の人は63人いる
1日目の人は288人いる
2日目の人は270人いる
3日目の人は290人いる
~(省略)~
363日目の人は283人いる
364日目の人は284人いる
365日目の人は292人いる
同じ誕生日の組は366組
うるう年に相当する0日目が他の日の1/4程度になる。
ある人数の集団の誕生日の組(期待値)を求める [2ループ](うるう年あり)
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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> int main() { //乱数(時間による変化) srand((unsigned)time(NULL)); //ファイルの出力準備 FILE *sf; sf = fopen("simulation.dat","w");//出力するファイルの名前を指定 //エラー if(sf==NULL){ printf("ファイルオープンエラー\n"); return -1; } int p_max=100;//集団の人数 int k;//試行回数 int k_max=1000; int pair_sum;//ペアのカウント用 pair_sum=0;//初期化 for(k=1;k<=k_max;k++){ int b;//配列用の変数(birthday) int b_max=365*4+1; int day[b_max]={0}; int i; int i_max; i_max=p_max; for(i=1;i<=i_max;i++) { int r;//乱数(rand) r=(int)b_max*rand()/(RAND_MAX+1.0);//0から365*4の乱数を出す day[r]+=1; }//for(i)文の終わり int pair;//ペアの数を数える pair=0; for(b=0;b<=365;b++) { if (b!=0){//b=0がうるう年に相当day[b]=day[b]で変わりない day[b]=day[b]+day[b+365]+day[b+730]+day[b+1095]; } if (day[b]>=2){//同じ誕生日の人が二人以上の場合カウント pair +=1; } } pair_sum+=pair; printf("%d番目で同じ誕生日の組は%d組\n", k,pair); fprintf(sf,"%d番目で同じ誕生日の組は%d組\n", k,pair); }//for(k)文の終わり double pair_exp=(double)pair_sum/(double)k_max;//期待値を出す(expected_value) printf("試行回数(k_max)は%d\n", k_max); fprintf(sf,"試行回数(k_max)は%d\n", k_max); printf("%d人いる時のpair_expは%lf\n", p_max, pair_exp); fprintf(sf,"%d人いる時のpair_expは%lf\n", p_max, pair_exp); //sfクローズ fclose(sf); return 0; }//プログラムの終わり |
上で紹介したプログラムの組み合わせなので、特に述べることはない。
2人~特定の人数までの誕生日の組(期待値)を求める [3ループ](うるう年あり)
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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> int main() { //乱数(時間による変化) srand((unsigned)time(NULL)); //ファイルの出力準備 FILE *sf; sf = fopen("simulation.dat","w");//出力するファイルの名前を指定 //エラー if(sf==NULL){ printf("ファイルオープンエラー\n"); return -1; } fprintf(sf,"#人数\t同じ誕生日のペアの期待値(pair_exp)\n"); int p;//集団の人数 int p_max=3000; for(p=2;p<=p_max;p++){ int k;//試行回数 int k_max=100000; int pair_sum;//ペアのカウント用 pair_sum=0;//初期化 for(k=1;k<=k_max;k++){ int b;//配列用の変数(birthday) int b_max=365*4+1; int day[b_max]={0}; int i; int i_max; i_max=p; for(i=1;i<=i_max;i++) { int r;//乱数(rand) r=(int)b_max*rand()/(RAND_MAX+1.0);//0から365*4の乱数を出す day[r]+=1; }//for(i)文の終わり int pair;//ペアの数を数える pair=0; for(b=0;b<=365;b++) { if (b!=0){//b=0がうるう年に相当day[b]=day[b]で変わりない day[b]=day[b]+day[b+365]+day[b+730]+day[b+1095]; } if (day[b]>=2){//同じ誕生日の人が二人以上の場合カウント pair +=1; } } pair_sum+=pair; }//for(k)文の終わり double pair_exp=(double)pair_sum/(double)k_max;//期待値を出す(expected_value) printf("%d人いる時のpair_expは%lf\n", p, pair_exp); fprintf(sf,"%d\t%lf\n", p, pair_exp); }//for(p)文の終わり //sfクローズ fclose(sf); return 0; }//プログラムの終わり |
上で紹介したプログラムの組み合わせなので、特に述べることはない。
ここの部分の結果を詳しく知りたい場合は、人数VS誕生日が同じペア(組):うるう年ありバージョンをみてください
関連記事
※追記:完成したアプリの紹介→同じ誕生日のペアがどのくらいいるかシミュレーション
~プログラミングを勉強してみませんか?~
TechAcademy [テックアカデミー] は無料の体験講座が用意されているので、気軽に体験できます。
※私(サイト主)も無料体験講座を実際に受けてみました(→感想)
コメント