レーティングランキングを作る

人の強さを1つの指標で表したい。そういった欲求はドラゴンボールの戦闘能力や、筋肉マンの超人強度にも見て取れる。


ただ、数値が何倍だから何倍強い、といった数値を決めるのは難しい。実際に使われている指標はチェスや将棋のレートのようなものだ。これは数値の差を見て、例えば、レートが200上なら勝率75%だ、というような数である。何倍強いとは言えないけど、差が同じなら、実力差も同じだけあることを表せるので便利な指標だ。
この数値は
イロレーティング(Wikipedia)
という方法で決められているらしい。


このレートが決まる原理は、wikipediaによれば次のような感じだ。勝敗が決まったとき、勝った方が負けた方のレートをもらえる。レートが上の人が勝った場合はもらえるレートが少なく、レートが下の人が勝った場合はもらえるレートが多い。このルールにしたがって何百回と対戦すれば、各人のレートがある値に落ち着いてくる、というわけだ。


勝率はレートに関してロジスティック関数にしたがう。だからレートの差が勝率を決めるというわけだ。簡便法では差を一次関数で表しているので、ややずれが生じる。計算量はそれほど問題にならないので、詳細な方を書いてみる。

(defun rate (ra rb)
  "レートAがレートBに勝つ確率(200差で25%-75%)"
  (if (< (- rb ra) 4000) ; オーバーフロー対策の上限値
      (/ (+ 1.0 (exp (/ (- rb ra) 182))))
    0.0))

(defun rate-delta (a b win-a)
  (if win-a (round (* 16 (rate b a)))
    (rate-delta b a t)))

; (game a b) など、試合結果を求める関数を用いて
; (rate-delta a b (game a b)) で変動量が算出されるので、
; その数値を足したり引いたりすれば良い。

何も難しいことはなかった。これで、一定値以上レートが高い人は勝っても変動量が0になるのも分かる。
レートが低い人が勝っても最大16しか変化しないので、それが面白くないという場合は情報量による修正を加えて、0〜100の値を取るようにしても良い。ただし、どちらにしても、あまり差が大きい場合は安定するまでに必要な対戦数が膨大になるだろう。

(defun rate-delta-2 (a b win-a) ; 小数は(log 0)対策の下限値
  (if win-a (ceiling (* 11 (- (log (max 0.00012 (rate a b))))))
    (rate-delta b a t)))