モンテカルロ法(賭け)の欠点&改良手法の提案(c++)

  • このエントリーをはてなブックマークに追加

スポンサーリンク

※サイト運営にサーバーは必須です※
ロリポップ! はコスパのよい初心者向けサーバーです~

はじめに

※目次用の記事:ギャンブルの賭け方の種類をまとめてみた

モンテカルロ法と聞くと、多くの方は、乱数を用いて数値解析する手法としてのモンテカルロシミュレーションをイメージされる人は多いと思うが、この記事で扱うのは、ギャンブルの領域において、利用される賭け方の1つであるモンテカルロ法。

※モンテカルロ法(賭け方)は、モンテカルロシミュレーション(計算手法)使った賭け方を意味しているのではない。

この記事では、モンテカルロ法の欠点と、それを改善した手法を提案する。最後に、c++を用いてシミュレーションした結果をのせておく。

※モンテカルロ法(賭け方)を知らない人は、モンテカルロ法の賭けシミュレーション(c++)という記事を参照してください。モンテカルロ法(賭け方)がある程度わからないとこの記事はわかりづらいと思われるので……。

モンテカルロ法とは

一応、モンテカルロ法のルールを確認する。

初めに(1,2,3)という数列を準備する。
数列の左端と右端を足し合わせた数だけ賭ける(最初は1+3=4)

負けた場合:賭け金額を数列の右端に加える
勝った場合:数列の右端と左端を消す

※払戻金の倍率によって、どれだけ数字を消すか変わる
(払戻金が2倍の場合):一番左と一番右。計2つ

例:2連敗後、勝利
(1,2,3,4,5)→(1,2,3,4,5)

(払戻金が3倍の場合):一番左の数字2つと一番右の数字2つ。計4つ
例:2連敗後、勝利
(1,2,3,4,5)→(1,2,3,4,5)

数列の大きさが1より小さくなった場合は、ゲームをリセット。数列は(1,2,3)から再びスタートする。

※数列の大きさが1以下だと、右端と左端を足し合わせて、賭け金額を算出するという操作ができなくなる。

※主に、払戻金が3倍のパターンで、モンテカルロ法は使用される。

モンテカルロ法の欠点

  • 数列がリセットされた時、必ず、最初よりも手持ちのお金が増えるわけではない

(理由)
数列1つ数字が残っている場合も数列がリセットされるが、その場合、6-(最後に残った数列の数字)が手持ちの金額として増減する。

※6という数字は(1,2,3)の合わせて+6に由来する

もしも(最後に残った数列の数字)が大きいと、ゲームがリセットされても損する。

  • 最初に賭ける金額が、1ではなく、4である。

破綻する率を減らしたいなら、ゲームがリセットされた時に、最小単位の賭け金額からスタートできるのが望ましい。しかし、モンテカルロ法は、(1,2,3)という数列で決まるため、最小単位の賭け金額の4倍からスタートせざるを得ない。

※(÷4)すれば、いいという問題ではない。なぜなら、もし負けた場合、次に必要な賭け金額は(1,2,3,4)の数列の両端を足した5となる。5÷4=1.25で、小数となってしまい、困る。

モンテカルロ法の改善提案

  • 数列1つ数字が残っている場合は数列をリセットせず、その数字を二つに分解して、ゲームを続行する。

例えば、数列の数字が7だけだった場合、従来のモンテカルロ法はそこでいったん数列をリセットしていたが、ここでは(7)→(3,4)と、なるべく均等になるように分解する

  • 最初にスタートする数列を(1,2,3)ではなく、(0,1)でスタートする

そうすることで、最小の賭け単位で勝負ができるようになる。

モンテカルロ法の改良版のルール確認

初めに(0,1)という数列を準備する。
数列の左端と右端を足し合わせた数だけ賭ける(最初は0+1=1)

負けた場合:賭け金額を数列の右端に加える
勝った場合:数列の右端と左端を消す

※勝率によって、どれだけ数字を消すか変わる
(払戻金が2倍の場合):一番左と一番右。計2つ
(払戻金が3倍の場合):一番左の数字2つと一番右の数字2つ。計4つ

数列が完全に消えた場合は、数列をリセット。数列は(0,1)から再びスタートする。

数列が1つ残った場合は、数字をなるべく均等に分解する。数列は、分解した2つの数字を並べた数列を使用する。

例:数字分解時は、小さい数字を左に、大きい数字を右に書く
5→(2,3)
8→(4,4)
11→(5,6)

モンテカルロ法(分解法)の特徴

  • 数列がリセットされた時に、最初に持っていた金額よりも+1多い金額になる。

なぜなら、数列が(0,1)からスタートして、右端に賭ける金額の数字を加えることで、負けた時の賭け金額を記憶しておく。数列が全部消えたという状況は、最初の数列の合計である0+1=1と、このセッションで負けた分の賭け金額を回収できたことに他ならない。

  • 主に、払い戻し2倍の時に使用

払い戻し3倍の時は、一番左の数字2つと一番右の数字2つ。計4つ消すので、「数列がリセットされた時に、最初に持っていた金額よりも+1になる」というのは正確には成り立たない

  • 払い戻し2倍の時、局所的に見て、負けた数が勝ちの数が同じくらいであれば、いずれ、数列をリセットされる

※正確には、負けた数が勝ちの数より2倍を超えなければ、期待値的に、数列は小さくなるはず。

マーチンゲール法のように一度の勝ちで、今までの負債を帳消しするのではなく、ゆっくりと、負けた金額を回収するため、資金が尽きるスピードは遅くなることが期待される。

モンテカルロ法(分解法)の検証

以下では2種類のプログラムを紹介する。

それぞれの種類に対して、払い戻し2倍と3倍のバージョンを乗せ、計4つ掲載する。

言語はC++で作った。

※C言語では動かない。大きさの変わる配列を扱う必要があり、vectorという動的配列を取り扱っている。vector はC言語では使用できないので注意。

※このプログラムを組んだ人間は、大学時代に少しプログラムをかじった程度の戦闘能力しかない。

モンテカルロ法(分解法)個別シミュレーション

払い戻し2倍

 

払い戻し3倍

 

プログラムで使用している変数の説明

マーチンゲール法(2倍賭け)の破綻までのシミュレーションで紹介したプログラムと同じ変数の置き方をしている

i_max:勝負の回数
w:賭けたお金の戻る倍率を指定。ここでは2倍(3倍)を指定
p:勝率を指定。ここでは1/2(1/3)の確率なので50(33.333)を指定。

m_min:かけ金を指定
m_ini:最初の資金を指定

※ここではm_min=1でm_ini=1000と、最小の賭け金の1000倍を所持していると考えている。

例えば、最小の賭け金が1000円(千円)だとするなら、手元の軍資金は、1000000円(百万円)。割と現実的な設定だと思われる。

r:乱数(0~100の乱数)

※乱数rが勝率pより下の数で収まるなら勝ちの判定がでる。

そして、勝負を続けていくうちに、負け続けることもあるだろう。
そしてついに、賭けしようにも手持ちのお金が足りなくなるかもしれない。
この場合、これ以上勝負ができなくなり、プログラムは終了する。(破綻判定)

つまり、借金はNG

c:トータルの勝ち負けの差がどのくらいあるか調べる用 。

払い戻し2倍と払い戻し3倍のプログラムの違い

賭けたお金の戻る倍率wと勝率pの値の違い
数列の先頭と最後尾を消すための記述が3倍の場合2回使用。
(下の部分)

 

実際にプログラムを走らせると以下のような結果となる
勝負回数i_maxは100000(10万回)までとする

※最初の賭け金額は0+1=1となる

(2倍の払い戻しの場合)

#倍率は2.000000 勝率は50.000000 最小の賭け金は1.000000 手持ちのお金は1000.000000
1回目 賭け金は1.000000 数列:0/1/ win 勝敗差1 手持ちのお金は1001.000000 数列リセット
2回目 賭け金は1.000000 数列:0/1/ lose 勝敗差0 手持ちのお金は1000.000000
3回目 賭け金は1.000000 数列:0/1/1/ win 勝敗差1 手持ちのお金は1001.000000 数字分解
4回目 賭け金は1.000000 数列:0/1/ lose 勝敗差0 手持ちのお金は1000.000000
5回目 賭け金は1.000000 数列:0/1/1/ lose 勝敗差-1 手持ちのお金は999.000000
(省略)
45回目 賭け金は1.000000 数列:0/1/ win 勝敗差-1 手持ちのお金は1007.000000 数列リセット
46回目 賭け金は1.000000 数列:0/1/ lose 勝敗差-2 手持ちのお金は1006.000000
47回目 賭け金は1.000000 数列:0/1/1/ lose 勝敗差-3 手持ちのお金は1005.000000
48回目 賭け金は1.000000 数列:0/1/1/1/ lose 勝敗差-4 手持ちのお金は1004.000000
49回目 賭け金は1.000000 数列:0/1/1/1/1/ win 勝敗差-3 手持ちのお金は1005.000000
50回目 賭け金は2.000000 数列:1/1/1/ lose 勝敗差-4 手持ちのお金は1003.000000
51回目 賭け金は3.000000 数列:1/1/1/2/ lose 勝敗差-5 手持ちのお金は1000.000000
52回目 賭け金は4.000000 数列:1/1/1/2/3/ lose 勝敗差-6 手持ちのお金は996.000000
53回目 賭け金は5.000000 数列:1/1/1/2/3/4/ lose 勝敗差-7 手持ちのお金は991.000000
54回目 賭け金は6.000000 数列:1/1/1/2/3/4/5/ lose 勝敗差-8 手持ちのお金は985.000000
55回目 賭け金は7.000000 数列:1/1/1/2/3/4/5/6/ lose 勝敗差-9 手持ちのお金は978.000000
56回目 賭け金は8.000000 数列:1/1/1/2/3/4/5/6/7/ win 勝敗差-8 手持ちのお金は986.000000
57回目 賭け金は7.000000 数列:1/1/2/3/4/5/6/ lose 勝敗差-9 手持ちのお金は979.000000
58回目 賭け金は8.000000 数列:1/1/2/3/4/5/6/7/ lose 勝敗差-10 手持ちのお金は971.000000
59回目 賭け金は9.000000 数列:1/1/2/3/4/5/6/7/8/ lose 勝敗差-11 手持ちのお金は962.000000
60回目 賭け金は10.000000 数列:1/1/2/3/4/5/6/7/8/9/ win 勝敗差-10 手持ちのお金は972.000000
61回目 賭け金は9.000000 数列:1/2/3/4/5/6/7/8/ win 勝敗差-9 手持ちのお金は981.000000
62回目 賭け金は9.000000 数列:2/3/4/5/6/7/ lose 勝敗差-10 手持ちのお金は972.000000
63回目 賭け金は11.000000 数列:2/3/4/5/6/7/9/ lose 勝敗差-11 手持ちのお金は961.000000
64回目 賭け金は13.000000 数列:2/3/4/5/6/7/9/11/ win 勝敗差-10 手持ちのお金は974.000000
65回目 賭け金は12.000000 数列:3/4/5/6/7/9/ win 勝敗差-9 手持ちのお金は986.000000
66回目 賭け金は11.000000 数列:4/5/6/7/ lose 勝敗差-10 手持ちのお金は975.000000
67回目 賭け金は15.000000 数列:4/5/6/7/11/ win 勝敗差-9 手持ちのお金は990.000000
68回目 賭け金は12.000000 数列:5/6/7/ win 勝敗差-8 手持ちのお金は1002.000000 数字分解
69回目 賭け金は6.000000 数列:3/3/ win 勝敗差-7 手持ちのお金は1008.000000 数列リセット
(省略)
16653回目 賭け金は1305.000000 数列:284/453/737/1021/ lose 勝敗差-269 手持ちのお金は187.000000
16653回目で手持ちのお金が足りません

45回目から69回目までの一連の動きが、ルールを理解するのに、わかりやすいと思われるので、切り取っている。45回目終了して、46回目最初の段階で持っている金額は1007。68回目で勝利して、残った数字は6になる。6を分解して(3/3/)にする。69回目で勝利して、無事数列が全部消え、69回目が終わった段階で持っている金額は1008。最初の段階より+1される

※賭け金額の最小単位が2倍に増えた場合、数列の数字を2倍にするか、数列は2倍にせず、最後の賭け金額の計算の時に2倍にする、二つの流儀が考えられるが、ここでは、賭け金額の最小単位(m_min)が2倍になっても、数列は2倍にせず、最後の計算で2倍にする流儀に従う。

※数列の数字を2倍にする流儀だと、数字を分解するところの書き方が少しめんどくさくなるため。例えば、最後に残った数字が7の場合で、賭け金額の最小単位が2に増えた場合を考える。数列の数字を2倍にする(賭け金額を記録する流儀)にすると、14となり、普通に分解すると、(7,7)になる。賭ける金額が奇数になり、賭け金額の最小単位が2であるのと矛盾が起きる。この部分をきちんと処理できるように書くのは、少し骨が折れそう。

※サイズが1になった時の処理の、m_bet=vec[0];は、正確には賭けの金額ではなく、数列を考えるために、×m_minを抜いて考えている。また、負けた時のvec.push_back(m_bet/m_min); のm_bet/m_minの処理も同じく、数列で議論するため。

※size==0でなく、size<=0としているのは、払い戻し金額が3倍の時に、中に格納していた要素を削りすぎて、負の値をとる場合があるため。

(3倍の払い戻しの場合で、所持金が増えないケース)

14213回目 賭け金は1.000000 数列:0/1/ win 勝敗差-4721 手持ちのお金は8128.000000 数列リセット
14214回目 賭け金は1.000000 数列:0/1/ lose 勝敗差-4722 手持ちのお金は8127.000000
14215回目 賭け金は1.000000 数列:0/1/1/ lose 勝敗差-4723 手持ちのお金は8126.000000
14216回目 賭け金は1.000000 数列:0/1/1/1/ win 勝敗差-4722 手持ちのお金は8128.000000 数列リセット

14213回目終了後、14214回目開始の時の所持金は、8128。一方で14216回目最後の所持金も8128で増えていない。このように払い戻し3倍の時は、「数列がリセットされた時に、最初に持っていた金額よりも+1になる」が成り立たない。

※srand((unsigned)time(NULL));の部分で時間を参照した上で乱数を発生させている。そのため、実行するタイミングで結果が変化する。

モンテカルロ法(分解法)の期待値シミュレーション

上のプログラムを何度も走らせて期待値や破綻する率をもとめたい

※この期待値を求める時にモンテカルロシミュレーションの考え方が使用されている。

そのためのプログラムは以下のようになる

払い戻し2倍

 

払い戻し3倍

 

プログラムで使用している変数の説明

b_pro:破綻率
※ここでいう破綻率とは手持ちのお金が、賭けに耐えられない状況を意味する。借金してお金を用意することはできないとする。

i_exp: 破綻するまで何回勝負ができるかの期待値
m_exp: 勝負が終わった段階で持っているお金の期待値
k_max:試行回数。
※k_maxを大きくすればするほど、正確な期待値が求まる。

c_exp:勝敗差の期待値

以下では、k_max=100000、i_max=100000で指定。
試行回数10万回、勝負回数10万回。

結果は以下のようになる

(2倍の払い戻し)

勝負回数は100000
試行回数は100000
破たん回数は96838
破たん率は96.838000%
破たんするまでに行える勝負回数の期待値(i_exp)は12665.090120
破たんする直前で持っているお金の期待値(m_exp)は989.985760
破たんした時の勝負差の期待値は-0.449580

(3倍の払い戻し)

勝負回数は100000
試行回数は100000
破たん回数は99035
破たん率は99.035000%
破たんするまでに行える勝負回数の期待値(i_exp)は6358.175200
破たんする直前で持っているお金の期待値(m_exp)は1023.800680
破たんした時の勝負差の期待値は-2119.157820

2倍の払い戻しの破綻率96.838%は、高いように見えるが、マーチンゲール法やなどの99%越えと比べるとだいぶましな数字である。

もとになった単純なモンテカルロ法も破たん率は99%は超えていたので、悪くはない結果だろう。

破綻するまでに行える勝負回数の期待値(i_exp)も12665回と1万回を超える。

3倍の払い戻しの方の破綻率が高いのは、おそらく、2倍の払い戻しと比べて、3倍の払い戻しの場合、数列がリセットされた時に+1以上の利益を出すことがあるため、その分リスクも高まっているからだろう。

※3倍の払い戻しにおいて、かならず、+1以上の利益を出すわけではない。たまに、0の場合もある(個別シミュレーションの項目で説明済み)。数列がリセットされた時に、最初と比べて、負になることはおそらくない。

まとめ

上で提案した方法は、

  • 主に、払い戻し2倍で使用されることを想定している。
  • 払い戻し2倍において、数列がリセットされると、最終的な収支は+1される
  • 単純なモンテカルロ法より、長くゲームが楽しめる

※もしも命名するなら「(0,1)分解法」とか「モンテカルロ分解法」、あるいは単純に「分解法」といったところかな……。私程度の人間が考えつくベットシステムなので、既にどこかで確立されている手法として存在しているかも……

関連記事

※目次用の記事:ギャンブルの賭け方の種類をまとめてみた

~Webサイトを自分で作ってみませんか?~

Webサイトを運営するにはサーバーが必須です。
このサイトは、エックスサーバー のサーバーを使用しています。
エックスサーバーは無料で10日間お試しができます。

  • このエントリーをはてなブックマークに追加

スポンサーリンク


コメント

  1. へのへのもへじ より:

    こんばんは。
    凄く勉強になり、助かりました。
    有り難う御座います。
    負けた時に掛け金を増やしていく方法は、いずれ破綻するのですね。

    それなら、1000から始めて1100や2000まで増やせる確率は
    どれぐらいかわからないでしょうか。
    いずれ破綻するのであれば、投資のように一定額で
    利益確定してはどうかと思いまして。
    もしわかるようであれば、宜しくお願いします。

    1. kaen より:

      有益なコメントありがとうございます。
      マーチンゲール法で確率計算した記事をアップしましたので、もしよろしければ覗いてみてください。
      マーチンゲール法で2倍儲かる確率を検証(c++/c言語)

コメントを残す

*

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)