スポンサーリンク
※サイト運営にサーバーは必須です※
~ ロリポップ! はコスパのよい初心者向けサーバーです~
目次
はじめに
※目次用の記事:ギャンブルの賭け方の種類をまとめてみた
この記事では、ウィナーズ投資法の紹介と、そのシミュレーション結果を示す
ウィナーズ投資法とは
最小の賭け金額(例えば1)を自分で設定しておく
負けた時に賭けた金額を覚えておくための数列を用意する
※最初は、勝負が始まっていないので数列の中身は空と考える。数列:{}
勝負の最初の賭け金額は1とする
負けた場合:
1回目負けた場合、数列の右側に数字の1を加える。数列:{1}
2回目の勝負のかけ金額も1とする
2回目も負けた場合(2連敗)、数列の右側に数字の1を加える。数列:{1,1}
3回目以降の賭け金額は(数列の左端)×2を使う。
負けた場合は、数列の右側に、賭けた金額を加える。
例えば、3回目も負けた場合、数列:{1,1,2}
勝った場合:
数列の左端の数字を1つ消す。
賭け金額は、負けた時と同様で、(数列の左端)×2を使う
数列の数字が全部なくなった場合は、再び、賭け金額を1からスタートする。
ウィナーズ投資法の具体例
ルールだけ、説明してもイメージがつかめないと思うので、具体例を挙げる。
勝負 | 数列(前) | 賭け金 | 勝敗 | 数列(後) | 収支 |
1 | {空} | 1 | × | 1 | -1 |
2 | 1 | 1 | × | 1,1 | -2 |
3 | 1,1 | 2 | × | 1,1,2 | -4 |
4 | 1,1,2 | 2 | × | 1,1,2,2 | -6 |
5 | 1,1,2,2 | 2 | ○ | -4 | |
6 | 1,2,2 | 2 | ○ | -2 | |
7 | 2,2 | 4 | ○ | 2 | |
8 | 2 | 4 | × | 2,4 | -2 |
9 | 2,4 | 4 | ○ | 2 | |
10 | 4 | 8 | × | 4,8 | -6 |
11 | 4,8 | 8 | ○ | 2 | |
12 | 8 | 16 | ○ | 18 | |
合計賭け金額 | 54 |
ウィナーズ投資法の特徴
- 負けの数と勝ちの数が等しくなる時、数列がなくなる
数列を使うという観点で、2in1法やモンテカルロ法に似ている。だが、数列の消え方が異なる。2in1法やモンテカルロ法は、勝った時に、負けていた2回分の損失を取り返す手法なので、勝った時は数列の数字を2つ消すが、ウィナーズ投資法は1つしか消さない。
モンテカルロや2in1と比べて、数列の消え方が緩やかなので、いつまでたっても決着がつかず、ズルズルと賭ける金額が膨らむ危険性が高い。
左端の2倍の数字を賭けているということは、勝った時は、以前負けた時の2倍のお金が手元に入るということ。
- (賭けで得た金額)=(賭けで失った金額)×2
※数列がリセットされた直後に勝った場合や、数列がリセットされた直後一度負けてすぐに勝った場合は、この式が当てはまらない。すぐ勝った場合は収支:+1。一度負けてすぐに勝った場合は、±0。
※具体例からもこの式を理解することができる。(賭けで得た金額)+(賭けで失った金額)に対応する、賭けた金額の合計が54。(賭けで得た金額)-(賭けで失った金額)に対応する、最終的な収支が+18。54が18のピッタリ3倍になることから、この式が正しいことがわかる。
ウィナーズ投資法の検証
以下では2種類のプログラムを紹介する。
言語はC++で作った
※C言語としてコンパイルしても動かない
※このプログラムを組んだ人間は、大学時代に少しプログラムをかじった程度の戦闘能力しかない
※2017年10月追記
以下のプログラムを見返したら、ミスがあることおに気づきました。
勝った時のm_betの値を変更するという処理がすっかり抜けている。
時間がある時に修正する予定(修正完了)
ウィナーズ投資法の個別シミュレーション
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 |
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <vector> using namespace std; // 名前空間指定 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_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;//最初は最初の手持ちの資金でスタート // vct(vector)を生成 int n; vector<int> vec; //初期化 int size; size = (int)vec.size(); m_bet=m_min;//賭け金は最小の賭け金額でスタート int lose_num; int win_num; lose_num=0; win_num=0; 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); printf("数列:");//配列の出力 fprintf(sf,"数列:"); for(n=0;n<size;n++){ printf("%d/",vec[n]); fprintf(sf,"%d/",vec[n]); } printf("\t"); fprintf(sf,"\t"); if (p<=r){//負けの場合 m_now-=m_bet; lose_num+=1; if (lose_num==1){ vec.push_back(m_bet/m_min); m_bet=m_min; } else{ vec.push_back(m_bet/m_min); // 末尾にm_bet*2 を追加 m_bet=vec[0]*2*m_min; } c-=1; printf("lose\t"); fprintf(sf,"lose\t"); } else{//勝ちの場合 m_now +=m_bet*(w-1.0); printf("win\t"); fprintf(sf,"win\t"); if(size>=1){ vec.erase(vec.begin()); } c+=1; win_num+=1; if(size<=0){ printf("数列リセッ\t"); fprintf(sf,"数列リセッ\t"); vec.clear(); lose_num=0; win_num=0; m_bet=m_min; } } printf("win%d-lose%d\t",win_num,lose_num); fprintf(sf,"win%d-lose%d\t",win_num,lose_num); printf("勝敗差%d\t手持ちのお金は%lf",c,m_now); fprintf(sf,"勝敗差%d\t手持ちのお金は%lf",c,m_now); size = (int)vec.size();//sizeの大きさ再度確認 printf("\n"); fprintf(sf,"\n"); if(m_now<m_bet){//手持ちの金より賭けるお金が高い場合 printf("次の勝負に必要な金額は%lfです。%d回目でゲームを終了します\n",m_bet,i); fprintf(sf,"次の勝負に必要な金額は%lfです。%d回目でゲームを終了します\n",m_bet,i); break; } }//for(i)文の終わり //sfクローズ fclose(sf); return 0; }//プログラムの終わり |
プログラムで使用している変数の説明
※マーチンゲール法(2倍賭け)の破綻までのシミュレーションで紹介したプログラムと同じ変数の置き方をしている
i_max:勝負の回数
w:賭けたお金の戻る倍率を指定。ここでは2倍を指定
p:勝率を指定。ここでは1/2の確率なので50を指定。
m_min:かけ金を指定
m_ini:最初の資金を指定
※ここではm_min=1でm_ini=1000と、最小の賭け金の1000倍を所持していると考えている。
例えば、最小の賭け金が1000円(千円)だとするなら、手元の軍資金は、1000000円(百万円)。割と現実的な設定だと思われる。
r:乱数(0~100の乱数)
※乱数rが勝率pより下の数で収まるなら勝ちの判定がでる。
そして、勝負を続けていくうちに、負け続けることもあるだろう。
そしてついに、賭けしようにも手持ちのお金が足りなくなるかもしれない。
この場合、これ以上勝負ができなくなり、プログラムは終了する。(破綻判定)
つまり、借金はNG
c:トータルの勝ち負けの差がどのくらいあるか調べる用
実際にプログラムを走らせると以下のような結果となる
勝負回数i_maxは100000(10万回)までとする
#倍率は2.000000 勝率は50.000000 最小の賭け金は1.000000 手持ちのお金は1000.000000
1回目 賭け金は1.000000 数列: lose win0-lose1 勝敗差-1 手持ちのお金は999.000000
2回目 賭け金は1.000000 数列:1/ win win1-lose1 勝敗差0 手持ちのお金は1000.000000
3回目 賭け金は1.000000 数列: win 数列リセッ win0-lose0 勝敗差1 手持ちのお金は1001.000000
(省略)
180回目 賭け金は64.000000 数列:64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/
win win65-lose90 勝敗差-20 手持ちのお金は480.000000
181回目 賭け金は64.000000 数列:64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/
lose win65-lose91 勝敗差-21 手持ちのお金は416.000000
182回目 賭け金は128.000000 数列:64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/
lose win65-lose92 勝敗差-22 手持ちのお金は288.000000
183回目 賭け金は128.000000 数列:64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/128/ lose win65-lose93 勝敗差-23 手持ちのお金は160.000000
184回目 賭け金は128.000000 数列:64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/64/128/128/ lose win65-lose94 勝敗差-24 手持ちのお金は32.000000
次の勝負に必要な金額は128.000000です。184回目でゲームを終了します
※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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <vector> using namespace std; // 名前空間指定 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_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;//賭け金は最小の賭け金額でスタート int c;//勝ち負け差のカウント(count) c=0;//初期化 // vct(vector)を生成 //int n; vector<int> vec; //初期化 int size; size = (int)vec.size(); m_bet=m_min;//賭け金は最小の賭け金額でスタート int lose_num; int win_num; lose_num=0; win_num=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; lose_num+=1; if (lose_num==1){ vec.push_back(m_bet/m_min); m_bet=m_min; } else{ vec.push_back(m_bet/m_min); // 末尾にm_bet*2 を追加 m_bet=vec[0]*2*m_min; } c-=1; } else{//勝ちの場合 m_now +=m_bet*(w-1.0); if(size>=1){ vec.erase(vec.begin()); } c+=1; win_num+=1; if(size<=0){ vec.clear(); lose_num=0; win_num=0; m_bet=m_min; } } i_count=i;//i_countを定義しないと破たんしない(セーフ)場合、iと値が1ずれてしまうため size = (int)vec.size();//sizeの大きさ再度確認 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
破たん回数は100000
破たん率は100.000000%
破たんするまでに行える勝負回数の期待値(i_exp)は208.805930
破たんする直前で持っているお金の期待値(m_exp)は745.977420
破たんした時の勝負差の期待値は-0.109150
破綻率が100%で、破たんするまでに行える勝負回数の期待値(i_exp)は208回。かなり早いスピードで資金が枯渇している。マーチンゲール法(2倍)よりも早い。
ウィナーズ投資法と同じくらいで破たんする手法は、マーチンゲール法(3倍)である。今までの負けを帳消しした上で、負ければ負けた分だけ、利益を出そうとする観点では似ている。
また、最初にあったお金である1000から700程度まで減っている。つまり損する可能性がある。
この理由ははっきりわかっていないが、おそらく数列の数字の増え方はそこまで激しくないので、徐々に資金が削られていくからだろう。
まとめ
- ウィナーズ投資法は、損する危険性がある
- 勝ちの数と負けの数が等しくなる時、利益を出す
書籍の紹介
『ギャンブルの必勝法が本当に儲かるかプログラミングで検証してみた』は以下の疑問にお答えします。
●勝率が50%の場合、利益を生み出す必勝法は存在するか?
●勝率が60%の場合、どのように賭けるのが最適か?
必勝法と思われている手法を15種類紹介します。必勝法には、例えば、マーチンゲール法(負けた時に2倍賭ける手法)などがあります。これらの手法が本当に儲かるかプログラミングを使用して検証します。また、検証するために必要なプログラミングの知識(C#)も紹介しています。
ギャンブルの必勝法が本当に儲かるかプログラミングで検証してみた
関連記事
※目次用の記事:ギャンブルの賭け方の種類をまとめてみた
~ギャンブルに絶対儲かる必勝法があるのだろうか?~
私(サイト主)はこの疑問に対して非常に興味を持ち、プログラミングで検証してみました。
このサイトを応援してもいいかなと思う人はぜひとも購入を検討してみてください。
はじめまして!
Amazonの書籍とこの記事を拝見して一点質問があるのですが、
ウィナーズ投資法の試行のうち、破たんする前に最も利益が
伸びた試行回の勝負回数と最大利益額はいくらになりましたか?
私もウィナーズ投資法をシミュレーションしたのですが、
一回だけ勝負回数29,702、最大利益251,146ドルまでいき、
その後破綻しました。
※資金1,000ドル、1ユニット1ドルでやりました。
100%破綻する投資法とのことですが、破綻する前の最大値が
気になり質問させて頂きました。
コメントありがとうございます。
>破たんする前に最も利益が伸びた試行回の勝負回数と最大利益額はいくらになりましたか?
問題の設定によって、答えは変わります。
理論上、無限の試行回数をこなせば、最大利益額も無限に増えていきます。
有限の試行回数で、有限の勝負回数ならば、最大利益額も有限に落ち着きます。
勝負回数を一定にして、試行回数を可能な限り大きくした場合、最大利益額は、一定の勝負回数で実現しうる最大利益になります(ウィナーズ投資法の場合、連勝した時が最大利益になるとは限りません)。
例えば、勝負回数は10回にして、試行回数を10000回にすれば、ほぼ全てのパターンを網羅することができることをイメージしてください。
ご返信ありがとうございます。
この記事のシミュレーション、勝負回数100,000回、
試行回数100,000回の場合だと、結果としては
この回数設定で破綻する前に出せる理論上最大の勝負回数・
利益の近似値が出ましたでしょうか?
また、記事内にある”勝負回数の期待値”というのはどのような
計算式で算出していますか?
例えば下記のような設定の場合、
勝負回数10,000回
試行回数 3回
■結果
試行A:破綻直前の勝負回数 20
試行B: 〃 50
試行C: 〃 200
■平均勝負回数
(20+50+200)/3=90回
↑のような単純な平均回数とは異なる計算式で算出しているのでしょうか。
長々と申し訳ないのですが、よろしくお願い致します。
コメントありがとうございます。
>この記事のシミュレーション、勝負回数100,000回、
>試行回数100,000回の場合だと、結果としては
>この回数設定で破綻する前に出せる理論上最大の勝負回数・
>利益の近似値が出ましたでしょうか?
申し訳ございませんが、この質問に対する回答は本書のサポートの範囲を超えているので、対応いたしません。
本書で紹介したプログラムを改造すれば、計算できると思いますので、ご自身でご確認ください。
>勝負回数の期待値”というのはどのような計算式で算出していますか?
単純な平均回数で算出しています。
(記載されている認識で間違いないです。)