賭け式を統合化する方式の提案

今は3連単が最も売れていてそれに投票が集中するため、他の賭け式で売上が少なくなり本命党が太い張りをやり難くなる場合がある。2車単で売上が50万円しかないのに1-2に10万円張る何てやるとそれで配当がぐっと下がってしまう。枠やワイドはもっと売上が少ないので実質的に買えないことがある。昔の単勝複勝と同じだ。

人気のない賭け式は投票する人が少なく、そんなに大きくない張りでも全体に影響を与え、配当が非常に不安定になってしまう。また、少数の人の意見しか反映されないので、常識的に予想される配当から大きく離れた配当になってしまう問題もある。このように人気のない賭け式は不安定なので、それゆえに余計に人気がなくなってしまう。

しかし、人気のない賭け式だってそれに賭ける人がいるわけだから、需要は確実にある。今はない単勝なんかは単純なので、初心者はまずこれを買ってみたいと思うだろう。

概要

このような問題を解決するために以下に述べる新しい方式を提案する。

競輪,あるいは公営競技での賭けというのは、客が張る目を言ってざるに金を放り胴はざるから2割5分テラを抜いて残りを勝った客で分ける、という仕組みである*1。このざるは各賭け式ごとに別々にある。3連単のざるは金がたくさん入っていて良いが、ワイドのざるは少ないので問題があるというわけだ。

提案する新しい方式では、賭け式ごとにあったざるを全体で1つにする。これがポイントだ。発売する車券の種類は3連単、2車単など従来と全く同じである。投票が終了した後に、3連単以外の車券を3連単車券に分解して3連単に内部的に再投票する。例えば、2車単の1-2という目の車券は、3連単の1-2-3456789という7つの車券に分解される。またその投票枚数は3連単のみの投票分布に比例して決められる。したがって、この分解された7つの車券はどれが当たっても同じ金額が回収される。3連単の投票分布が下表のようだったとしよう。

1-2-3 1-2-4 1-2-5 1-2-6 1-2-7 1-2-8 1-2-9 1-2-xの合計
投票数 934 1081 302 197 848 281 689 4332
割合 0.216 0.250 0.070 0.045 0.196 0.065 0.159 1.0

「割合」というのは、それぞれの票数の1-2-x全部の票数に対する割合のことで、1-2-3なら934/4332=0.216と算出される。2車単の1-2に投じられた1枚は、この割合に分割されて3連単に再投票されることになる。

このようにして、3連単以外の全ての賭け式の全ての投票が、内部的に3連単に再投票される。そして元のものと合わせて1つの総合化した3連単の投票分布が得られる。

次に配当倍率の決め方を説明する。まず総合化した3連単分布から従来の方法で3連単の配当倍率を決める。その計算式は「配当の決め方」に書いた。これがそのまま3連単の配当倍率になる。その他の種類の車券は、その種類の車券1枚を分割した割合に、総合化した3連単の配当倍率を掛け合わせて得られる。上の例で、1-2-3が的中であるとすると、2車単の1-2の配当倍率は、0.216 * (総合化した3連単の配当倍率) となる。1-2という車券1枚のうち0.216枚分が的中したと考えるわけだ。

以上のように、この新しい方式では投票された全ての車券が1つの投票分布にまとめられ、そこから配当倍率が決まる。そのため、売上げの少ないワイドや2枠複でも配当が不安定にならず、客は安心して太い買いを入れることができる。レースギャンブルの基本である単勝を無理なく売れることも大きな長所である。また、発売される車券の種類は従来と同じであり、投票中のオッズ表示も従来と同じようにできる。表面的には変わることがないので、客にとって面倒なことはなく、移行はスムースだろう。

簡単な実装

配当決定のアルゴリズムを具体的に示すために、実際のレースの投票分布から配当を算出するプログラムをRubyで書いてみる。全ての賭け式に対応するのは面倒だから、3連単と2車単だけを扱うことにする。ruby-1.9.1とruby-1.8.7で動作を確認している。

# -*- coding: utf-8 -*-

class Array
  def sum
    # nil以外の要素の和
    inject(0){|t, e| e ? t + e : t}
  end
end

def cutoff(x)
  # 少数第2位以下を切捨て
  (10.0 * x).floor / 10.0
end

def div_e3_e2_simple(f3, dist_e3, dist_e2, v_e3_total, v_e2_total)
  # 3連単, 2車単の配当倍率を算出する(的中投票無しの場合に未対応)
  #  f3          3連単の的中した目の配列 (例) [[1,2,3], ...]
  #  dist_e3     3連単の投票分布を表すHash (例) {[1,2,3]=>1234, ...}
  #  dist_e2     2車単の投票分布を表すHash (例) {[1,2]=>1234, ...}
  #  v_e3_total  3連単の投票総数
  #  v_e2_total  2車単の投票総数
  ## 3連単の配当を決定
  us = f3.map do |f|
    a = (1..9).map{|i| dist_e3[[f[0], f[1], i]]}
    v = a[f[2]-1].to_f
    v + dist_e2[f[0, 2]] * v / a.sum
  end
  v_total = v_e3_total + v_e2_total
  n, uu = us.size, us.sum
  ds = us.map{|u| 0.75 * ((v_total - uu) / n + u) / u}
  e3 = f3.zip(ds)
  ## 2車単の配当を決定
  f2 = f3.map{|f| f[0, 2]}.uniq
  e2 = f2.map do |f|
    a = (1..9).map{|i| dist_e3[[f[0], f[1], i]]}
    v = a.sum
    [f, e3.select{|e| e[0][0, 2] == f}.map{|f, d| d * a[f[2]-1] / v}.sum]
  end
  ## 結果
  [e3.map{|f, d| [f, cutoff(d)]}, e2.map{|f, d| [f, cutoff(d)]}]
end

puts '平塚 2008/12/30 (3日目) 11R'
# http://keirin.jp/pc/dfw/dataplaza/guest/raceresult?KCD=35&KBI=20081230&RNO=11
total_v_e3 = 37518307
dist_e3 =
  {[7,1,2]=>48217, [7,1,3]=>73101, [7,1,4]=>63960, [7,1,5]=>50035,
   [7,1,6]=>94375, [7,1,8]=>23822, [7,1,9]=>70893}
total_v_e2 = 13964628
dist_e2 = {[7,1]=>152985}
f3 = [[7, 1, 9]]
e3, e2 = div_e3_e2_simple(f3, dist_e3, dist_e2, total_v_e3, total_v_e2)
p e3 #=> [[[7, 1, 9], 400.3]]
p e2 #=> [[[7, 1], 66.8]]

puts '松坂 2008/03/08 (3日目) 5R'
# http://keirin.jp/pc/dfw/dataplaza/guest/raceresult?KCD=47&KBI=20080308&RNO=5
total_v_e3 = 17303
dist_e3 =
  {[5,8,1]=>9, [5,8,2]=>6, [5,8,3]=>5, [5,8,4]=>1, [5,8,6]=>1,
   [5,8,7]=>3, [5,8,9]=>3, [5,9,1]=>306, [5,9,2]=>223, [5,9,3]=>1278,
   [5,9,4]=>51, [5,9,6]=>63, [5,9,7]=>216, [5,9,8]=>56}
total_v_e2 = 4384
dist_e2 = {[5,8]=>11, [5,9]=>465}
f3 = [[5, 8, 9], [5, 9, 8]]
e3, e2 = div_e3_e2_simple(f3, dist_e3, dist_e2, total_v_e3, total_v_e2)
p e3 #=> [[[5, 8, 9], 1940.5], [[5, 9, 8], 120.1]]
p e2 #=> [[[5, 8], 207.9], [[5, 9], 3.0]]

同着があるので的中した目は一般に複数あることに注意されたい。投票分布は配当を算出するのに必要な目だけをHashで与えている。

特殊な場合の対処

上のスクリプトはほとんど全てのレースで動くが、3連単で的中票がない場合を考慮していない(ランタイムエラーになる)。この場合どうするかだが、最も簡単なのは、3連単を特払いにしてその結果他の賭け式も全て特払いになる、という処置だ。しかしこれはほとんどの客に受け入れられないだろう。1-6-8と入着して3連単の1-6-8が無投票だから2車単の1-6も特払いになる、なんてのは非常に不自然だ。

あるいは、3連単で的中票がない場合は、このときだけ従来の方式に戻すことが考えられる。すなわち、3連単を特払いにして、他の賭け式はその賭け式ごとにざるを別にして配当を算出する。投票が少なくて偏っている賭け式があるとまずいが、稀なケースなので許容範囲だろうか。

もう一つ、非常にテクニカルだが実用的に十分妥当であると思うやり方がある。3連単で的中票がない場合、その目に1枚投票されていたと仮定する。すると上で示したアルゴリズムで全ての賭け式の配当が算出できる。その後、全投票から、3連単以外の賭け式に対して払い戻された票を除いた票を原資として、3連単を特払いにする。この特払いは従来のように配当倍率が0.7*2の一定ではない。

的中票=ゼロでは困るので、取り敢えず全体にほとんど影響を与えない1枚だけ的中があったことにして配当を算出し、しかる後その仮想の的中1枚が得るはずだった回収金を特払いとして3連単の全投票者に返すわけである。

以下にこのやり方で3連単で的中票がない場合に対応した配当算出のスクリプトを示す。同着があって複数の的中した目があり、その一部の目のみが無投票だった、という非常に稀にしか起こらないが面倒な場合にも対応できるようにしてあるので、ちょっとややこしいものになっている。

V_MIN = 1

def div_e3_e2(f3, dist_e3, dist_e2, v_e3_total, v_e2_total)
  # 3連単, 2車単の配当倍率を算出する (的中投票無しの場合に対応する一例)
  ## 3連単に的中票がない場合の前処理
  zf = []
  f3.each do |f|
    if dist_e3[f] == 0
      dist_e3[f] = V_MIN
      v_e3_total += V_MIN
      zf.push(f)
    end
  end
  ## 3連単の配当を決定
  us = f3.map do |f|
    a = (1..9).map{|i| dist_e3[[f[0], f[1], i]]}
    v = a[f[2]-1].to_f
    v + dist_e2[f[0, 2]] * v / a.sum
  end
  v_total = v_e3_total + v_e2_total
  n, uu = us.size, us.sum
  ds = us.map{|u| 0.75 * ((v_total - uu) / n + u) / u}
  e3 = f3.zip(ds)
  ## 2車単の配当を決定
  f2 = f3.map{|f| f[0, 2]}.uniq
  e2 = f2.map do |f|
    a = (1..9).map{|i| dist_e3[[f[0], f[1], i]]}
    v = a.sum
    [f, e3.select{|e| e[0][0, 2] == f}.map{|f, d| d * a[f[2]-1] / v}.sum]
  end
  ## 3連単に的中票がない場合の修正
  if zf.any?
    zf.each{|f| dist_e3[f] = 0}
    v_total -= V_MIN * zf.size
    v_e3_total -= V_MIN * zf.size
    v = v_total - e2.map{|f, d| dist_e2[[f[0], f[1]]] * d}.sum / 0.75
    if zf.size == f3.size
      e3 = [[nil, 0.75 * v / v_e3_total]]
    else
      f3 = f3.select{|f| !zf.include?(f)}
      us = f3.map{|f| dist_e3[f]}
      n, uu = us.size, us.sum
      ds = us.map{|u| 0.75 * ((v - uu) / n + u) / u}
      e3 = f3.zip(ds)
    end
  end
  ## 結果
  [e3.map{|f, d| [f, cutoff(d)]}, e2.map{|f, d| [f, cutoff(d)]}]
end

puts '高松 2007/04/10 (2日目) 7R'
# http://keirin.jp/pc/dfw/dataplaza/guest/raceresult?KCD=71&KBI=20070410&RNO=7
total_v_e3 = 25071
dist_e3 =
  {[7,1,2]=>7, [7,1,3]=>9, [7,1,4]=>3, [7,1,5]=>2, [7,1,6]=>0,
   [7,1,8]=>11, [7,1, 9]=>15, [7,6,1]=>0, [7,6,2]=>1, [7,6,3]=>5,
   [7,6,4]=>12, [7, 6,5]=>4, [7,6,8]=>8, [7,6,9]=>5}
total_v_e2 = 5960
dist_e2 = {[7,1]=>37, [7,6]=>22}
f3 = [[7, 1, 6], [7, 6, 1]]
e3, e2 = div_e3_e2(f3, dist_e3, dist_e2, total_v_e3, total_v_e2)
p e3 #=> [[nil, 0.5]] # 目=nilは特払いを表す
p e2 #=> [[[7, 1], 136.9], [[7, 6], 200.6]]

puts '上のレースで、7-1-6に2枚投票されていた場合'
dist_e3[[7, 1, 6]] = 2
e3, e2 = div_e3_e2(f3, dist_e3, dist_e2, total_v_e3, total_v_e2)
p e3 #=> [[[7, 1, 6], 6926.2]]
p e2 #=> [[[7, 1], 135.3], [[7, 6], 200.6]]

他に特殊な場合として、失格、落車棄権などによって、着を得た選手が3車に満たないレースがある。このとき三連単は成立せず全返還になる。よって他の賭け式に配当を付けるには、従来と同じく賭け式ごとにざるを別にして算出するしかなく、これが妥当だろう。

*1:JRAはこれよりちょっとややこしいやり方だ

*2:0.75の少数第2位以下を切捨てたもの