SPSP — 理論編 1: 概念

基本編で SPSP が「順位評価」「直対評価」「総合評価」の 3 つで成り立っていることは紹介した。 このページでは、そのアルゴリズムが どんな考え方で動いているのか を、数式抜きの言葉だけで説明する。 読み終わると「なぜ強い相手に勝つと大きく上がるのか」「なぜ大会順位ポイントが上位ほど大きく増えるのか」 といった理屈が概念として腑に落ちる状態を目指す。
厳密な検証 (どのパラメータがなぜその値か) は 理論編・評価、 数式そのものは 理論編・数式 で扱う。

1. 直対評価の仕組み (まずこっちから)

直対評価は、よくあるオンライン対戦レーティング (例: スマメイトの 1v1 レート) と 同じ系統の Bradley-Terry レーティング を採用している。 試合 1 つの勝敗が起きるたびに、両プレイヤーのレートをほんの少しずつ更新していく方式だ。

1.1 ベースの考え方: 「期待勝率」と「実結果」のズレを反映

レーティングシステムは、現在の両者のレートから「この対戦カードならプレイヤー A が勝つ確率はだいたい これくらい」という 期待勝率 を算出する。 そして実際の試合が終わったあと、期待勝率と実結果のズレを見て、ズレた分だけレートを動かす。

これを直感に言い換えると: 「強い相手に勝てば大きく上がる、格下に勝っても小幅、格下に負けると痛い」。 総当たり戦をしなくても、ランダムに当たった試合を積み重ねるだけでだんだん正しい序列に収束していく。 これがレーティング系の強みである。

SPSP が採用している直対評価は、OpenSkill ライブラリの BradleyTerryFull というモデル。 Elo 系のシンプルな計算をベイズ的に拡張したものと考えてよい。 具体的な式は数式編にある。

1.2 もう一つの軸: 不確実性 (分散)

直対評価は単なる「レート 1 つ」ではなく、「現在のレート推定値」と「その推定がどれくらい不確実か」 という 2 つの値をプレイヤーごとに持っている。後者がいわゆる分散・標準偏差にあたる。 不確実性が大きいプレイヤーほど、1 試合の結果でレートが大きく動く。

つまり「最初は粗く決め、試合数が増えるにつれて精度を上げていく」という挙動が、 レートと不確実性の組で自然に表現できる。これは Bradley-Terry / OpenSkill 系の本質的な利点。

2. σ floor — 不確実性を狭めすぎないコツ

そのままの Bradley-Terry を長期間動かすと、ある問題が出てくる: 試合数が増えるほど不確実性が縮んでいき、ベテランは何百試合をしてもレートがほとんど動かなくなる ということ。 これ自体は数学的には正しい (推定が確信に近づくため) のだが、競技シーンの実態とは噛み合わない場面がある。

実際のシーンでは、何年も平凡だった選手が ある日突然強くなる (覚醒) ことがある。 キャラ変、立ち回り改善、研究の成果といった理由で、過去のレートが急に過小評価になるケースだ。 不確実性が小さくなりすぎていると、そうした「最近本当に強くなった」シグナルにシステムが追従できない。

そこで SPSP では、不確実性に下限 (= σ floor) を設ける。 試合をどれだけ重ねても、不確実性はこの下限より下には行かない。 結果として、ベテランでも勝ち続ければそれなりに上昇し、覚醒選手のレートが急速に伸びるようになる。

ただし下限を上げすぎると「ベテランのレートがフワフワして安定しない」副作用が出る。 シグナル (覚醒) を拾うのとノイズを抑えるののバランスが必要で、 現状の値は実評価上のスイートスポット。評価編で議論する。

2.1 不参加期間による不確実性の増加 (inactivity inflation)

σ floor で「下限」は決めたが、長期間試合をしていないプレイヤーには 逆に不確実性を増やす 仕組みも入れている。 1 年以上大会に出ていないプレイヤーは、レートが「過去の実力」のままで止まっていて、復帰時の本当の実力が分からない状態。 これに合わせて σ を膨らませることで、復帰戦の結果がより柔軟にレートへ反映されるようにする。

増加カーブは sigmoid 曲線で滑らか に作っており、ある日を境にいきなり σ がジャンプすることはない。 また σ を下げる方向には作用しない (= 通常の試合更新で σ が小さくなるのは妨げない)。

このルールは 評価編 のパラメータ sweep で 全 scope の Kendall τ が改善 することを確認して採用したもの。step 関数 (= 1 年経過した瞬間に σ をジャンプ) よりも sigmoid のほうが顕著に良い結果。

3. レア / 常連ゲート

直対評価には、もう 1 つ独自の工夫がある。大会の「種類」によって学習対象を切り替える 仕組みだ。 SPSP はこれを「ゲート (gating)」と呼んでいる。

3.1 仕分けルール

3.2 なぜ常連は休日のみ?

平日に開催されるスマコミ・スマパといった大会は、強者が「ガチ大会の前の練習」「キャラ調整」「手抜き気味の参加」になりがちで、 本来の実力どおりの結果が出ないことが多い。 こうした試合を常連のレートに反映すると、本気の試合のレートが歪んでしまう。

一方で、休日に開催される大会は、参加者がスケジュールを空けて本気で出ているケースが大半なので、 本来の実力差が結果に出やすい。常連は休日の試合だけを使うことで、レートの精度を上げる という発想。

プレ大会の扱い: 大会名に「プレ大会」「プレオフ」などの prefix を含む大会は、 実暦の開催日が土日祝でも 平日扱い として処理される (= 常連プレイヤーの直対学習および Lv4/Lv5 の順位評価集計から除外)。 本戦前の練習・調整目的の大会が多く、本気の結果が出にくいため。
下位クラスイベントの扱い: メイン大会のサブイベントである 下位クラスイベント (Bクラス・Cクラス・…) は、直対評価では 平日大会と同じ扱い となる (= レアプレイヤーのみ学習対象、常連プレイヤーのレートは動かさない)。 順位評価でも Lv3 / Lv4 / Lv5 では除外 となる (Lv1 / Lv2 のみで集計対象)。
制限大会の扱い: 出場資格を制限している大会 (雛囃子・灰神楽・鬼灯火・西武撃 Rising 等の 制限大会) は、 順位評価では Lv3 / Lv4 / Lv5 で除外 される (Lv1 / Lv2 のみで集計対象)。 直対評価には通常通り反映される (= 常連プレイヤーのレートも動く)。 順位評価を Lv2 (Top 1024) までに留めることで上位帯への影響は十分に抑えられるため、直対評価まで除外する必要はないとの判断。 一方の 下位クラスイベント は、メイン大会と並行開催のサブイベントという性格上、お楽しみ参加の常連プレイヤーが一定数いるため、 直対評価では平日扱いとしてレートを動かさない (= 上記参照)。

3.3 「混在」する試合の扱い

レアと常連が平日大会で当たった場合は、片側だけがレートを更新する。 つまりレア側のレートだけ動き、常連側は試合がなかったかのように据え置き。 これにより、平日のノイズで常連の評価が歪むのを防ぎつつ、レアにとっては学習データを増やせる。

3.4 DQ (途中棄権) の扱い

試合に出ずに棄権 (= DQ) となったプレイヤーは、その試合をスコア計算から取り除く。 具体的には、start.gg 側で「dq」フラグが立っている試合は、勝敗もスコアもなかったこととして扱い、 直対評価 (BT) の学習にも、対戦相手の SPR / UF の計算にも使われない。

一方で「この大会から DQ した」という事実だけは保持しておき、 プレイヤーページの大会履歴では SPR の代わりに DQ と表示する。これによって「最終 placement だけ見ると最下位だが、実際には不参加 = 実力評価の対象外」を区別できる。

DQ の判定ロジック (2段階):
  1. 明示的 DQ: matches.json に dq=True の試合があれば、その敗者を DQ 扱い
  2. 暗黙的 DQ (no-show): 試合データが取れている大会で、 参加者 standings に名前があるのに 1 試合もしていない (wins=0 かつ losses=0)、 かつ placement が 3 位以下のプレイヤーは「不参加扱い」として DQ 認定
暗黙的 DQ ルールが保守的 (wins=0 AND losses=0) なのは、 データ欠損 (= start.gg API から一部試合が取得できないケース) で誤って DQ 扱いしないため。 1 試合でも記録があれば「参加した」とみなす。
下位クラス bracket での DQ: 篝火#15 等のメイン+クラス bracket 構成の大会では、メインから DQ したのか下位クラスから DQ したのかを別々に判定する。 例えば「メインで普通に最速敗退し、下位クラスで不参加 DQ」というケースでは、 メイン entry は通常の placement、下位クラス virtual entry のみ DQ となる。

4. 順位評価の核心アイデア

順位評価は、各大会の「最終順位」をベースにポイントを配るシステム。 ただし単純に「1 位は 10 点」「2 位は 7 点」とテーブルで配るのではなく、 その大会の参加者の強さに応じて配点を動的に決める。

はじめに押さえておきたい性質:

4.1 順位は「敗者復活で何ラウンド勝ち上がったか」に等しい

SPSP では、大会の順位を 敗者復活トーナメント (ダブルイリミの負け側ブラケット) の構造に当てはめて解釈する。 敗者復活ブラケットは、負けた人が下に集まり、そこからさらにトーナメントを進めていく構造をしている。 優勝者は全ラウンドを勝ち抜けた人、最下位はラウンドゼロで終わった人になる。

つまり、最終順位を聞けば「どのラウンドまで勝ち上がったか」が機械的に決まる。 ここでは実際にダブルエリミ大会である必要はなく、シングルエリミやスイス大会、リーグでも、 最終順位だけ揃っていれば「敗者復活ブラケットならこのラウンドで敗退したのと等価」と換算できる。

4.2 ラウンドの難しさ = そのラウンドの「場」にいる参加者の平均 gain

次に、各ラウンドの「難しさ」をどう測るか。 SPSP は、大会の参加者をシード番号で並べたうえで 仮想的なダブルイリミの敗者ブラケットを組み、 各ラウンドにどのシードが「居る」かを機械的に決める。そのラウンドに居る参加者の 直対評価レートから計算される gain (= Elo のレート増加量) を平均して、ラウンドの難しさにする。

重要なのは 実際の大会の進行 (誰が勝ち上がったか) は見ていない 点。 あくまで「シードどおりに進めばこのラウンドの場にはこの人たちが居るはず」という 想定だけで全ラウンドの強さを大会開始前に確定させる。

8 人ダブルイリミの敗者ブラケットを模式化すると下のようになる (5 ラウンド構造):

WB R1 敗者 4 人 WB R2 敗者 2 人 WB 決勝敗者 1 人 WB 優勝者 1 人 Round 1 4 人 Round 2 4 人 Round 3 2 人 Round 4 LB 決勝 · 2 人 Round 5 Grand Final · 2 人 7位 / 8位 5位 / 6位 4位 3位 2位 が敗退 が敗退 が敗退 が敗退 が敗退 → 1位 青破線 = 勝者ブラケット側からの落下 · 赤破線 = そのラウンドで敗退する placement · 矢印 = 勝者の進行

各ラウンドの「場」には、そのラウンドで敗退する人と、そこを勝ち抜けて次のラウンドへ進む人の 両方 が含まれている。 SPSP の「ラウンドの難しさ」は、その 場にいる全員 (= 勝つ人 + 負ける人) の gain を平均することで、 「そのラウンドの場の平均的な強さ」を表現している。

そして「ラウンドの難しさポイント」は、スマメイトと同じ Elo レーティングを使うとして、 その場にいる相手 1 人ずつに勝った時のレート増加量を計算し、それを全員で平均したもの。 つまり 「強い人だらけのラウンドを勝ち抜けた = 1 試合あたりの価値が高い」 という発想がそのままポイントに反映される。

「自分のレート」は固定値を使う: ポイントを計算するときの「勝った人のレート」には実際のレートを使わず、 Lv ごとに決められた 固定の基準レート を使う (Lv1 は控えめ、Lv2 以降は高め)。 これにより、レートが高い人と低い人のあいだに不公平な差が生まれるのを防ぎ、 どのプレイヤーも同じものさしで点数化できる。

具体的な数値例 (8 人大会で各 mj を実際に計算する流れ) はページ末尾の §8. 具体例 にある。

4.3 大会のポイント = 勝ち上がったラウンドの難しさの合計

あるプレイヤーが大会で「ラウンド k まで勝ち上がった」なら、 そのプレイヤーがその大会で稼ぐポイントは ラウンド 1 から k までの「難しさポイント」の合計。 上位ラウンドほど難しさが大きいので、上位入賞ほど合計が大きく増える設計になっている。

「ラウンド 1 から勝ち上がった分のポイントを順に足し合わせるだけ」というシンプルな構造のため、 1 位はもらえる最大ポイント、3 位はそれより少し下、5 位はさらに下…と自然な階段ができる。 しかも階段の段差は「その大会の参加レベル」によって変わる: 強豪が集まる大会のラウンド 5 と、層の薄い大会のラウンド 5 では、後者の方が難しさポイントが小さい。

4.4 「最強プレイヤーがその場にいなかった」として計算する

順位評価には、放っておくとどうしても出てしまう偏りがある。それは、各大会の 「最強プレイヤー」が他の参加者より高い点数を稼ぎやすいという傾向。

理由は単純で、ラウンドの難しさを「その場にいる参加者の平均的なレートを使って、その相手から 勝った時にもらえるポイント」で計算しているため。最強プレイヤー自身も「場の参加者」に含まれるので、 自分の高いレートが場の難しさを引き上げ、結果として自分が稼ぐポイントも持ち上げてしまう。 実際の大会で 1 位を取った人ほど、こうした自己強化の影響を受けやすい。

そこで SPSP では、各大会で「場の難しさ」を計算するとき、出場者の中で一番レートが高い人を その場にいなかったものとして扱う。場から最強プレイヤーを 1 人抜いた残りで平均を取り直すことで、 上の自己強化が起きなくなる。 実際の最終順位や、他の人がもらうポイントの仕組みは普通に計算するので、ラウンド構造そのものは変えない。

精度評価でこの調整を入れた結果、順位評価ランキング単体での予測精度 (= シード位置と実際の最終順位のずれ |SPR|) は 1.26 → 1.07 と大幅に改善。実際に優勝した人 (Top1) の予測ズレで見ても 4.92 → 2.92 と約 4 割小さくなった

別案: 「自分自身を場から外す」。 理屈のうえでは、各プレイヤーが点数を計算するときに「その人自身」を場から外す方法も考えられる。 精度を測ると最強プレイヤー外し案とほぼ同じ結果になったが、 この方式だと同じ大会で同じ順位を取っても、プレイヤーごとに違うポイントになってしまう (= 計算のたびに場から外す人が変わるため)。 SPSP は「同じ大会の同じ順位なら同じポイント」という分かりやすさを大事にしたいので、 シンプルに最強プレイヤー 1 人だけを外す方式を採用した。

5. 5 層カスケード (= 5 つのレベル)

順位評価は 1 回だけでなく 5 回 計算される。 Lv1 から Lv5 までの 5 段階で、それぞれ対象プレイヤーや大会フィルタを少しずつ厳しくしながら再計算する。 プレイヤーは到達した最高 Lv のスコアでランクづけされる。

5.1 なぜ複数レベルにするのか? — 解像度の問題

Elo 系のレート差から計算される「勝った時のレート増加量」は、 両者のレートが大きく離れすぎると 差がほぼ飽和して見えなくなる 性質がある (上位帯にとって格下相手の試合はほぼ 0 ポイント、格上相手の試合はほぼ満額に張り付く)。

§4.2 で説明したとおり、SPSP は 自分側のレートをレベルごとの固定値で評価する。 そのため固定値を一通りにしてしまうと、「中堅層から見たラウンドの難しさ」と 「上位層から見たラウンドの難しさ」のどちらかの解像度が必ず潰れる:

そこで Lv ごとに 母集団 (どこまでの上位を残すか) と固定値の組 を切り替えて再計算し、 各実力帯にとって解像度が最も高い設定を使う。 Lv1 は広い層をカバーする中庸設定、Lv2 以降は上位帯向けに絞ったうえで固定値を厳しく上げる。 これによって、ピラミッドのどの段にいるプレイヤーも「自分の帯から見たちょうど良いものさし」で評価される。

また、強い人ほど「規模の小さい大会」と「一定規模以上の大会」でのパフォーマンス差が大きく出る (= 一定規模以上の大会では本気度・対戦相手の質ともに上がりやすい)。 Lv3 以降は 参加者数や休日開催を条件にした大会フィルタ を追加することで、 上位帯では「本気度の高い大会」だけを使って順位評価を再計算する。 これによって、トップ層の評価が小規模・カジュアル大会の結果に揺さぶられにくくなる。

5.2 カスケードの流れ (ざっくり)

プレイヤーは Lv5 まで残ればその Lv5 スコアでランクづけされ、Lv4 止まりの人は Lv4 スコアで、… という形で「自分が到達した最高 Lv」のスコアを採用する。 Lv5 まで残った Top 256 のあいだでは、最も精度の高いカスケードの結果で順位が決まる仕組みだ。

各 Lv のパラメータ詳細 (上位何名にするか、大会フィルタ条件など) は数式編に表で載っている。 ここでは「段階的に上位を絞り込むことで、上位帯の解像度を上げている」という発想だけ押さえてほしい。

6. 集計の重みづけ (時間減衰 + 上位 3 大会)

プレイヤーが持つ最終スコアは、出場した 全大会のポイントの合計 である。 ただし、ただ足すのではなく次の 2 つの重みを掛けてから足す。

6.1 時間減衰: 古い大会ほど軽くなる

過去のレートをそのまま現在の評価に持ち込むと、何年も前のピークがいつまでも反映される。 SPSP では、毎月およそ 3 % 減衰するように設計している。 たとえば半年前の大会のポイントは約 83 %、1 年前は約 70 %、2 年前は約 48 % まで縮む。 最新の活動が最も重く、古い実績はだんだん影響を弱める。

6.2 上位 3 大会まではフル評価、それ以降は減衰

プレイヤーの大会ポイントを「貢献の大きい順」にソートし、

たとえばある大会のポイントが「100 ポイント」だったとすると、 その大会が上位 3 件に入っていれば 100 ポイントそのまま、 4 件目だと 30 ポイント、5 件目だと 9 ポイント、6 件目だと約 3 ポイント、と急激に小さくなる。

これによって 「同じ強さのプレイヤーなら、何十大会出てもポイントが青天井に伸び続けることはない」 設計になっている。 本当に効くのは上位 3 大会で、それ以降は「ちょっとずつ補強」程度の意味合い。 短期間に強いパフォーマンスを集中させた人と、薄いパフォーマンスを長期間続けた人で、 前者の方が評価されやすくなる。

「上位 3 件はフル評価」と「毎月 3% 減衰」は同時に効く。 つまり「最近 3 つの良いリザルト」が評価のコア、「それ以外」と「古い実績」はゆっくり溶けていく。

7. 総合評価 (順位評価 + 直対評価)

最後に、SPSP の公式順位である「総合評価」。 これは 順位評価のランクと直対評価のランクを単純平均 したもの。 スコアそのものを平均するのではなく、ランク (= 順位) を平均することで、 両方式のスコアのスケール差に依存しない仕組みになっている。

たとえば順位評価で 15 位、直対評価で 9 位のプレイヤーは、 総合の暫定値が (15 + 9) / 2 = 12 となる。 すべてのプレイヤーをこの暫定値でソートし直したものが、最終的な SPSP 順位。

平均順位が並んだ場合のタイブレークは、順位評価と直対評価の Elo 換算スコアの平均を降順で使う。 順位だけだと粒度が粗いので、スコアで細かい差を拾う設計。

8. ちょっとした具体例 (8 人の小さな大会)

架空の 8 人参加大会で、ラウンドポイントから最終スコアまでをステップバイステップで計算してみる。 §4.2 で説明した「場の平均 gain」モデルを実装通りに辿る。

8.1 仮定するレートとパラメータ

各シード位置のプレイヤーの直対評価レートを次のように仮定する:

シード12345678
レート r 25002300210019001700150013001100

計算に使うパラメータは Lv1 のものを採用する (Lv1 = 全プレイヤー対象、最も基本的な層):

ラウンド j の難しさポイント mj の計算式 (Rj = そのラウンドの場に居る参加者シード集合):

m_j = ε + average over i ∈ R_j of g( max(r_i, floor) )
    ただし   g(r) = 1 − 1 / (1 + 10^((r − base) / 400))

8.2 各ラウンドの「場」のシード集合

8 人ダブルイリミの敗者ブラケットを「強い半分が勝つ」前提でシミュレートすると、各ラウンドに残るシードは次の通り:

ラウンド場にいるシード人数
Round 1 (LB 初戦)5, 6, 7, 84
Round 2 (WB R2 敗者と合流)3, 4, 5, 64
Round 3 (LB 内部)3, 42
Round 4 (LB Final · WB 決勝敗者と合流)2, 32
Round 5 (Grand Final · WB 優勝者と合流)1, 22

各プレイヤーが「何ラウンドぶんを稼ぐか」は 最終順位 (placement_to_tier 経由) から決まる:

最終順位稼ぐラウンド数 (rw)稼ぐラウンド
1 位5R1 + R2 + R3 + R4 + R5
2 位4R1 + R2 + R3 + R4
3 位3R1 + R2 + R3
4 位2R1 + R2
5 位 / 6 位1R1
7 位 / 8 位0(なし)

8.3 ステップ 1: 各シードの gain 値を計算

まず、参加するシード 1〜8 全員について gain 値を計算しておく。 gain 関数 g(r) = 1 − 1 / (1 + 10(r − 1700) / 400) を各シードの相手レートに当てはめる:

シードレート r(r − 1700) / 40010(r−1700)/400g(r)
12500+2.00100.0000.9901
22300+1.5031.6230.9693
32100+1.0010.0000.9091
41900+0.503.1620.7597
517000.001.0000.5000
61500−0.500.3160.2403
71300−1.000.1000.0909
81100−1.500.0320.0307

8.4 ステップ 2: ラウンドポイント mj を計算

各ラウンドの場にいるシードの gain を平均し、ε = 0.01 を足す:

Round 1 — シード [5, 6, 7, 8]:

m₁ = 0.01 + (0.5000 + 0.2403 + 0.0909 + 0.0307) / 4
    = 0.01 + 0.8619 / 4
    = 0.01 + 0.2155 ≈ 0.2255

Round 2 — シード [3, 4, 5, 6]:

m₂ = 0.01 + (0.9091 + 0.7597 + 0.5000 + 0.2403) / 4
    = 0.01 + 0.6023 ≈ 0.6123

Round 3 — シード [3, 4]:

m₃ = 0.01 + (0.9091 + 0.7597) / 2 = 0.01 + 0.8344 ≈ 0.8444

Round 4 (LB Final) — シード [2, 3]:

m₄ = 0.01 + (0.9693 + 0.9091) / 2 = 0.01 + 0.9392 ≈ 0.9492

Round 5 (Grand Final) — シード [1, 2]:

m₅ = 0.01 + (0.9901 + 0.9693) / 2 = 0.01 + 0.9797 ≈ 0.9897

上位ラウンドほど場に強いシードが集まるので、mj はラウンドごとに単調に大きくなっていく。

8.5 ステップ 3: 最終順位ごとに合計

各プレイヤーは「自分の最終順位に応じたラウンド数 (rw)」ぶんの mj を Round 1 から順に足し合わせる:

最終順位稼ぐラウンド合計ポイント (raw)サイト表示用 Elo (= raw × 100)
1 位R1 + R2 + R3 + R4 + R50.2255 + 0.6123 + 0.8444 + 0.9492 + 0.9897 = 3.6211362.1
2 位R1 + R2 + R3 + R40.2255 + 0.6123 + 0.8444 + 0.9492 = 2.6314263.1
3 位R1 + R2 + R30.2255 + 0.6123 + 0.8444 = 1.6822168.2
4 位R1 + R20.2255 + 0.6123 = 0.837883.8
5 位 / 6 位R10.225522.5
7 位 / 8 位(なし)00.0

※ サイト上の「順位評価スコア」表示は raw 合計を 100 倍した値 (オフセットなし)。 raw のままだと値が小さすぎて差が見えにくいため、整数 1〜2 桁の桁感に揃えるためのスケーリング。 順位そのものは raw でも × 100 でも変わらない。

実際の SPSP では、ここで求めた大会あたりのスコアにさらに 時間減衰 0.97d/30位置減衰 (上位 3 件はフル、それ以降は 0.3 倍ずつ) をかけて、 プレイヤーの最終スコアに加算していく (§6 参照)。 多くの大会のスコアが積み上がることで、実際のプレイヤーの表示 Elo は 1000〜3000 規模になる。

もっと知りたい人へ