avatar

大兜

右手寫程式,左手寫音樂

計算主題熱門度的演算法(上)

很多網站都會在文章主題上加上評分的功能(五星評分、喜歡不喜歡、加一減一),主要用於計算主題的熱門程度,並加以排序,評分越高表示這篇主題越熱門,能見度也越高。

評分實做很容易,但排序不簡單,在實做排序過程中會面臨不少問題:

  • 經驗不足的工程師會重造輪子,套用自己的演算法,不懂得使用統計公式帶來的好處
  • 必須防範那些會意圖使自己的文章排序在最上面的 spammer
  • 複雜度太高的演算法導致系統負載過高

這篇文章會試著介紹一些排序演算法,從簡單到複雜,並介紹其優缺點,也會分享一些知名網站的排序演算法。就讓我們從最簡單的開始吧:

評分決定熱門

假設我們的資料庫已經存著數筆資料,也許是文章或影音,而我們可以透過評分去排序其熱門度:

熱門度 = 總評分

SELECT * from posts ORDER BY vote ASC|DESC

這個演算法實做上最簡單,但也很糟糕。通常我們會希望新主題可以有一定的能見度,但如果套此演算法,在首頁可能會被到好幾年前的熱門文章所霸佔,新主題無法置頂,也因能見度不高,難以翻身。加上也許幾篇熱門主題會因為網站離峰時段導致評分較低,例如上個月的熱門主題可以得到一百分,這個月因為暑假大家出去玩,同樣熱門的主題只能拿十分,這樣的算法明顯不公平。

考慮時間

讓我們在公式上加一個時間變數,越年輕的主題會有較高的機會出現在首頁,很合理吧?

熱門度 = 總評分/主題壽命

主題壽命 = 現在時間 - 主題發布時間

對於越老得文章,變得熱門必須要有相對越多的評分。乍看之下沒問題,但這方法仍是不公平的。理由很簡單,試想一下一篇凌晨三點發布的主題,由於這時間是離峰時期,即使這主題真的很棒,卻因為大多人當時都在睡覺,導致相較於白天才發布的主題,這篇超炫主題已經輸在起跑點。

為了改善這個問題,我們再變換一下公式:

熱門度 = Σ(評分/評分壽命)

評分壽命 = 現在時間 - 評分時間

越年輕的評分有較大的權重,乍看確實解決了剛剛的離峰問題,但卻有新的麻煩:如果一個人對一個好幾年前的主題投下一票,這一票將會比這幾年來,在這主題上的任何評分要來得值錢,導致老舊的主題會再一次出現在首頁。所以我們還得再考慮主題壽命,於是公式變成:

熱門度 = [Σ(評分/評分壽命)]/主題壽命

對於上一個公式再除以主題壽命,以防主題回春,好極了,這下上述所有問題都獲得解決,但你是否發現這個演算法潛藏的危機?他的複雜度太高以致計算成本昂貴,難以在現實的系統中實現。

由於該演算法非常依賴時間,每一秒的熱門都會改面,設計上需要每幾秒刷新整個資料庫(或幾分鐘,單看系統設計需要的即時程度而定),這對高流量的網站來說簡直惡夢。

因為這方法用起來不太妙,我來介紹另一個簡單的替代方案:

Bayesian Average

假設我們的設計是喜歡某個主題,給他 +1,討厭就 -1,而一個主題評分則是所有的正評除以評分總數,所以公式變成:

主題分數 = 正評數/(正評數+負評數)

所以主題分數的值會介於 0 到 1 之間。

但這樣設計仍有個問題,假設有一個主題在經過幾百人的評分之後得到 0.96,另一個主題只有 2 個人評分,且都給予正評,於是這個主題躍然成了平分為 100% 的主題!這和我們想要的不一樣:

  1. 我們希望當一個主題只有少數人評分時,權重應比較低。
  2. 反之一個主題有大量的人去評過分,權重比較高。

熱門度的可信度應決定於評分人數的多寡,一個主題的評分人數越多,則計算出來的熱門度也比較趨近於真實情況,評分人數少,則計算的結果則可能與實際結果相差甚遠。

而求解這個問題也不是什麼新鮮的研究,因為公式在幾百年前就被發明了:Bayesian Average

利用 Bayesian Average,可以把公式改為:

熱門度 = ((C*所有主題平均評分)+該主題總分)/(C+該主題受評數)

C = 所有主題平均受評數
該主題總分 = 該主題受評數*主題分數

建議先看過 wiki 上的解釋,如果還是不太清楚可以參考這兩個網址:

這個方法比先前的好多了,前一個對時間的計算相當依賴,而此法卻與時間無關,至少效能好上許多。