1. 程式人生 > >Elo評分演算法原理與實現

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