競馬や競艇などでの券種で「複勝」と言うものがあります。
これは選んだ馬や選手が、上位3位以内(競艇は上位2位以内)に入ると当たりという券種です。
また3連複という券種もあります。
これは選んだ3頭(船なら3艇)全てが3位以内に入ればあたりと言う券種です。
予想する頭数が異なりますが、どちらの券種も上位3位以内に入るものを予想するという券種である点で共通しています。
今回はpythonで競艇の予想AIを作っている時に、機械学習の結果得られた「ある選手が上位3位以内に入る確率」から3連複の確率を計算しようとした際に少し詰まってしまったので記録に残しておくことにしました。
詰まった経緯
簡単な競艇の説明
競艇は6艇のボートで行われる競技です。
競艇で複勝と言えば上位2位以内に入ることを意味しますが、競艇でのメインの券種は3連単、次いで3連複なので、各艇が上位3位以内に入る確率を考えるのが重要となります。
もちろん1位になる艇を当てる単勝の確率を求めることができるのであればそれが一番なのですが、データの関係で上位3位率の方が機械学習で求めやすそうなので、上位3位確率を採用しています。
(ただし管理人は機械学習を覚えて2ヵ月のペーペーなのでこの見解は正しいかどうかはというと、恐らく間違っています。)
詰まった経緯
競艇のデータスクレイピングが完了し、加工を施し、目的変数を3位以内に入るかどうかとしてLightGBMにかけた結果、それぞれの選手が上位3位以内に入る確率っぽい数値を求めることができました。
しかしそれぞれの選手が1位になる確率とは違い、この数値はあまり使いやすくはなさそうです。
例えば6艇のそれぞれの3位内率を以下のようにします。
- 90%
- 70%
- 50%
- 40%
- 30%
- 20%
この数値は出走表に書かれている3位内率(実績率)ではありません。あくまで確率です。
また上位3位に入る確率なので、合計は100%ではなく300%になります。
では1位から3位までが1, 2, 3の3艇となる確率(3連複1-2-3の実現確率)はいくらでしょうか?
単順に掛け合わせると31.5%となりそうですが、これは間違いです。
というのも2-3-4で同じようにすると14%になりますが、これは1番艇が3位以内に入らないことが必要であるのに、1番艇が3位圏外の確率である10%を超えているので間違いということが分かります。(1番艇は90%で3位以内に入る = 1番艇は10%で3位以内に入らない)
ではちょっとわかりやすい数値を入れながら実験してみます。
仮想数値での実験
実験1:1,2,3艇は100%、4,5,6艇は0%とする
- 100%
- 100%
- 100%
- 0%
- 0%
- 0%
6艇の3位内確率を上記のように仮定すると、計算するまでもなく1位から3位は1-2-3の3艇になります。
これは単純に確率を掛け合わせただけの100%×100%×100% = 100%と言うのと一致します。
もちろん他の組み合わせは0%ですが、単純な掛け合わせの場合は0%を必ずどこかで掛けるので0になるのは当然です。
第n艇が上位3位以内に入る確率をPnとあらわすとすると、分子にP1*P2*P3が入っているのは間違いないようです。
実験2:1,2,3,4,5,6艇全て50%とする
- 50%
- 50%
- 50%
- 50%
- 50%
- 50%
1~6艇まで全て50%の確率で同じだとします。
その組み合わせ数は6C3なので20通りでどの組み合わせでも確率は同じことから、上位3艇が1-2-3の確率は1/20、つまり5%となるはずです。
ここで単純に50%を掛けると50%×50%×50%=12.5%となります。
8/20を掛けることで5%になります。もちろん他の数値の場合に8/20を単純に掛けると数値が違ってくるのは実験1からわかるので、それぞれの艇の確率によって、この数値が変わってくることが分かります。
実験3:1艇が100%,2,3,4,5艇が50%,6艇が0%とする
- 100%
- 50%
- 50%
- 50%
- 50%
- 0%
この場合、1艇が3位以内に入ることは確定し、6艇が入らないことが確定しているので、残り2枠を4艇で争うということになります。
そしてその4艇は全て同じ確率なので、組み合わせ数4C2=6から考えられる1,2,3が上位3艇となる確率は1/6、つまり16.6666….%となります。
単純な掛け合わせだと25%に4/6を掛けることで上記の数字になります。
Yahoo知恵袋を覗いてみる
ちょっとまだよく分からないのでYahoo知恵袋を覗いてみることにします。
数学のカテゴリーで「複勝」で探してみました。競馬の複勝は上位3位以内なので同じような計算式になるはずです。
1つ目
Q.3着までに入る確率がオッズ通り50%・25%・20%と仮定した場合、 3頭とも3着までに入る確率の計算式は0 .5×0.25×0.2=2.5%であってますか?
A.各馬が1着2着3着になるのを同確率と仮定した場合、2倍の馬が1着になるのは(50/3)%です。1着2着3着が2倍4倍5倍ときた場合 (0.50/3)×(0.25/3)×(0.20/3) 1着2着3着の順列が6通りあるので (0.50/3)×(0.25/3)×(0.20/3)×6 =1/180 =0.005555… ≒0.56%
検証してみます。
- 90%
- 70%
- 50%
- 40%
- 30%
- 20%
この6艇の3位以内確率で全通りを計算して足し合わせると、本来であれば100%にならなければいけないところが48%ぐらいにしかなりません。
pythonで計算するコードは以下の通りです。確認してみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# それぞれの艇の確率を辞書型にする prob_dict = {1:0.9, 2:0.7, 3:0.5, 4:0.4, 5:0.3, 6:0.2} # 計算後の確率を入れる辞書を作る calc_probs = {} # それぞれの組み合わせについて確率を計算する for i in range(1,7): for j in range(i+1,7): for k in range(j+1,7): # 艇番号3つをキーにする key = str(i)+str(j)+str(k) # 知恵袋の式の計算をする prob = prob_dict[i]/3*prob_dict[j]/3*prob_dict[k]/3*6 calc_probs[key] = prob # 全確率の合計を計算して表示させる print(sum(calc_probs.values())) |
2つ目
Q.例えば複勝率50%の馬と複勝率50%の馬がいたとしたらワイドで的中させる確率は 25%という計算で合っているでしょうか?
A.38%でやります 38%の馬が入着した段階で、残りの馬の複勝率の合計は200%となります。 しかるに、走る前の複勝率は300ー38=262%です よって、38%の馬の入着が確定した段階で、複勝率を修正する必要があります。単純に考えると、あと1頭の38%の馬の複勝率は 38×(200/262)=29.00・・となります。よって、ワイドの的中率は約11%ととなります。
ワイドの計算ですが同じようなものです。
つまり1艇目の確率はそのまま使う、2艇目は「2艇目の確率×2.00/(3.00-1艇目の確率)」、ということで3艇目は「3艇目の確率×1.00/(3.00-1艇目の確率-2艇目の確率)」となるようです。
ただしこれだと1艇目、2艇目の選び方で計算結果が異なります。
例えば同じ1-2-3の組み合わせでも、1番艇、2番艇を順番に選んだ場合と、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 |
# それぞれの艇の確率を辞書型にする prob_dict = {1:0.9, 2:0.7, 3:0.5, 4:0.4, 5:0.3, 6:0.2} # 計算後の確率を入れる辞書を作る calc_probs = {} # それぞれの組み合わせについて確率を計算する for i in range(1,7): for j in range(i+1,7): for k in range(j+1,7): # 艇番号3つをキーにする key = str(i)+str(j)+str(k) # 知恵袋の式の計算をする prob = (prob_dict[i]*prob_dict[j]*2/(3-prob_dict[i])*prob_dict[k]/(3-prob_dict[i]-prob_dict[j])+ prob_dict[j]*prob_dict[k]*2/(3-prob_dict[j])*prob_dict[i]/(3-prob_dict[j]-prob_dict[k])+ prob_dict[k]*prob_dict[i]*2/(3-prob_dict[k])*prob_dict[j]/(3-prob_dict[i]-prob_dict[k])+ prob_dict[i]*prob_dict[j]*2/(3-prob_dict[j])*prob_dict[k]/(3-prob_dict[i]-prob_dict[j])+ prob_dict[j]*prob_dict[k]*2/(3-prob_dict[k])*prob_dict[i]/(3-prob_dict[j]-prob_dict[k])+ prob_dict[k]*prob_dict[i]*2/(3-prob_dict[i])*prob_dict[j]/(3-prob_dict[i]-prob_dict[k]))/6 calc_probs[key] = prob # 全確率の合計を計算して表示させる print(sum(calc_probs.values())) |
計算結果は1になりました。そして数値をどれだけいじっても6艇の合計値が300%になる限りは1になります。
予想どおりの結果になりました。
まとめ
Yahoo知恵袋はあてにならないと思っていましたが、結果的に答えが見つかったので良かったです。
式の意味は理解していませんが、私は答えより導出過程の理解の方が重要な理学部出身じゃなくて結果が合えばOKな工学部出身なので問題ありません。
(まぁ計算ができたところでpythonと機械学習の知識が初学者なので、舟券が当たるかどうかは別の話)
コメントを残す