スポンサーリンク
※サイト運営にサーバーは必須です※
~このサイトもエックスサーバーを使用しています~
目次
はじめに
※目次用の記事:ギャンブルの賭け方の種類をまとめてみた
私が小学生の頃、テレビで絶対勝てる賭けの必勝法として、マーチンゲール法(後で解説)を紹介していた。
やりかたとしては、競馬で、単勝2倍以上の馬券を買う。負けた場合は、それまでの倍のお金を賭けるだけ。
マーチンゲール法がどうやって儲かるか理解した後、当時、小さかった私は、
「マーチンゲール法、スゲー。絶対もうかるじゃん」
と思った。
そして、頭の中で、馬券を買って、お金がウハウハな状態の自分をイメージした。
しかし、隣でテレビを見ていた親父がボソッと
「マーチンゲール法は絶対に破綻する。あれは無限の資金を仮定しているから成り立つのだ」
とつぶやいた。
親父のいうことはだいたい信じていた私は、それを聞いて、頭の中で思い描いていたリッチな自分のイメージが急速にしぼんでいった……
この記事では、マーチンゲール法(2倍賭け)の仕組みと、そのシミュレーション用プログラムを紹介。シミュレーションの結果としてなぜ、マーチンゲール法(2倍賭け)が破綻するが説明する
※親父に言われたことが完全に納得できず、どこかであきらめきれなかったともいえる。
マーチンゲール法とは
勝率が50%で払戻金が2倍の勝負を基本的に想定しており、以下のルールに従って賭ける
最小の賭け金額を自分で設定し、最初はこの最小の賭け金額からスタートする
勝った場合:次の勝負の賭け金は、設定した最小の賭け金額に戻す
負けた場合:次の勝負の賭け金は、現在賭けている2倍の金額にする
マーチンゲール法の具体例
ルールだけ、説明してもなぜこれが儲かるかイメージがつかめないと思うので、具体例を挙げる。
勝率が50%で払戻金が2倍の勝負として、コインの表裏の賭けなどをイメージしてください。
例えば、5回連続で負けて、6回目で勝利する時を考えると以下のようになる
勝負 | 勝敗 | 賭け金 | 持ち金 |
1 | × | 1 | -1 |
2 | × | 2 | -3 |
3 | × | 4 | -7 |
4 | × | 8 | -15 |
5 | × | 16 | -31 |
6 | ○ | 32 | 1 |
負けている場合は、2倍で賭けていくので、
1→2→4→8→16
と増やしていく
この時点で、最初の持ち金から-31になっている。
しかし、6回目で勝つことで、+32される。
最終的に、32-31=+1が残り、最初の持ち金と比べ+1される。
このように、負け続けていても、勝てば、今までの負債が一気に解消される賭け方となっている。
マーチンゲール法の検証
マーチンゲール法は、無限の資金力があれば、必勝法といえるだろう。
しかし、実際には、有限のお金しか手元にない。
有限の資金を仮定した場合、マーチンゲール法で儲かるか検証する。
以下では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 |
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> int main() { int i=0; int i_max=100000;//勝負回数 double w=2.0; //賭け金の変換率(2倍以上)(win) double p=50; //勝率(50%以下)(probability) double m_min=1;//最小の賭け金額(money) double m_int=1000;//最初の手持ちの資金(money_initial) //乱数(時間による変化) 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_int); fprintf(sf,"#倍率は%lf\t勝率は%lf\t最小の賭け金は%lf\t手持ちのお金は%lf\n",w,p,m_min,m_int); double m_now;//現在の資金 double m_bet;//賭け金 //初期化 m_bet=m_min;//最初は最小の賭け金でスタート m_now=m_int;//最初は最初の手持ちの資金でスタート 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_now-m_bet; m_bet=m_bet*2;//2倍賭け } else{//勝ちの場合 m_now =m_now+m_bet*(w-1.0); m_bet =m_min;//賭け金を最小額に戻す } printf("%d回目\t賭け金は%lf\t手持ちのお金は%lf\n",i,m_bet,m_now); fprintf(sf,"%d回目\t賭け金は%lf\t手持ちのお金は%lf\n",i,m_bet,m_now); if(m_now<m_bet){//手持ちの金より賭けるお金が高い場合 printf("%d回目で手持ちのお金が足りません\n",i); fprintf(sf,"%d回目で手持ちのお金が足りません\n",i); break; } }//for(i)文の終わり //sfクローズ fclose(sf); return 0; }//プログラムの終わり |
i_maxで勝負の回数を指定
wでは、賭けたお金の戻る倍率を指定。マーチンゲール法なのでここでは2倍を指定
pで勝率を指定、ここでは半々の確率なので50。
m_minでかけ金を指定。m_intで手持ちの軍資金を指定
※初期(initial)のスぺルを自分が勘違いしていたことに後から気が付いた。だが、プログラムと記事を今からすべて手直しするのはしんどいので、時間があったら、宣言する変数の名前をm_intからm_iniに書き替えたい。……文章を書いている人間の英語力の低さがばれて、地味に恥ずかしい。
ここではm_min=1でm_int=1000と、最小の賭け金の1000倍を所持していると考えている。
例えば、最小の賭け金が1000円(千円)だとするなら、手元の軍資金は、1000000円(百万円)。割と現実的な設定だと思われる。
0~100の乱数rを振って、乱数rが勝率pより下の数で収まるなら勝ちの判定がでる。
イメージそしては勝率が30だった場合、乱数をふって5、12、29のような30より下回れば、勝ち。(p>r )31、45、62、98のように30以上だった場合は負け判定が出る。(p<=r)
そして、勝負を続けていくうちに、負け続けることもあるだろう。
そしてついに、2倍賭けしようにも手持ちのお金が足りなくなるかもしれない。
この場合、これ以上勝負ができなくなり、プログラムは終了する。(破綻判定)
つまり、借金はNG
実際にプログラムを走らせると以下のような結果となる
勝負回数i_maxは100000(10万回)までとする
※ここで、表示される「賭け金」の意味は、「次の勝負に必要な賭け金」という意味で使われている。(ちょっとわかりづらいかも……)
#倍率は2.000000 勝率は50.000000 最小の賭け金は1.000000 手持ちのお金は1000.000000
1回目 賭け金は1.000000 手持ちのお金は1001.000000
2回目 賭け金は2.000000 手持ちのお金は1000.000000
3回目 賭け金は1.000000 手持ちのお金は1002.000000
4回目 賭け金は1.000000 手持ちのお金は1003.000000
~(省略)~
1189回目 賭け金は1.000000 手持ちのお金は1619.000000
1190回目 賭け金は1.000000 手持ちのお金は1620.000000
1191回目 賭け金は2.000000 手持ちのお金は1619.000000
1192回目 賭け金は1.000000 手持ちのお金は1621.000000
1193回目 賭け金は2.000000 手持ちのお金は1620.000000
1194回目 賭け金は4.000000 手持ちのお金は1618.000000
1195回目 賭け金は8.000000 手持ちのお金は1614.000000
1196回目 賭け金は16.000000 手持ちのお金は1606.000000
1197回目 賭け金は32.000000 手持ちのお金は1590.000000
1198回目 賭け金は64.000000 手持ちのお金は1558.000000
1199回目 賭け金は128.000000 手持ちのお金は1494.000000
1200回目 賭け金は256.000000 手持ちのお金は1366.000000
1201回目 賭け金は512.000000 手持ちのお金は1110.000000
1202回目 賭け金は1024.000000 手持ちのお金は598.000000
1202回目で手持ちのお金が足りません
srand((unsigned)time(NULL));の部分で時間を参照した上で乱数を発生させている。
そのため、実行するタイミングで結果が変化する。
あと4回ほど実行してみたが以下のようになった
2282回目で手持ちのお金が足りません
6036回目で手持ちのお金が足りません
1407回目で手持ちのお金が足りません
8972回目で手持ちのお金が足りません
このようにことごとく、途中で勝負ができなくなる
※プログラムの結果は「simulation.dat」に出力されるように組まれている。
プログラムを組んで、悩んだ点
以下の2点をどうするか微妙に迷った
(そして、きちんと処理できているか自信があまりない)
- 負け判定の時、「p<=r」にするべきか、それとも「p<r」するべきか
- 乱数はr=100.0*rand()/(RAND_MAX+1.0)にするかr=100.0*rand()/RAND_MAXにするか
ぶっちゃけ、この差が結果に大きな影響は与えないのだが、ちょっとまじめに考えた。(こういう時は、極端な例を考えるに限る)
まず、pがゼロの場合(勝率が0%)
rが万が一0を取った場合p<rだと、必ず負けるはずなのに、うまく負け判定がでないかもしれない。
p<=rで負け判定にする
p<=rの状況で、pが100(勝率が100%)の場合で、rが万が一100を取った時を考える。
このとき、勝率が100%なのに負け判定されてしまう。
rがあらかじめ100をぎりぎり取らないようにr=100.0*rand()/(RAND_MAX+1.0)にしておく
マーチンゲール法の期待値シミュレーション
上で紹介したマーチンゲール法の個別シミュレーションだと、
「たまたま運が悪くて負けただけでは?」
と思われるかもしれない。
そのためには、上のプログラムを何度も走らせて期待値や破綻する率をもとめたい
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 |
#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=2.0; //賭け金の変換率(2倍以上)(win) double p=50; //勝率(50%以下)(probability) double m_min=1;//最小の賭け金額(money) double m_int=1000;//最初の手持ちの資金(money_initial) int b;//破たん回数のカウント(Bankruptcy) b=0;//初期化 int i_sum;//勝負回数の合計 double m_sum;//最終的なお金の合計 i_sum=0;//初期化 m_sum=0.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_int); fprintf(sf,"#倍率は%lf\t勝率は%lf\t最小の賭け金は%lf\t手持ちのお金は%lf\n",w,p,m_min,m_int); for(k=1;k<=k_max;k++) { int i_count;//勝負回数のカウント i_count=0; double m_now;//現在の資金 double m_bet;//賭け金 //初期化 m_bet=m_min;//最初は最小の賭け金でスタート m_now=m_int;//最初は最初の手持ちの資金でスタート 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_now-m_bet; m_bet=m_bet*2;//2倍賭け } else{//勝ちの場合 m_now =m_now+m_bet*(w-1.0); m_bet =m_min;//賭け金を最小額に戻す } 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; }//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); //sfクローズ fclose(sf); return 0; }//プログラムの終わり |
上のプラグラムになかった要素として
破綻率b_proを求める
※ここでいう破綻率とは手持ちのお金が、2倍賭けに耐えられない状況を意味する。借金してお金を用意することはできないとする。
破綻するまで何回勝負ができるかの期待値であるi_exp
※もしも、指定した勝負回数が終了した段階で、破綻しなかった場合は、指定した勝負回数の値が、期待値算出のために使われる
勝負が終わった段階で持っているお金の期待値m_exp
期待値を求めるために何回試行するかを決めるk_max
k_maxを大きくすればするほど、正確な値が求まる。
ひとまずk_max=10でプログラムを走らせると以下のなる
#倍率は2.000000 勝率は50.000000 最小の賭け金は1.000000 手持ちのお金は1000.000000
1番目2294回目で破たん 手持ちのお金は85.000000
2番目1475回目で破たん 手持ちのお金は679.000000
3番目101回目で破たん 手持ちのお金は32.000000
4番目2340回目で破たん 手持ちのお金は151.000000
5番目3211回目で破たん 手持ちのお金は504.000000
6番目2139回目で破たん 手持ちのお金は27.000000
7番目185回目で破たん 手持ちのお金は65.000000
8番目2018回目で破たん 手持ちのお金は1010.000000
9番目16578回目で破たん 手持ちのお金は1199.000000
10番目432回目で破たん 手持ちのお金は206.000000
勝負回数は100000
試行回数は10
破たん回数は10
破たん率は100.000000%
破たんするまでに行える勝負回数の期待値(i_exp)は3077.300000
破たんする直前で持っているお金の期待値(m_exp)は395.800000
この場合、10万回の勝負までたどり着けず、途中で破たんしている。
破綻するまで行える勝負回数は3077.300000回。
実質3千回程度の勝負しかできず、お金も最初1000から395.8となり、儲かっていない。
試行回数が10回だけだと納得できないと思われるので
試行回数k_maxを100000(10万回)、勝負回数も100000(10万回)
勝負回数は100000
試行回数は100000
破たん回数は99616
破たん率は99.616000%
破たんするまでに行える勝負回数の期待値(i_exp)は3927.368880
破たんする直前で持っているお金の期待値(m_exp)は1008.040340
99%以上の確率(99.616000%)で10万回勝負ができずに終わる。
勝負できる回数の期待値はほぼ4000回(3927.368880)
持っているお金は少し増えて、1000から1008.040340と若干増える結果に。
個人的に、最終的なお金が本当に増えると信じられなかった。
なので、さらにk_maxを10倍して、1000000(100万回)と設定
勝負回数は100000
試行回数は1000000
破たん回数は996408
破たん率は99.640800%
破たんする直前で持っているお金の期待値(m_exp)は994.861619
※i_expは、オバーフローをして、正しい結果を示していなかったので、割愛
結果考察
マーチンゲール法で賭けて、何度も勝負していくと、ほぼ100%の確率で勝負ができなくなる。
勝負できる回数の期待値は、4000回程度。
1024(2の10乗)回勝負した場合、期待値的には10連続負けることが1回程度はあってもいいはず。
いつ10連敗するタイミングがいつ来るかわからないが、手持ちのお金が最小賭け金額の1000倍の状況を考えたとき、マーチンゲール法である程度お金を稼いでいないと、この連敗を波を超えられない。
そう考えると、勝負できる回数の期待値である4000と2の10乗である1024が同じオーダー程度になるのは納得できる。
1024の4倍ほどの値がなぜでるか正確には説明できないが、以下のように個人的に考えている
たしか、大学時代の統計の授業で貯蓄額のグラフを見せられたことがあった気がする。
貯蓄額の平均値は高く見えるが、これは、一部の高所得者が平均を押し上げているからだと習った。
いわゆる「中央値と平均値」の違いを説明する流れで見せられるグラフだ。
この結果もその類で、連敗をうまく免れ、多くの回数の勝負をこなした一部の結果が、全体の平均を押し上げているのかもしれない。
破たんする直前で持っているお金の期待値は10万回試行の時は1008.040340。100万回試行の時は、994.861619。
正直言って、最初に持っている手持ちのお金とさして変わらない。
マーチンゲール法が破綻する根本原因
プログラムを使って、シミュレーションして、マーチンゲール法がなぜうまくいかないか分かった。
マーチンゲール法で試行回数を増やしていくといずれ10連敗、11連敗、12連敗などの連続した敗北をいずれくらう。
この連敗の波を乗り越えるには、勝負回数が2倍になる際に、自分の所持金が2倍以上になっていないとといずれ破たんが迫る。
しかしだ。
賭け金の戻し金額が2倍のマーチンゲール法において、勝負回数を2倍に増やしても、絶対に手持ちのお金は2倍にはならない。
なぜなら、マーチンゲール法は勝利した時に、元に手持ちのお金に+1されるだけだ。
持ち金は増える期待値は、勝ち負けが半々で来たとして、+(勝負回数/2)ほどだ。
いずれ、連敗の波を超えられなくなる。
まとめ
マーチンゲール法は一見必勝法に見えるが、無限の資金を仮定している時にしか成立しない
戻し金額が2倍のマーチンゲール法において、勝負回数が2倍に増えた時、持ち金は勝負回数の1/2倍にしか増えない。そのため、勝負回数が2倍になっても、所持金が2倍以上にならず、いずれ破たんする。
書籍の紹介
『ギャンブルの必勝法が本当に儲かるかプログラミングで検証してみた』は以下の疑問にお答えします。
●勝率が50%の場合、利益を生み出す必勝法は存在するか?
●勝率が60%の場合、どのように賭けるのが最適か?
必勝法と思われている手法を15種類紹介します。必勝法には、例えば、マーチンゲール法(負けた時に2倍賭ける手法)などがあります。これらの手法が本当に儲かるかプログラミングを使用して検証します。また、検証するために必要なプログラミングの知識(C#)も紹介しています。
ギャンブルの必勝法が本当に儲かるかプログラミングで検証してみた
関連記事
目次用の記事:ギャンブルの賭け方の種類をまとめてみた
~Webサイトを自分で作ってみませんか?~
Webサイトを運営するにはサーバーが必須です。
このサイトは、エックスサーバー のサーバーを使用しています。
エックスサーバーは無料で10日間お試しができます。
ルーレットの場合、赤黒交互掛け、6連敗で切り上げれば
37/1だった場合には増えますよ。
ディーラーの腕は凄いな!