競馬の枠番を求める数式と数列

レースに参加する頭数によって決まる枠番を計算する関数を作りました。作った計算式、VBAプログラム、計算結果の枠番の数列について書きます。

レースの頭数と枠番の関係

枠番は1から8までの最大8枠で、レースの登録頭数(レースに参加する馬の数、以下は頭数と略します)によって一枠に入る馬の数が変わり、馬番1から順に枠番が決まります。

例えば、頭数が9なら次のように馬番8と9が枠番8になります。

馬番 1 2 3 4 5 6 7 8 9
枠番 1 2 3 4 5 6 7 8 8

頭数が10なら次のように馬番7と8は枠番7で、馬番9と10は枠番8です。

馬番 1 2 3 4 5 6 7 8 9 10
枠番 1 2 3 4 5 6 7 7 8 8

頭数が8以下なら、馬番と枠番は同じ値です。

枠番は8が最大で、頭数が増えると全ての馬番が枠番1から8に収まるように、一枠に入る馬の数が増やすのです。

頭数1から28までの馬番と枠番の対応は次のようになります。

     馬番
頭数 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
1    1  
2    1  2
3    1  2  3  
4    1  2  3  4
5    1  2  3  4  5  
6    1  2  3  4  5  6
7    1  2  3  4  5  6  7  
8    1  2  3  4  5  6  7  8
9    1  2  3  4  5  6  7  8  8  
10   1  2  3  4  5  6  7  7  8  8
11   1  2  3  4  5  6  6  7  7  8  8  
12   1  2  3  4  5  5  6  6  7  7  8  8
13   1  2  3  4  4  5  5  6  6  7  7  8  8  
14   1  2  3  3  4  4  5  5  6  6  7  7  8  8
15   1  2  2  3  3  4  4  5  5  6  6  7  7  8  8  
16   1  1  2  2  3  3  4  4  5  5  6  6  7  7  8  8
17   1  1  2  2  3  3  4  4  5  5  6  6  7  7  8  8  8  
18   1  1  2  2  3  3  4  4  5  5  6  6  7  7  7  8  8  8
19   1  1  2  2  3  3  4  4  5  5  6  6  6  7  7  7  8  8  8  
20   1  1  2  2  3  3  4  4  5  5  5  6  6  6  7  7  7  8  8  8
21   1  1  2  2  3  3  4  4  4  5  5  5  6  6  6  7  7  7  8  8  8  
22   1  1  2  2  3  3  3  4  4  4  5  5  5  6  6  6  7  7  7  8  8  8
23   1  1  2  2  2  3  3  3  4  4  4  5  5  5  6  6  6  7  7  7  8  8  8  
24   1  1  1  2  2  2  3  3  3  4  4  4  5  5  5  6  6  6  7  7  7  8  8  8
25   1  1  1  2  2  2  3  3  3  4  4  4  5  5  5  6  6  6  7  7  7  8  8  8  8  
26   1  1  1  2  2  2  3  3  3  4  4  4  5  5  5  6  6  6  7  7  7  7  8  8  8  8
27   1  1  1  2  2  2  3  3  3  4  4  4  5  5  5  6  6  6  6  7  7  7  7  8  8  8  8  
28   1  1  1  2  2  2  3  3  3  4  4  4  5  5  5  5  6  6  6  6  7  7  7  7  8  8  8  8

ちなみに、JRAのレースの最大頭数は18ですが、JRA発信の競馬データJVDataの仕様書では最大頭数は28になっています。これは80年代、90年代には19頭以上の頭数のレースがあったからです。ここで考える頭数は1以上の整数なので制限はありませんが、実際、役に立つのは頭数18までです。頭数と馬番から枠番を求める関数がほしいだけなら、上のように値を決め打ちでプログラミングしてしまってもいいのですけど、面白そうだから枠番の数列を作ってみたかったのです。

頭数と馬番から枠番を求める数式

上の頭数・馬番と枠番の対応表から枠番の数式を考えました。対応表がシンプルなので数式もシンプルですが、場合分けが少し複雑です。考え方は後で説明するので、まずは数式を書きます。

頭数を\(N\)(\(N = 1, 2, 3, ...\))、馬番を\(k\)(\(k = 1, 2, ..., N\))、\(N\)と\(k\)で決まる枠番を\(n_k\)とします。

\(N \leq 8\)の場合、

\[
n_k = k.
\]

\(N > 8\)の場合、加えて次の場合に分かれます。

\(k < k_M\)の場合、

\[
n_k =
\begin{cases}
n_{k-1} + 1 & \text{if $k-1 \ mod \ M-1 = 0$,} \\
n_{k-1} & \text{if $k-1 \ mod \ M-1 \neq 0$,} \\
n_0 = 0.
\end{cases}
\]

\(k \geq k_M\)の場合、

\[
n_k =
\begin{cases}
n_{k-1} + 1 & \text{if $k-k_M \ mod \ M = 0$,} \\
n_{k-1} & \text{if $k-k_M \ mod \ M \neq 0$,} \\
n_0 = 0.
\end{cases}
\]

ここで、\(M\)は一枠に入る最大頭数を表していて、\(\frac{N}{8} \leq M < \frac{N}{8}+1\)の整数となります。\(k_M\)は一枠に入る頭数が\(M-1\)から\(M\)へ変わる馬番を表していて、\(k_M = (M-1)(8-l)+1, \ l = N \ mod \ (M-1)\)となります。ただし、\( l = 8(M-1) \ \text{if $N \ mod \ (M-1) = 0$.} \)

VBAでのコーディングと入出力

考えた私が見ても、数式だけではピンときません。プログラムを書いて計算します。

先ほどの数式をVBAで実装すると次のようになります。Wakubanという名前の関数です。Wakuban(N, k)の入力は頭数と馬番で、枠番を出力します。

' 頭数HorseCount(=N)、馬番HorseNumber(=k)から枠番nkを求めて返す
Function Wakuban(ByRef HorseCount As Long, ByRef HorseNumber As Long) As Long

  Dim M  As Long
  Dim L  As Long
  Dim kM As Long

  ' 入力値が不正 または 馬番が0の場合は0を返す
  If HorseCount < HorseNumber Or HorseNumber <= 0 Then Exit Function
  
  If HorseCount <= 8 Then
    ' 頭数が8頭以下の場合は 枠番=馬番 を返す
    Wakuban = HorseNumber
  
  Else
    ' 頭数が8頭より大きい場合
    M = WorksheetFunction.RoundUp(HorseCount / 8, 0) ' 一枠に入る最大頭数
    L = HorseCount Mod 8 * (M - 1)                   ' 1, 2, ..., 8となるカウント
    If L = 0 Then L = 8 * (M - 1)
    kM = (M - 1) * (8 - L) + 1                       ' 一枠に入る頭数がM-1からMへ変わる馬番
    If HorseNumber < kM Then
      ' 一枠に入る頭数がM-1の場合
      If (HorseNumber - 1) Mod (M - 1) = 0 Then
        ' HorseNumber-1とHorseNumberの馬番が違う枠番の場合
        Wakuban = Wakuban(HorseCount, HorseNumber - 1) + 1
      Else
        ' 同じ枠番の場合
        Wakuban = Wakuban(HorseCount, HorseNumber - 1)
      End If
    Else
      ' 一枠に入る頭数がMの場合
      If (HorseNumber - kM) Mod M = 0 Then
        ' HorseNumber-1とHorseNumberの馬番が違う枠番の場合
        Wakuban = Wakuban(HorseCount, HorseNumber - 1) + 1
      Else
        ' 同じ枠番の場合
        Wakuban = Wakuban(HorseCount, HorseNumber - 1)
      End If
    End If
  
  End If

End Function

Wakuban(N, k)を使って頭数によって決まる枠番の数列を作ってみます。WakubanSeriesという名前の関数です。WakubanSeries(N)の入力は頭数で、馬番と枠番の数列をイミディエイトウィンドウに出力します(Cなどの標準出力と同じ)。

Sub WakubanSeries(ByRef HorseCount As Long)

  Dim HorseNumber As Long

  Debug.Print "馬番k", "枠番nk"
  For HorseNumber = 1 To HorseCount
    Debug.Print HorseNumber, Wakuban(HorseCount, HorseNumber)
  Next

End Sub

例えば、頭数\(N = 10\)の馬番と枠番の数列を得るには、WakubanSeries(10)を実行します。

WakubanSeries(10)
馬番k         枠番nk
 1             1 
 2             2 
 3             3 
 4             4 
 5             5 
 6             6 
 7             7 
 8             7 
 9             8 
 10            8 

頭数\(N = 18\)なら、WakubanSeries(18)を実行します。

WakubanSeries(18)
馬番k         枠番nk
 1             1 
 2             1 
 3             2 
 4             2 
 5             3 
 6             3 
 7             4 
 8             4 
 9             5 
 10            5 
 11            6 
 12            6 
 13            7 
 14            7 
 15            7 
 16            8 
 17            8 
 18            8 

枠番の数式導出の考え方

枠番の決定方法は、

  1. 馬番と等しい(\(n_k = k\))
  2. 1つ前の馬番の枠番と同じ値(\( n_k = n_{k-1}\))
  3. 1つ前の馬番の枠番より1大きい(\( n_k = n_{k-1}+1\))

の3通りしかありません。

この3通りの方法の場合分けが一見ややこしいので、次のような表を作りました。

N1枠2枠3枠4枠5枠6枠7枠8枠
110000000
211000000
311100000
411110000
511111000
611111100
711111110
811111111
911111112
1011111122
1111111222
1211112222
1311122222
1411222222
1512222222
1622222222
1722222223
1822222233
1922222333
2022223333
2122233333
2222333333
2323333333
2433333333
2533333334
2633333344
2733333444
2833334444
(1) \(N \leq 8\)と\(N > 8\)の場合分け

この表は、頭数\(N\)によって変化する「一枠に入る頭数」を表しています。

この表を眺めて、

  1. \(1 \leq N \leq 8\)の場合、一枠に入る頭数は0または1である。
  2. \(9 \leq N \leq 16\)の場合、一枠に入る頭数は1または2である。
  3. \(17 \leq N \leq 24\)の場合、一枠に入る頭数は2または3である。
  4. \(25 \leq N \leq 32\)の場合、...

のように一枠に入る頭数のパターンを見つけました。

まず、\(1 \leq N \leq 8\)の場合と、\(9 \leq N \)の場合では、パターンが違います。

\(1 \leq N \leq 8\)の場合は、一枠に入る頭数は、\(N = 1, 2, ...\)の順に増えていくだけです。このパターンからまず、\(N \leq 8\)の場合は、\(n_k = k\)となります。

\(9 \leq N\)の場合は、一枠に入る頭数は、その最大頭数\(M\)と\(M-1\)の2値で、\(M-1\)から\(M\)へ変化する点(\(k_M\))は8枠から1枠へ向かってずれていきます。このパターンから、\(k < k_M\)の場合と\(k \geq k_M\)の場合でそれぞれ、\( n_k = n_{k-1}\)または\( n_k = n_{k-1}+1\)となります。

(2) \(k < k_M\)と\(k \geq k_M\)の場合分け

頭数\(N\)は一枠に入る最大頭数\(M\)による次の不等式が成り立ちます。

\[
8(M-1) < N \leq 8M
\]

この不等式を次のように、

\[
8(M-1) < N \\
M < \frac{N}{8}+1
\]

と、

\[
N \leq 8M \\
\frac{N}{8} \leq M
\]

に直して、

\[
\frac{N}{8} \leq M < \frac{N}{8}+1
\]

となりますので、\(M\)は\(\frac{N}{8} \leq M < \frac{N}{8}+1\)の整数です。例えば、\(N = 8\)なら\(1 \leq M < 2\)なので\(M = 1\)。\(N = 17\)なら\(2.125 \leq M < 3.125\)なので\(M = 3\)となります。Excelは少数を切り上げる関数RoundUp()があるので、RoundUp(N, 0)で\(M\)が求まります。

\(k_M\)の計算式を考えるために、上表に、\(M\)、\(l\)などの列を加えます。

N1枠2枠3枠4枠5枠6枠7枠8枠\(M-1\)\(M\)\(8(M-1)\)\(N \ mod \ 8(M-1)\)\(l\)\(8-l\)\(k_M\)
9111111121281178
10111111221282267
11111112221283356
12111122221284445
13111222221285534
14112222221286623
15122222221287712
16222222221280801
1722222223231611715
1822222233231622613
1922222333231633511
202222333323164449
212223333323165537
222233333323166625
232333333323167713
243333333323168801
2533333334342411722
2633333344342422619
2733333444342433516
2833334444342444413

この表からわかるように、\(l\)は\(l = 1, 2,..., 8, 1, 2,..., 8\)となっています。\(M\)が変わる\(N\)を周期に\(l\)は1から8を繰り返しています。

先ほど、\(k_M = (M-1)(8-l)+1, \ l = N \ mod \ (M-1). \ \)ただし、\( l = 8(M-1) \ \text{if $N \ mod \ (M-1) = 0$.} \)であると書きました。これは、\(k_M\)が一枠に入る頭数が\(M-1\)から\(M\)へ変わる馬番になるように、この表から\(mod\)演算を工夫して得ました。

(3) \(k-1 \ mod \ M-1 = 0\)と\(k-1 \ mod \ M-1 \neq 0\)の場合分け

\(N > 8\)かつ\(k < k_M\)の場合に、\( n_k = n_{k-1}\)または\( n_k = n_{k-1}+1\)となる場合分けは、

\[
n_k =
\begin{cases}
n_{k-1} + 1 & \text{if $k-1 \ mod \ M-1 = 0$,} \\
n_{k-1} & \text{if $k-1 \ mod \ M-1 \neq 0$,} \\
n_0 = 0.
\end{cases}
\]

と書きました。

\(k < k_M\)ですから、一枠に入る頭数は\(M-1\)です。

一つ前の馬番(\(k-1\))が\(M-1\)の倍数ならば、枠番は1つ前の枠番より1大きい値になります。つまり、\(k-1 \ mod \ M-1 = 0\)が成り立つならば、\( n_k = n_{k-1}+1\)です。

一つ前の馬番(\(k-1\))が\(M-1\)の倍数でないならば、枠番は1つ前の枠番と同じ値になります。つまり、\(k-1 \ mod \ M-1 \neq 0\)が成り立つならば、\( n_k = n_{k-1}\)です。

(4) \(k-k_M \ mod \ M = 0\)と\(k-k_M \ mod \ M \neq 0\)の場合分け

\(N > 8\)かつ\(k \geq k_M\)の場合に、\( n_k = n_{k-1}\)または\( n_k = n_{k-1}+1\)となる場合分けは、

\[
n_k =
\begin{cases}
n_{k-1} + 1 & \text{if $k-k_M \ mod \ M = 0$,} \\
n_{k-1} & \text{if $k-k_M \ mod \ M \neq 0$,} \\
n_0 = 0.
\end{cases}
\]

と書きました。

こちらも考え方は同じです。異なるのは、\(k \geq k_M\)なので一枠に入る頭数が\(M\)ということと、馬番を\(k_M\)からの相対的な馬番に変換するために、\(k - k_M + 1\)としていることです。

\(k - k_M + 1\)の一つ前の馬番(\(k - k_M\))が\(M\)の倍数かを考えるので、\(k-k_M \ mod \ M = 0\)というわけです。

まとめ

レースに参加する馬の頭数と馬番から枠番を求める数式を作りました。作った数式は次のとおりです。

頭数を\(N\)(\(N = 1, 2, 3, ...\))、馬番を\(k\)(\(k = 1, 2, ..., N\))、\(N\)と\(k\)で決まる枠番を\(n_k\)とします。

\(N \leq 8\)の場合、

\[
n_k = k.
\]

\(N > 8\)かつ\(k < k_M\)の場合、

\[
n_k =
\begin{cases}
n_{k-1} + 1 & \text{if $k-1 \ mod \ M-1 = 0$,} \\
n_{k-1} & \text{if $k-1 \ mod \ M-1 \neq 0$,} \\
n_0 = 0.
\end{cases}
\]

\(N > 8\)かつ\(k \geq k_M\)の場合、

\[
n_k =
\begin{cases}
n_{k-1} + 1 & \text{if $k-k_M \ mod \ M = 0$,} \\
n_{k-1} & \text{if $k-k_M \ mod \ M \neq 0$,} \\
n_0 = 0.
\end{cases}
\]

ここで、\(M\)は、

\[
\frac{N}{8} \leq M < \frac{N}{8}+1
\]

を満たす整数。一枠に入る最大頭数を表しています。

\(k_M\)は、

\[
k_M = (M-1)(8-l)+1, \\
l =
\begin{cases}
N \ mod \ (M-1) & \text{if $N \ mod \ (M-1) \neq 0$,} \\
8(M-1) & \text{if $N \ mod \ (M-1) = 0$.}
\end{cases}
\]

で、一枠に入る頭数が\(M-1\)から\(M\)へ変わる馬番を表しています。

また、この数式をVBAで実装して、枠番の数列を出力しました。

Kosuke Maeda / まえだこうすけ

「機械学習で競馬予想して勝てるのか?」をテーマに活動中! QiitaにはR、VBAなどのTipsを投稿しています。