Elo評分演算法原理與實現
《社交網路》裡的Mark Zackburg被女朋友甩後,在舍友的啟發下,充分發揮了技術宅男自娛自樂的惡搞天分,做出了Facemash網站,對學校女生的相貌進行排名打分,結果網站訪問流量過大,直接把學校網路搞癱瘓了。Facemask大受歡迎的關鍵就在於Zackburg基友Eduardo寫在窗戶上的排名公式,看電影之時就對這個排名公式非常感興趣,上網瞭解下,才發現這條公式就是大名鼎鼎的ELO等級分制度。ELO的應用非常廣泛,大部分棋類比賽,現在流行的MODB遊戲,像11平臺的DOTA天梯系統,都是採用ELO等級分。
ELO等級分制度是由匈牙利裔美國物理學家Elo建立的一個衡量各類對弈活動選手水平的評分方法,是當今對弈水平評估的公認的權威方法。被廣泛應用於國際象棋、圍棋、足球等運動,以及很多網遊與電子競技產業。遊戲界比較著名的應用有: WOW(魔獸世界)、DOTA、LOL。
ELO計算方法
Ra:A玩家當前的積分
Rb:B玩家當前的積分
Sa:實際勝負值,勝=1,平=0.5,負=0
Ea:預期A選手的勝負值,Ea=1/(1+10^[(Rb-Ra)/400])
Eb:預期B選手的勝負值,Eb=1/(1+10^[(Ra-Rb)/400])
因為E值也為預估,則Ea+ Eb=1
R’a=Ra+K(Sa-Ea)
R’a:A玩家進行了一場比賽之後的積分
其中 K 值是一個常量係數,按照國際象棋裡的標準, K 值對於大師選手為16,對於一般選手是32。K值的大小直接關係到一局遊戲結束,根據勝負關係計算出的積分變化值。
關於K值
K值是一個極限值,代表理論上最多可以贏一個玩家的得分和失分,K/2就是相同rating的玩家其中一方勝利後所得的分數。國際象棋大師賽中,K=16;在大部分的遊戲規則中,K=32。通常水平越高的比賽中其K值越小,這樣做是為了避免少數的幾場比賽就能改變高階頂尖玩家的排名。
關於分母400
公式Ea和Eb中分母的400是怎麼來的呢?為何是400,不是200、100或者是其他?
根據公式可以得出,當K值相同的情況下,越高的分母,越低的積分變化。總體來說400是一個平衡的、萬金油的值、讓多數玩家積分保持 標準正態分佈 的值。具體可以參考:http://en.chessbase.com/post/arpad-elo-and-the-elo-rating-system
例項說明
若當前A玩家積分為1500,B玩家積分為1600
預估A玩家的勝負值: Ea = 1/(1+10^[(1600-1500)/400])≈0.36
預估B玩家的勝負值: Eb = 1-Ea = 1-0.36 = 0.64
假設A玩家獲勝,實際勝負值為Sa = 1
A玩家最終得分為 :R’a = 1500 + 32*(1-0.36) = 1500+20.5 = 1520
A玩家贏20分,B玩家輸20分。
假設B玩家獲勝,實際勝負值為Sa = 1
B隊最終得分為 R’b = 1600 + 32*(1-0.64) = 1600 + 11.52 = 1612,B玩家贏12分,A玩家輸12分。
class Elorating: ELO_RESULT_WIN = 1 ELO_RESULT_LOSS = -1 ELO_RESULT_TIE = 0 ELO_RATING_DEFAULT = 1500 ratingA = 0 ratingB = 0 def __init__(self, ratingA = ELO_RATING_DEFAULT, ratingB = ELO_RATING_DEFAULT): self.ratingA = ratingA self.ratingB = ratingB def setResult(self, result): scoreAwin = self.computeScore(self.ratingA, self.ratingB) scoreBwin = self.computeScore(self.ratingB, self.ratingA) score_adjust = 0 if result == self.ELO_RESULT_WIN: score_adjust = 1 elif result == self.ELO_RESULT_LOSS: score_adjust = 0 else: score_adjust = 0.5 self.ratingA = self.ratingA + self.computeK(self.ratingA) * (score_adjust - scoreAwin) self.ratingB = self.ratingB + self.computeK(self.ratingB) * (score_adjust - scoreBwin) def computeK(self, rating): if rating >= 2400: return 16 elif rating >= 2100: return 24 else: return 36 def computeScore(self, rating1, rating2): return 1 / (1+pow(10, (rating1 - rating2) / 400)) pass