#AtWaker Contestとは
東大生のたまり場、バーチャル東大DiscordというDiscordサーバー(通称VUTD)にはAtWakerというbotがいます。そのbotが毎朝7:30~12:00に開催している、早起きレーティングコンテストがAtWaker Contestです。
毎朝7:30にAtWakerが
「おはようございます! Good morning!
20yy-mm-ddのAtWaker Contest xxx開始です。
起きた人は下の「20yy-mm-dd」の
メッセージにでリアクションしてね。
徹夜勢の参加は禁止です。
一度寝てから出直してください。」
というメッセージを送信します。
その直後にAtWakerが「20yy-mm-dd」というメッセージを送信するので、
それに12:00までにでリアクションしてください。
時刻と各参加者の過去のパフォーマンスをもとに、
AtCoder Heuristic Contestの方法を少し改造した方法で
計算されたレーティングがコンテスト後に付与されます。
2回以上リアクションをしても、採用されるのは初回のみです。
2021/12/1112(2021/12/08にherokuの不具合があったせいでずれました)にAtWaker Contestは300回を迎えました。300回記念として(?)AtWaker Contestのレーティングの仕組みを解説します。
#概要
毎朝7:30~12:00にAtWakerのメッセージにリアクションを付けた人には、パフォーマンスと呼ばれる点数が付きます。参加した回のパフォーマンスを集計して、レーティングが算出されます。今回はパフォーマンスの計算方法、レーティングの集計方法を解説します。
#パフォーマンス
参加者が$n$人いるとします。早い順に参加者$1,\ldots,n$とします。参加者$i\ (i=1,\ldots,n)$がリアクションを押した時点から12:01:00までの秒数を$s_i$秒とします。参加者$i$のランク$l_i$を以下のように定めます。便宜上$l_0=0$とします。
$$l_i'=\frac{\max_{1\leq j \leq n} (s_j)-s_i}{16200}\times n\ (i=1,\ldots,n)\\
l_i=l_i'-\frac{3l_1'}{2}+\max\left(l_1',\frac{1}{16200}\right)\ (i=1,\ldots,n)$$
$\{l_i\}_{i=0,\ldots,n}$は単調増加で、すべての項が非負です。
次に各参加者の過去パフォーマンスの重み付き平均$a_i$を計算します。参加者$i$が前日までに$m_i$回参加しているとします。$m_i\geq 1$のとき、参加者$i$の過去パフォーマンスを時系列の逆順(新しい順)に$p_{i,1},\ldots,p_{i,m_i}$とし、
$$a_i=\frac{\sum_{j=1}^m0.9^jp_{i,j}}{\sum_{j=1}^m0.9^j}$$
とします。初参加者については、$a_i=1200$とします。
関数$f(x)$を
$$f(x)=\sum_{i=1}^n\frac{l_i-l_{i-1}}{1+6^{\frac{x-a_i}{400}}}$$
で定めます。$f(x)$は非負単調減少で、$\lim_{x\to\infty}f(x)=0$です。参加者$i$の暫定パフォーマンス$P_i'$は$f(P_i')\leq l_i$が成立する最小の整数です。最終的なパフォーマンス$P_i$は以下の式で求められます。$\lfloor x\rfloor$は$x$を超えない最大の整数を表します。
$$M'=\frac{1}{n}\sum_{i=1}^nP_i',\ D'=\sqrt{\frac{1}{n}\sum_{i=1}^n(P_i'-M')^2}\\
M=1200+300\ln(n),\ D=800\ln(6)\\
P_i''=\frac{D}{D'}(P_i'-M')+M\\
P_i=\left\{\begin{array}{ll}
\lfloor P_i''\rfloor&(P_i''>400)\\
\left\lfloor 400\times e^{\frac{P_i''-400}{400}}\right\rfloor&(P_i''\leq 400)
\end{array}\right.$$
#レーティング
定数$W,S$を次のように定めます。この定数については後で解説します。
$$W=0.827197336426137,\ S=724.47443003358$$
参加者$i$の今回のパフォーマンスを$p_{i,0}(=P_i)$、過去パフォーマンスを時系列の逆順(新しい順)に$p_{i,1},\ldots,p_{i,m_i}$とします。さらに、参加者が時系列の逆順(新しい順)に$d_{i,0},d_{i,1},\ldots,d_{i,m_i}$日前に参加していたとします。つまり、$0=d_{i,0}<d_{i,1}<\cdots<d_{i,m_i}$で、参加者は$d_{i,j}$日前のAtWaker Contestでパフォーマンス$p_{i,j}$を獲得し、$d_{i,1}$日前、…、$d_{i,m_i}$日前以外の日は参加していないとします。修正パフォーマンス$p_{i,j}'\ (i=1,\ldots,n,\ j=1,\ldots,m_i)$を$p_{i,j}'=0.99^{d_{i,j}}p_{i,j}$として定めます。これによって、不参加(あるいは開催中止)により1日当たりレーティングが約1%減少するようになっています。参加者$i$に対し、数列$\{q_{i,k}\}_{k=1,\ldots,100(m_i+1)}$を以下のように定めます。
$$q_{i,100j+l}=p_{i,j}-S\ln(l)\ (j=0,\ldots,m_i,\ l=1,\ldots,100)$$
$\{q_{i,k}\}_{k=1,\ldots,100(m_i+1)}$を降順にソートしたものを$\{q_{i,k}'\}_{k=1,\ldots,100(m_i+1)}$とします。暫定レーティング$R_i'$は$\{q_{i,k}'\}_{k=1,\ldots,100(m_i+1)}$の先頭100項の重み付き平均として計算されます。
$$R_i'=\frac{\sum_{k=1}^{100}W^kq_{i,k}}{\sum_{k=1}^{100}W^k}$$
最終的なレーティング$R_i$は以下のように計算されます。
$$R_i=\left\{\begin{array}{ll}
\lfloor R_i'+0.5\rfloor&(R_i'>400)\\
\left\lfloor 400\times e^{\frac{R_i'-400}{400}}+0.5\right\rfloor&(R_i'\leq 400)
\end{array}\right.$$
非参加者についても同様に計算します。(ただし、今回参加していないので、$p_{i,0},d_{i,0}$などは除いて計算します。)
###定数について
定数$W,S$は初参加者のパフォーマンスが$1600$以上のときにレーティングがパフォーマンスから$1000$を引いた値になり、なおかつ後述の$q_{i,1}',\ldots,q_{i,10}'$のレーティングへの寄与がちょうど全体の85%になるように調整したものです。具体的には、
$$\sum_{k=1}^{10}W^k=0.85\sum_{k=1}^{100}W^k\\
S=\frac{1000\sum_{k=1}^{100}W^k}{\sum_{k=1}^{100}W^k\ln(k)}$$
を満たすような$W,S$です。$W\neq 0,1$に注意して、一つ目の$W$の式を変形すると、
$$17W^{100}-20W^{10}+3=0$$
となります。$X=W^{10}$とおくと、
$$17X^{10}-20X+3=0$$
となります。これは10次方程式であり、代数的には解けないので、Halley's methodで近似値を求めます。
$$F(X)=17X^{10}-20X+3$$
とすると、
$$F'(X)=170X^9-20,\ F''(X)=1530X^8$$
となります。
$$G(X)=X-\frac{2F(X)F'(X)}{2(F'(X))^2-F(X)F''(X)}\
=\frac{26010X^{19}+24480X^{10}-5610X^9+120}{31790X^{18}+17000X^9-4590X^8+800}$$
初期値$X_0=0$として、$X_{i+1}=G(X_i)$を計算すると、
$$X_1=\frac{3}{20}=0.15\\
X_2=\frac{62914520938853327042075667}{419430125886693432194690620} \approx 0.15000000490153$$
となります。
$$f(X_2)\approx 4.10454\times 10^{-28}$$
なので、これで十分でしょう。
$$W\approx\left(\frac{62914520938853327042075667}{419430125886693432194690620}\right)^{\frac{1}{10}}\approx 0.827197336426137\\
S=\frac{1000\sum_{k=1}^{100}W^k}{\sum_{k=1}^{100}W^k\ln(k)}\approx 724.47443003358$$
となります。
#最後に
実装はこちら。ただし、チャンネルがVUTDのatwaker-駒場東大前チャンネルに固定されているため、適宜変更を加えないと他サーバーでは使えません。
みんなAtWaker Contestに参加してね!