フィギュアスケートの4回転半・5回転ジャンプの基礎点をscipy.optimizeを使って見積もってみる

フィギュアスケートのグランプリシリーズが開幕し、2022-2023シーズンが本格的に始まりました！今シーズンはマリニン選手が4回転半ジャンプを決めたことが話題になりました。

しかし、マリニン選手いわく4回転半ジャンプは高得点を得るためというより練習のために入れているようです[^Malinin]。これは4回転半ジャンプの基礎点が低いためで、将来4回転半ジャンプの基礎点が上がれば組み込むことがより合理的になるでしょう。

[^Malinin]: [Ilia Malinin: Ready for Skate America (A Divine Sport)](https://adivinesport.com/2022/10/15/ilia-malinin-ready-for-skate-america/)

4回転半ジャンプの基礎点が低すぎるというのは定説になっています。また5回転ジャンプの基礎点は設定されていません。そこで、この記事では4回転以下のジャンプの基礎点から4回転半以上のジャンプの基礎点を見積もってみることにします。

# ジャンプの基礎点 (2022-2023シーズン)

フィギュアスケートでジャンプ要素として点数がつくのは以下の6種類です。

- トウループ
- サルコウ
- ループ
- フリップ
- ルッツ
- アクセル

それぞれの種類について1回転から4回転までの基礎点が設定されています。

|  | トウループ | サルコウ | ループ | フリップ | ルッツ | アクセル[^Axel] |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| 1回転 | 0.4 | 0.4 | 0.5 | 0.5 | 0.6 | 1.1 |
| 2回転 | 1.3 | 1.3 | 1.7 | 1.8 | 2.1 | 3.3 |
| 3回転 | 4.2 | 4.3 | 4.9 | 5.3 | 5.9 | 8.0 |
| 4回転 | 9.5 | 9.7 | 10.5 | 11.0 | 11.5 | 12.5 |

アクセル以外の4回転ジャンプは同じ種類の3回転ジャンプの2倍近い基礎点ですが、4回転アクセルは3回転アクセルの1.56倍の基礎点しかありません。

[^Axel]: アクセルは半回転多いです。例えば3回転アクセルは3回転半ジャンプです。

# 仮定

回転数を $r$、ジャンプの種類による補正を $c$ とします。このとき関数 $f$ を用いて基礎点が $f(r+c)$ の形で書けると仮定します。

関数 $f$ は $f(x) = kx^t$ または $f(x) = ke^{tx}$ のいずれかと仮定します。

# 方法

$r$ 回転の種類 $c$ のジャンプの実際の基礎点を $s_{r, c}$ として誤差の2乗の総和 $\sum_{r, c} (s_{r, c} - f(r + c))^2$ を最小化する関数 $f$ と補正項 $c$ を求めます。ただし、適正基礎点が確立していないと思われる4回転アクセルは除きます。

最小化したい関数を定式化できたので[scipy.optimize.minimize](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html)関数で求めることにします。今回はmethod引数には['Powell'](https://docs.scipy.org/doc/scipy/reference/optimize.minimize-powell.html)を指定しました。

関数のパラメータを不定にするとうまく収束しなかったため手作業で試したところ $f(x) = kx^3$ がよいことが分かりました。

# コード

最適化の本質部分は1行で書けます！

```python
import scipy.optimize


def f(x):
    SOV = [[0.4, 0.4, 0.5, 0.5, 0.6, 1.1],
           [1.3, 1.3, 1.7, 1.8, 2.1, 3.3],
           [4.2, 4.3, 4.9, 5.3, 5.9, 8.0],
           [9.5, 9.7, 10.5, 11.0, 11.5, 12.5]]
    res = 0.0
    for i in range(4):
        for j in range(6):
            if i == 3 and j == 5:
                continue
            a = i + 1 + x[j]
            res += (SOV[i][j] - x[6] * a**3)**2
    return res


x = [1] * 7
res = scipy.optimize.minimize(f, x, method='Powell')
print(res)

for i in range(6):
    sov = [None] * 6
    for j in range(6):
        a = i + res.x[j]
        sov[j] = format(res.x[6] * a**3, '.1f')
    print(*sov)
```

# 実行結果

```
   direc: array([[ 1.        ,  0.        ,  0.        ,  0.        ,  0.        ,
         0.        ,  0.        ],
       [ 0.        ,  1.        ,  0.        ,  0.        ,  0.        ,
         0.        ,  0.        ],
       [ 0.        ,  0.        ,  1.        ,  0.        ,  0.        ,
         0.        ,  0.        ],
       [ 0.        ,  0.        ,  0.        ,  1.        ,  0.        ,
         0.        ,  0.        ],
       [ 0.        ,  0.        ,  0.        ,  0.        ,  1.        ,
         0.        ,  0.        ],
       [ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
         1.        ,  0.        ],
       [ 0.1527967 ,  0.15355233,  0.15443623,  0.15756659,  0.16456808,
         0.14584337, -0.01444201]])
     fun: 0.3036458737995145
 message: 'Optimization terminated successfully.'
    nfev: 1175
     nit: 13
  status: 0
 success: True
       x: array([0.30100402, 0.32813902, 0.45700614, 0.53008824, 0.61813608,
       1.06324816, 0.11856337])
0.0 0.0 0.0 0.0 0.0 0.1
0.3 0.3 0.4 0.4 0.5 1.0
1.4 1.5 1.8 1.9 2.1 3.4
4.3 4.4 4.9 5.2 5.6 8.0
9.4 9.6 10.5 11.0 11.7 15.4
17.7 17.9 19.3 20.1 21.0 26.4
```

# 現行基礎点と推定基礎点の比較

数値が2つ書かれているセルは左側が現行基礎点、右側が推定基礎点です。

数値が1つしか書かれていないセルは現行基礎点と推定基礎点が同じです。

|  | トウループ | サルコウ | ループ | フリップ | ルッツ | アクセル[^Axel] |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| 0回転 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 → **0.1** |
| 1回転 | 0.4 → **0.3** | 0.4 → **0.3** | 0.5 → **0.4** | 0.5 → **0.4** | 0.6 → **0.5** | 1.1 → **1.0** |
| 2回転 | 1.3 → **1.4** | 1.3 → **1.5** | 1.7 → **1.8** | 1.8 → **1.9** | **2.1** | 3.3 → **3.4** |
| 3回転 | 4.2 → **4.3** | 4.3 → **4.4** | **4.9** | 5.3 → **5.2** | 5.9 → **5.6** | **8.0** |
| 4回転 | 9.5 → **9.4** | 9.7 → **9.6** | **10.5** | **11.0** | 11.5 → **11.7** | 12.5 → **15.4** |
| 5回転 | 17.7 | 17.9 | 19.3 | 20.1 | 21.0 | 26.4 |

3回転と4回転のルッツの基礎点の乖離がやや大きいですが、他の4回転以下のジャンプでは0.1点以内に収まっています。

4回転アクセルの基礎点は現行より3点近く高い推定結果となりました。また、5回転トウループやサルコウは18点弱と推定されました。皆さんの感覚に合致しているでしょうか。

# まとめ

この記事ではscipy.optimizeを使って4回転半・5回転ジャンプの基礎点を見積もりました。関数の定式化ができてうまく収束するならば簡単なコードで最適化できるので、皆さんも試してみましょう！

## 余談：基礎点を決定する人達、およびその人達に意見を言いたい人たちへ

基礎点を決めるときに必ずしもこの記事を参考にする必要はありません。4回転半以上では4回転以下とは難易度の上がり方が変わるかもしれません。4回転半以上のジャンプの危険性が大きいと考えられるなら意図的に基礎点を低くする、もしくは禁止することも考えられます。

ルールは競技のあり方を決めるものです。4回転半ジャンプが受け入れられるべきものだと判断されれば今後のルール改正で基礎点が上がるでしょうし、高難度ジャンプよりそれ以外の演技の質を重視すべきと判断されれば基礎点が上がらないかもしれません。

一人一人よいと思う演技は違うのでファンとしては納得しづらいかもしれませんが、観戦するときは今あるルールを受け入れるほうが楽しめると思います！