SPSP — 理論編 3: 数式と厳密な定義

ここまでの 3 ページ (基本編 / 理論編・概念 / 理論編・評価) で概念と検証は説明した。 本ページは数式で厳密に定義する。再現実装が可能なレベルを目指す。 OpenSkill BradleyTerryFull の内部更新式は省略する (公式 docs を参照)。 それ以外の SPSP 固有の式はすべて記述する。

1. 記法

2. 直対評価 (OpenSkill BradleyTerryFull)

2.1 概要

2.2 σ floor + μ-compensation

通常の Bradley-Terry では試合を重ねると $\sigma$ が単調減少する。 これだと「覚醒選手」の急上昇を反映しにくい。SPSP では更新後の rating $(\mu_u, \sigma_u)$ に対し、 $\sigma$ が下限 $\sigma_{\text{floor}}$ を下回ったら次のように補正する:

$$\sigma'_u = \max(\sigma_u,\ \sigma_{\text{floor}})$$ $$\mu'_u = \mu_u + z \cdot (\sigma'_u - \sigma_u)$$

この補正によって ordinal は不変:

$$\text{ord}'_u = \mu'_u - z\, \sigma'_u = \mu_u - z\, \sigma_u = \text{ord}_u$$

つまり「一回の試合の結果」は正しく反映されつつ、次回の試合では $\sigma$ が下限以上で確保されているので 更新幅が大きい状態を維持できる。

SPSP 本番では $\sigma_{\text{floor}} = 3.0$。

2.3 Inactivity σ inflation (不参加期間による不確実性増加)

各プレイヤー $u$ の最終出場大会タイムスタンプを $\tau_u$ とおき、現大会 $t$ 開始時点での不参加日数を $d_u = (t_{\text{start}} - \tau_u) / 86400$ で定義する. 試合 update の 直前 に、$\sigma$ を以下の式で sigmoid 曲線で滑らかに膨らませる:

$$\sigma'_u = \max\!\left(\sigma_u,\ \sigma_{\text{floor}} + (\sigma_{\text{infl}} - \sigma_{\text{floor}}) \cdot \mathrm{sigmoid}\!\left(\frac{d_u - D_{\text{mid}}}{D_{\text{scale}}}\right)\right)$$

ただし $d_u \leq D_{\text{grace}}$ のときは何もしない (= $\sigma'_u = \sigma_u$). $\sigma$ を下げる方向には作用しない (max を取るため).

定数意味
$\sigma_{\text{infl}}$$6.0$inactivity による σ の上限 (target)
$D_{\text{mid}}$$540$ 日 (1.5 年)sigmoid の中央 (この日数で σ が floor と target の中間)
$D_{\text{scale}}$$180$ 日sigmoid の幅 (大きいほどなだらか)
$D_{\text{grace}}$$30$ 日この期間以内なら inflation を適用しない

この補正により、長期 inactive 後の復帰戦では BT update 幅が大きくなり、復帰時の本当の実力を素早く反映できる. Sweep 評価では 全 Kendall τ scope で改善 (top_32 で +0.0033, top_8 で +0.0019).

2.4 レア / 常連ゲート (per-player gating)

プレイヤー $u$ の累計大会数を $c_u$、大会 $t$ が休日なら $w_t = 1$、平日なら $w_t = 0$ とおく。 試合 $(u_w, u_l)$ (winner / loser) について、それぞれ独立に rating を更新するか判定する:

$$\text{update}(u) = \begin{cases} \text{Yes} & w_t = 1 \ \lor\ c_u \le M \\ \text{No} & \text{otherwise} \end{cases}$$

ここで $M = 20$ (定数 GATED_M_TOUR)。winner / loser それぞれ独立に判定し、 片方だけ rating が動く試合もあり得る。

つまり「自分の累計大会数が 20 以下」または「その大会が休日開催」のとき直対評価を更新。 ベテラン (>20 大会) は平日大会の影響を受けない。

下位クラスイベント (B/C/D/E クラス等のサブブラケット) は土日開催であっても $w_t = 0$ (= 平日扱い) とする。 お楽しみで参加する常連プレイヤーのレートを下位クラスでの結果で動かさないため。 制限大会 (雛囃子・西武撃 Rising 等) は通常通り $w_t$ を判定 (= 休日であれば $w_t = 1$)。

2.5 DQ (途中棄権) の判定

試合 $m$ について start.gg 側で dq=True となっているものは、BT 学習・SPR / UF 計算から除外する. 一方、大会 $t$ におけるプレイヤー $u$ の DQ 判定 $\mathrm{DQ}(u, t)$ は以下の OR で決まる:

$$ \mathrm{DQ}(u, t) = \mathrm{DQ}^{\text{exp}}(u, t) \;\lor\; \mathrm{DQ}^{\text{imp}}(u, t) $$

明示的 DQ (start.gg 側に dq match が存在):

$$ \mathrm{DQ}^{\text{exp}}(u, t) = (u \in D_t) $$

ここで $D_t$ は大会 $t$ の dq=True match の loser_id 集合. なお phase 内クラス bracket がある大会では、$D_t$ を main bracket / class bracket で分離して保持し、 親 event entry と class virtual entry に別々に適用する.

暗黙的 DQ (試合データありの大会で 1 試合も記録なし):

$$ \mathrm{DQ}^{\text{imp}}(u, t) = \begin{cases} \text{True} & |\mathcal{M}_t| > 0 \;\land\; p_u^t > 2 \;\land\; w_{u,t} = 0 \;\land\; \ell_{u,t} = 0 \\ \text{False} & \text{otherwise} \end{cases} $$

$|\mathcal{M}_t|$ = 大会 $t$ の総試合数, $p_u^t$ = $u$ の placement, $w_{u,t}$ = 勝ち数, $\ell_{u,t}$ = 負け数. 暗黙ルールが保守的 ($w=0 \land \ell=0$) なのは、data 欠損で false-positive を生まないため.

2.6 表示用 Elo 換算

ordinal を Elo の "log-odds" に変換する係数 $\beta_E$:

$$\beta_E = \frac{400}{\ln 10 \cdot \sqrt{2} \cdot \beta} \approx 29.48$$

これを使い、表示用 Elo はオフセット $1000$ を加えて:

$$\text{bt\_gated\_elo}_u = \text{ord}_u \cdot \beta_E + 1000$$

一方、順位評価の gain 計算に使う $r_u$ (正規化直対レート) は、全プレイヤーの最大が $2500$ になるようシフトする:

$$r_u = \text{ord}_u \cdot \beta_E - \bigl(\max_v \text{ord}_v \cdot \beta_E - 2500\bigr)$$

直対評価で値を持たないプレイヤー (該当試合に出ていない等) は $r_u = 1500$ (デフォルト基準値)。

3. 順位評価

3.1 placement → W2W (Wins-to-Win) 変換

ダブルイリミ (double elimination) bracket の構造を仮定する。優勝までに必要な勝ち数 W2W は placement ごとに決まる。 n 人参加の場合、losers bracket と winners bracket を組み立てた上で、各 placement $p$ が losers bracket の何ラウンド目に該当するかが一意に決まる。 たとえば $n = 8$ なら $\text{W2W}(1) = 5,\ \text{W2W}(2) = 4,\ \text{W2W}(3) = 3,\ \text{W2W}(5) = 2,\ \text{W2W}(7) = 1$。

定義: $\text{W2W}(p)$ = placement $p$ の参加者が losers bracket で 勝ち抜いた (= 勝ち上がる必要があった) ラウンド数。 SPSP では大会ごとの placement 列を `W2W` 列に変換してから集計を行う。

3.2 各ラウンドの参加者集合

各大会 $t$ で、参加者をシード番号順に並べ、それぞれの正規化直対レートを $\{r_i\}_{i=1}^{n_t}$ とする ($i$ はシード位置, 1-indexed)。 仮想ダブルイリミの losers' bracket のラウンド $j$ に到達するシード index 集合を $R_j$ とする (winners bracket と losers bracket の組み合わせから構築する標準的なダブルイリミシードの定式化)。

3.3 gain 関数 (Elo win probability)

ラウンド $j$ にいる相手レート $r$ に対し、基準レート $\text{base}$ の人が勝った時の「ゲインの大きさ」:

$$g(r;\ \text{base}) = 1 - \frac{1}{1 + 10^{(r - \text{base})/400}}$$

これは Elo の win probability 式 (= 基準が $\text{base}$ のプレイヤーが、レート $r$ の相手に勝つ確率を反転したもの)。 $r > \text{base}$ なら $g > 0.5$、$r < \text{base}$ なら $g < 0.5$。

3.4 ラウンドポイント $m_j(t)$

レート下限を $\text{floor}$、零回避定数 $\varepsilon = 0.01$ とする:

$$m_j(t) = \varepsilon + \frac{1}{|R_j|} \sum_{i \in R_j} g\bigl(\max(r_i, \text{floor});\ \text{base}\bigr)$$

$r_i < \text{floor}$ なら $\text{floor}$ にクリップしてから $g$ に渡す。 また $g$ の値は $\max(0, g)$ に下限を入れる (極端な負を除く)。

3.5 プレイヤー個別の大会 raw points

プレイヤー $u$ の大会 $t$ における raw points は「勝ち上がったラウンド数 $rw_t(u)$ ぶん」の和:

$$P_{t,u} = \sum_{j=1}^{rw_t(u)} m_j(t)$$

ここで

$$rw_t(u) = \max\bigl(0,\ \text{tier}(n_t) - \text{tier}(p^t_u)\bigr)$$

= 「大会の最大ラウンド数 − 自分が落ちたラウンド」 = 勝ち上がりラウンド数。

3.6 age decay と position factor

時間減衰は月 3% の指数減衰 (= 半減期 ≈ 22.8 ヶ月):

$$\alpha(d) = 0.97^{d/30}$$

プレイヤー $u$ の全大会を $P_{t,u} \cdot \alpha(d_t)$ の降順に並べる。 $i$ 番目 (1-indexed) の大会について:

$$\text{pos}_i = \begin{cases} 1 & i \le N \\ pd^{\,i - N + 1} & i > N \end{cases}$$

すなわち上位 $N$ 件はフル評価、$N+1$ 件目以降は $pd$ の冪で減衰。

3.7 大会の重み付き計上pt

$$w_{t,u} = P_{t,u} \cdot \alpha(d_t) \cdot \text{pos}_{r(u,t)}$$

ここで $r(u,t)$ は重み $P_{t,u} \cdot \alpha(d_t)$ をプレイヤー $u$ の全大会で降順に並べたときの順位。 $w_{t,u}$ が「大会 $t$ がプレイヤー $u$ のスコアに **計上** される実 pt 量」を表す。

3.8 順位評価スコア

$$\text{S}^{\text{rank}}_{\text{Lv}}(u) = \sum_{t \in F_{\text{Lv}}} w_{t,u}$$

(レベル Lv の対象大会フィルタ $F_{\text{Lv}}$ を満たす大会のみで集計)

3.9 表示 Elo

raw の順位評価スコアは大会の難しさ・本数に応じて 0 〜 数十 のオーダーになる。 サイト表示時は $c = 100$ 倍 して整数 3〜4 桁の Elo 風数値にする (順位に影響しない線形変換):

$$E^{\text{rank}}_u = \text{S}^{\text{rank}}(u) \cdot c, \quad c = 100$$

(オフセットなし、つまり raw = 0 のプレイヤーは表示 Elo = 0)

4. 5 層カスケード

各 Lv のパラメータ $(\text{base},\ \text{floor},\ N,\ pd)$ と大会フィルタ $F_k$:

Lv母集団大会フィルタ $\text{base}$$\text{floor}$$N$$pd$
1全プレイヤー$n_t \ge 8$、3 年以内170035010.3
2Lv1 上位 2048(Lv1 と同じ)190070030.3
3Lv2 上位 1024$n_t > 24$、制限大会・下位クラス除外190070030.3
4Lv3 上位 512$n_t > 24$、休日のみ、制限大会・下位クラス除外190070030.3
5Lv4 上位 256$n_t > 48$、休日のみ、制限大会・下位クラス除外190070030.3

プレイヤー $u$ の最終 Lv は到達した最大の $k$:

$$\text{Lv}(u) = \max\{\,k : u \in L_k\,\}$$

スコアはその Lv のスコア。Lv5 まで生き残った人は Lv5 で並び、Lv4 止まりの人は Lv4 で並び、....

5. 総合評価 (ensemble)

直対評価ランクを $R^{\text{bt}}_u$、順位評価ランクを $R^{\text{rank}}_u$ とする。 総合評価の平均順位:

$$\bar{R}_u = \frac{R^{\text{rank}}_u + R^{\text{bt}}_u}{2}$$

最終ランクは $\bar{R}_u$ の昇順。同値は平均 Elo

$$\bar{E}_u = \tfrac{1}{2}\bigl(E^{\text{rank}}_u + E^{\text{bt}}_u\bigr)$$

の降順で tie-break。 ここで $E^{\text{rank}}_u$ は §3.9 の順位評価表示 Elo、$E^{\text{bt}}_u$ は直対評価 (BT-gated) の表示 Elo (ordinal × βE + 1000)。 どちらか片方しか rate されていないプレイヤーは欠けた側を「全プレイヤー数 + 1」で穴埋めしてから平均する。

6. パラメータ一覧

定数意味
LOOKBACK 期間$365 \times 6$直対・順位評価の学習窓 (6 年)
レア / 常連 境界大会数 $M$$20$累計大会数がこの値以下のプレイヤーは平日大会も直対学習に含む
σ floor$3.0$直対評価 $\sigma$ の下限
σ inactivity target $\sigma_{\text{infl}}$$6.0$不参加期間で膨らませる $\sigma$ の上限
$D_{\text{mid}}$$540$sigmoid の中央 (1.5 年)
$D_{\text{scale}}$$180$sigmoid の幅
$D_{\text{grace}}$$30$inactivity inflation 猶予日数
直対基準デフォルト$1500$直対評価がないプレイヤーのデフォルトレート
順位評価 Elo 倍率$100$順位評価スコアの表示 Elo 換算
順位評価 Elo オフセット$0$順位評価表示 Elo の offset
直対評価 Elo オフセット$1000$直対評価表示 Elo の offset
$\beta_E$ (Elo 換算)$\dfrac{400}{\ln 10 \cdot \sqrt{2} \cdot \beta} \approx 29.48$直対評価 ordinal → Elo
OpenSkill $\beta$$25/6 \approx 4.167$BT skill spread
OpenSkill $\tau$$25/300$drift
Lv1 パラメータ$(1700,\ 350,\ 1,\ 0.3)$$(\text{base}, \text{floor}, N, pd)$
Lv2-5 パラメータ$(1900,\ 700,\ 3,\ 0.3)$$(\text{base}, \text{floor}, N, pd)$
$\varepsilon$ (gain)$0.01$ラウンドポイントの零回避
age decay rate$0.97^{d/30}$月 3% の時間減衰