スポンサーリンク
※サイト運営にサーバーは必須です※
~ ロリポップ! はコスパのよい初心者向けサーバーです~
目次
はじめに
※目次用の記事:ギャンブルの賭け方の種類をまとめてみた
賭け方にはいくつか種類がある。
そのうちの一つに、ココモ法というのがある。
この記事では、ココモ法とはなにかを説明した後、プログラムによるシミュレーション結果を示す。
ココモ法とは
最小の賭け金額(例えば、1)を自分で設定する。
負けた場合:
(直前に1回だけ負けている):賭け金は、1
(直前に2回以上負けている):1つ前の勝負で賭けていたお金の分だけ、賭け金に加える
※(今回の賭け金)=(前回の賭け金)+(前々回の賭け金)に従う
(直前に1回だけ負けている)場合は(前々回の賭け金)=0と考えている
勝った場合:次の勝負の賭け金を、1(最小の賭け金額)に戻す
※ココモ法は、戻り金が3倍のゲームを想定している
ココモ法の具体例
ルールだけ、説明してもイメージがつかめないと思うので、具体例を挙げる。
勝率が1/3で払戻金が3倍の勝負をイメージしてください。
9連敗した後、10回目で勝利した場合を考える。
勝負回数 | 賭け金 | 累計賭け金 | 利益 |
1 | 1 | 1 | 2 |
2 | 1 | 2 | 1 |
3 | 2 | 4 | 2 |
4 | 3 | 7 | 2 |
5 | 5 | 12 | 3 |
6 | 8 | 20 | 4 |
7 | 13 | 33 | 6 |
8 | 21 | 54 | 9 |
9 | 34 | 88 | 14 |
10 | 55 | 143 | 22 |
連敗したら、以下のテーブルのように賭け金を
1→1→2→3→5→8→13→21→34→55と増やしていく
10回目の累計賭け金額は148となるが、10回目で勝利すれば、55×3-148=+22だけ利益が得られる。
※この数字を見た時、高校で習ったフィボナッチ数列がフラッシュバックするのは私だけだろうか……
もしも、払戻金が2倍だったら……
ココモ法は払い戻し3倍の時に使用されることを想定されているが、2倍の場合どのようになるか考えてみる
勝負回数 | 賭け金 | 累計賭け金 | 利益 |
1 | 1 | 1 | 1 |
2 | 1 | 2 | 0 |
3 | 2 | 4 | 0 |
4 | 3 | 7 | -1 |
5 | 5 | 12 | -2 |
6 | 8 | 20 | -4 |
7 | 13 | 33 | -7 |
8 | 21 | 54 | -12 |
9 | 34 | 88 | -20 |
10 | 55 | 143 | -33 |
1連敗と2連敗の時の利益が0でそれ以上連敗すると、最終的な獲得金額はマイナスになる。
3連敗以上することがほんどないといえるのなら有効な手法なのかもしれない……(実際にプログラムを走らせると当然ながら、3連敗以上することはザラにある)
ココモ法の特徴
- 主に払い戻し3倍の時に使用
- 連敗しても、最後に勝てば、今まで負けていた分のお金を取り戻せる上に、利益を上乗せすることができる。そういう意味で、マーチンゲール法(主に払い戻し2倍の時に使用)みたいなところがある。
- 前々回の勝負に使用した賭け金額を覚えておかないといけない分、少し面倒くさい……実際に使うという観点でも、プログラム的な観点でも。(マーチンゲール法やパーレー法と比べて、宣言しないといけない変数は増える)
ココモ法の検証
以下では2種類のプログラムを紹介する。
言語はC++で作ったが、C言語としてコンパイルしても動くと思う
※このプログラムを組んだ人間は、大学時代に少しプログラムをかじった程度の戦闘能力しかない
ココモ法個別シミュレーション
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 |
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> int main() { int i=0; int i_max=100000;//勝負回数 double w=3.0; //賭け金の変換率(2倍以上)(win) double p=33.333; //勝率(50%以下)(probability) double m_min=1;//最小の賭け金額(money) double m_ini=1000;//最初の手持ちの資金(money_initial) int c;//勝ち負け差のカウント(count) c=0;//初期化 //乱数(時間による変化) srand((unsigned)time(NULL)); //ファイルの出力準備 FILE *sf; sf = fopen("simulation.dat","w");//出力するファイルの名前を指定 //エラー if(sf==NULL){ printf("ファイルオープンエラー\n"); return -1; } //初期条件の表示 printf("#倍率は%lf\t勝率は%lf\t最小の賭け金は%lf\t手持ちのお金は%lf\n",w,p,m_min,m_ini); fprintf(sf,"#倍率は%lf\t勝率は%lf\t最小の賭け金は%lf\t手持ちのお金は%lf\n",w,p,m_min,m_ini); double m_now;//現在の資金 double m_bet;//賭け金 //初期化 m_now=m_ini;//最初は最初の手持ちの資金でスタート m_bet=m_min;//賭け金は最小の賭け金額でスタート double m_bet1;//ココモ法において一つ前の回のかけ金を参照する用 m_bet1=0;// double m_bet_tem;//m_bet_temに一時的(temporary)に今回の賭け金を覚えさせる用 for(i=1;i<=i_max;i++) { double r;//乱数(rand) r=100.0*rand()/(RAND_MAX+1.0);//0から100の乱数を出す printf("%d回目\t賭け金は%lf\t",i,m_bet); fprintf(sf,"%d回目\t賭け金は%lf\t",i,m_bet); if (p<=r){//負けの場合 m_now-=m_bet; m_bet_tem=m_bet;//m_temに一時的(temporary)に今回の賭け金を覚えさせる m_bet=m_bet+m_bet1;//今回と一つ前のかけ金の足し合わせ m_bet1=m_bet_tem; c-=1; printf("lose\t"); fprintf(sf,"lose\t"); } else{//勝ちの場合 m_now +=m_bet*(w-1.0); m_bet=m_min; m_bet1=0; c+=1; printf("win\t"); fprintf(sf,"win\t"); } printf("勝敗差%d\t手持ちのお金は%lf\n",c,m_now); fprintf(sf,"勝敗差%d\t手持ちのお金は%lf\n",c,m_now); if(m_now<m_bet){//手持ちの金より賭けるお金が高い場合 printf("%d回目で手持ちのお金が足りません\n",i); fprintf(sf,"%d回目で手持ちのお金が足りません\n",i); break; } }//for(i)文の終わり //sfクローズ fclose(sf); return 0; }//プログラムの終わり |
プログラムで使用している変数の説明
※マーチンゲール法(2倍賭け)の破綻までのシミュレーションで紹介したプログラムと同じ変数の置き方をしている
i_max:勝負の回数
w:賭けたお金の戻る倍率を指定。ここでは3倍を指定
p:勝率を指定。ここでは1/3の確率なので33.333を指定。
※pの値は、個別シミュレーションでは、33.333、期待値シミュレーションでは33.3333を使用。3の数が違うのに特に意味はない
m_min:かけ金を指定
m_ini:最初の資金を指定
※ここではm_min=1でm_ini=1000と、最小の賭け金の1000倍を所持していると考えている。
例えば、最小の賭け金が1000円(千円)だとするなら、手元の軍資金は、1000000円(百万円)。割と現実的な設定だと思われる。
r:乱数(0~100の乱数)
※乱数rが勝率pより下の数で収まるなら勝ちの判定がでる。
そして、勝負を続けていくうちに、負け続けることもあるだろう。
そしてついに、賭けしようにも手持ちのお金が足りなくなるかもしれない。
この場合、これ以上勝負ができなくなり、プログラムは終了する。(破綻判定)
つまり、借金はNG
c:トータルの勝ち負けの差がどのくらいあるか調べる用 。
※ココモ法で、新たに宣言した変数
m_bet1:一つ前の勝負の賭け金額を記憶する(最初は0)
m_bet_tem:今回の賭け金額を一時的(temporary)に記憶させる用
実際にプログラムを走らせると以下のような結果となる
勝負回数i_maxは100000(10万回)までとする
#倍率は3.000000 勝率は33.333000 最小の賭け金は1.000000 手持ちのお金は1000.000000
1回目 賭け金は1.000000 lose 勝敗差-1 手持ちのお金は999.000000
2回目 賭け金は1.000000 lose 勝敗差-2 手持ちのお金は998.000000
3回目 賭け金は2.000000 lose 勝敗差-3 手持ちのお金は996.000000
(省略)
1771回目 賭け金は377.000000 lose 勝敗差-611 手持ちのお金は2056.000000
1772回目 賭け金は610.000000 lose 勝敗差-612 手持ちのお金は1446.000000
1773回目 賭け金は987.000000 lose 勝敗差-613 手持ちのお金は459.000000
1773回目で手持ちのお金が足りません
1774回目に必要な賭け金額は610+987=1597
1773回目終了時に持っていたお金である459を超えてしまったため、勝負不能という判定がでた。
勝負差が-613と大きな負の値を持のは、この勝負は勝率33.333%だと考えているため。
勝負を続ければ、全体の1/3が勝利で、全体の2/3が敗北する。
まとめると全体の勝負回数の-1/3の勝敗差になっていることが期待される
勝敗差は、-613となっているのは1773/3を考えるとそこまでおかしくない
※srand((unsigned)time(NULL));の部分で時間を参照した上で乱数を発生させている。そのため、実行するタイミングで結果が変化する。
ココモ法の期待値シミュレーション
上のプログラムを何度も走らせて期待値や破綻する率をもとめたい
そのためのプログラムは以下のようになる
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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> int main() { int k; int k_max=100000;//試行回数 int i=0; int i_max=100000;//勝負回数 double w=3.0; //賭け金の変換率(2倍以上)(win) double p=33.3333; //勝率(50%以下)(probability) double m_min=1;//最小の賭け金額(money) double m_ini=1000;//最初の手持ちの資金(money_initial) int b;//破たん回数のカウント(Bankruptcy) b=0;//初期化 int i_sum;//勝負回数の合計 double m_sum;//最終的なお金の合計 i_sum=0;//初期化 m_sum=0.0; int c_sum;//勝負差の合計 c_sum=0;//初期化 //乱数(時間による変化) srand((unsigned)time(NULL)); //ファイルの出力準備 FILE *sf; sf = fopen("simulation.dat","w");//出力するファイルの名前を指定 //エラー if(sf==NULL){ printf("ファイルオープンエラー\n"); return -1; } //初期条件の表示 printf("#倍率は%lf\t勝率は%lf\t最小の賭け金は%lf\t手持ちのお金は%lf\n",w,p,m_min,m_ini); fprintf(sf,"#倍率は%lf\t勝率は%lf\t最小の賭け金は%lf\t手持ちのお金は%lf\n",w,p,m_min,m_ini); for(k=1;k<=k_max;k++) { int i_count;//勝負回数のカウント i_count=0; double m_now;//現在の資金 double m_bet;//賭け金 //初期化 m_now=m_ini;//最初は最初の手持ちの資金でスタート m_bet=m_min;//賭け金は最小の賭け金額でスタート double m_bet1;//ココモ法において一つ前の回のかけ金を参照する用 m_bet1=0;// double m_bet_tem;//m_bet_temに一時的(temporary)に今回の賭け金を覚えさせる用 int c;//勝ち負け差のカウント(count) c=0;//初期化 for(i=1;i<=i_max;i++) { double r;//乱数(rand) r=100.0*rand()/(RAND_MAX+1.0);//0から100の乱数を出す if (p<=r){//負けの場合 m_now-=m_bet; m_bet_tem=m_bet;//m_temに一時的(temporary)に今回の賭け金を覚えさせる m_bet=m_bet+m_bet1;//今回と一つ前のかけ金の足し合わせ m_bet1=m_bet_tem; c-=1; } else{//勝ちの場合 m_now +=m_bet*(w-1.0); m_bet=m_min; m_bet1=0; c+=1; } i_count=i;//i_countを定義しないと破たんしない(セーフ)場合、iと値が1ずれてしまうため if(m_now<m_bet){//手持ちの金より賭けるお金が高い場合 break; } }//for(i)文の終わり if(i_count<i_max) { printf("%d番目%d回目で破たん\t手持ちのお金は%lf\n",k,i_count,m_now); fprintf(sf,"%d番目%d回目で破たん\t手持ちのお金は%lf\n",k,i_count,m_now); b=b+1; } else{ printf("%d番目\t%d回目までセーフ\t手持ちのお金は%lf\n",k,i_count,m_now); fprintf(sf,"%d番目\t%d回目までセーフ\t手持ちのお金は%lf\n",k,i_count,m_now); } i_sum=i_sum+i_count; m_sum=m_sum+m_now; c_sum=c_sum+c; }//for(k)文の終わり //破たん率 printf("勝負回数は%d\n",i_max); printf("試行回数は%d\n",k_max); printf("破たん回数は%d\n",b); fprintf(sf,"勝負回数は%d\n",i_max); fprintf(sf,"試行回数は%d\n",k_max); fprintf(sf,"破たん回数は%d\n",b); double b_pro= (double)b/(double)k_max*100.0;//(double)を抜くとb/k_maxがゼロと評価されてしまう printf("破たん率は%lf%\n",b_pro); fprintf(sf,"破たん率は%lf%\n",b_pro); double i_exp=(double)i_sum/(double)k_max; double m_exp=(double)m_sum/(double)k_max; printf("破たんするまでに行える勝負回数の期待値(i_exp)は%lf\n",i_exp); printf("破たんする直前で持っているお金の期待値(m_exp)は%lf\n",m_exp); fprintf(sf,"破たんするまでに行える勝負回数の期待値(i_exp)は%lf\n",i_exp); fprintf(sf,"破たんする直前で持っているお金の期待値(m_exp)は%lf\n",m_exp); double c_exp=(double)c_sum/(double)k_max; printf("破たんした時の勝負差の期待値は%lf\n",c_exp); fprintf(sf,"破たんした時の勝負差の期待値は%lf\n",c_exp); //sfクローズ fclose(sf); return 0; }//プログラムの終わり |
プログラムで使用している変数の説明
b_pro:破綻率
※ここでいう破綻率とは手持ちのお金が、賭けに耐えられない状況を意味する。借金してお金を用意することはできないとする。
i_exp: 破綻するまで何回勝負ができるかの期待値
m_exp: 勝負が終わった段階で持っているお金の期待値
k_max:試行回数。
※k_maxを大きくすればするほど、正確な期待値が求まる。
c_exp:勝敗差の期待値
以下では、k_max=100000、i_max=100000で指定。
試行回数10万回、勝負回数10万回。
結果は以下のようになる
勝負回数は100000
試行回数は100000
破たん回数は99903
破たん率は99.903000%
破たんするまでに行える勝負回数の期待値(i_exp)は1930.530130
破たんする直前で持っているお金の期待値(m_exp)は989.007780
破たんした時の勝負差の期待値は-643.516070
破綻率は99.903%。この破綻率と一番近いのはグランマーチンゲール(α=1)だろう。
※参考:グランマーチンゲール法の破綻率は99.874%ほどで、勝負回数の期待値(i_exp)は2049.226230
ココモ法とグランマーチンゲール法には、
- 勝った時に今まで負けてきた分の損を取り返すことができる
- 連敗していればしているほど、勝った時の利益が大きくなる
という共通点があるので納得がいく
※ちなみに、払い戻し2倍の場合の結果も載せておく
勝負回数はオーバーフローする可能性を考慮して、2万回
勝負回数は100000
試行回数は20000
破たん回数は16073
破たん率は80.365000%
破たんするまでに行える勝負回数の期待値(i_exp)は39382.323450
破たんする直前で持っているお金の期待値(m_exp)は999.816450
破たんした時の勝負差の期待値は1.652450
当然ながら、破綻する率の値は小さくなる
書籍の紹介
『ギャンブルの必勝法が本当に儲かるかプログラミングで検証してみた』は以下の疑問にお答えします。
●勝率が50%の場合、利益を生み出す必勝法は存在するか?
●勝率が60%の場合、どのように賭けるのが最適か?
必勝法と思われている手法を15種類紹介します。必勝法には、例えば、マーチンゲール法(負けた時に2倍賭ける手法)などがあります。これらの手法が本当に儲かるかプログラミングを使用して検証します。また、検証するために必要なプログラミングの知識(C#)も紹介しています。
ギャンブルの必勝法が本当に儲かるかプログラミングで検証してみた
関連記事
※目次用の記事:ギャンブルの賭け方の種類をまとめてみた
~ギャンブルに絶対儲かる必勝法があるのだろうか?~
私(サイト主)はこの疑問に対して非常に興味を持ち、プログラミングで検証してみました。
このサイトを応援してもいいかなと思う人はぜひとも購入を検討してみてください。
コメント