SPSP — 理論編 3: 数式と厳密な定義
ここまでの 3 ページ (基本編 / 理論編・概念 / 理論編・評価) で概念と検証は説明した。 本ページは数式で厳密に定義する。再現実装が可能なレベルを目指す。 OpenSkill BradleyTerryFull の内部更新式は省略する (公式 docs を参照)。 それ以外の SPSP 固有の式はすべて記述する。
1. 記法
- $t$ = 大会、$u$ = プレイヤー、$p^t_u$ = 大会 $t$ におけるプレイヤー $u$ の placement (順位)
- $n_t$ = 大会 $t$ の参加者数 (重複・DQ・no-show 除外後)
- $d_t$ = 大会 $t$ の eval_date からの経過日数
- $\sigma_u,\ \mu_u$ = プレイヤー $u$ の直対評価レート (OpenSkill BT) の標準偏差・平均
- $r_u$ = プレイヤー $u$ の正規化直対レート (= 順位評価の gain 計算入力)
- 「休日」 = 土日祝。期間中のいずれかが休日なら大会全体を休日扱い (ただし 14 日超は開始日のみで判定)
2. 直対評価 (OpenSkill BradleyTerryFull)
2.1 概要
- 各プレイヤーは平均 $\mu$ と標準偏差 $\sigma$ の 2 パラメータ
- 初期値: $\mu_0 = 25,\ \sigma_0 = 25/3 \approx 8.33$
- skill spread $\beta = 25/6 \approx 4.167$、drift $\tau = 25/300$
- 順序付けスコア (ordinal): $\text{ord}_u = \mu_u - z\, \sigma_u$ (デフォルト $z = 3$)
- 詳細な更新式は OpenSkill 公式 docs を参照
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 で決まる:
明示的 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 年以内 | 1700 | 350 | 1 | 0.3 |
| 2 | Lv1 上位 2048 | (Lv1 と同じ) | 1900 | 700 | 3 | 0.3 |
| 3 | Lv2 上位 1024 | $n_t > 24$、制限大会・下位クラス除外 | 1900 | 700 | 3 | 0.3 |
| 4 | Lv3 上位 512 | $n_t > 24$、休日のみ、制限大会・下位クラス除外 | 1900 | 700 | 3 | 0.3 |
| 5 | Lv4 上位 256 | $n_t > 48$、休日のみ、制限大会・下位クラス除外 | 1900 | 700 | 3 | 0.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% の時間減衰 |