HITS算法--從原理到實現

本文介紹HITS算法的相關內容。html

1.算法來源
2.算法原理
3.算法證實
4.算法實現
4.1 基於迭代法的簡單實現
4.2 MapReduce實現
5.HITS算法的缺點
6.寫在最後
參考資料node


1. 算法來源

1999年,Jon Kleinberg 提出了HITS算法。做爲幾乎是與PageRank同一時期被提出的算法,HITS一樣以更精確的搜索爲目的,併到今天仍然是一個優秀的算法。python

HITS算法的全稱是Hyperlink-Induced Topic Search。在HITS算法中,每一個頁面被賦予兩個屬性:hub屬性和authority屬性。同時,網頁被分爲兩種:hub頁面和authority頁面。hub,中心的意思,因此hub頁面指那些包含了不少指向authority頁面的連接的網頁,好比國內的一些門戶網站;authority頁面則指那些包含有實質性內容的網頁。HITS算法的目的是:當用戶查詢時,返回給用戶高質量的authority頁面。算法


2. 算法原理

不少算法都是創建在一些假設之上的,HITS算法也不例外。HITS算法基於下面兩個假設[^ref_1]:spring

  • 一個高質量的authority頁面會被不少高質量的hub頁面所指向。
  • 一個高質量的hub頁面會指向不少高質量的authority頁面。

什麼叫「高質量」,這由每一個頁面的hub值和authority值肯定。其肯定方法爲:網絡

  • 頁面hub值等於全部它指向的頁面的authority值之和。
  • 頁面authority值等於全部指向它的頁面的hub值之和。

爲了讓你們快速理解HITS算法,先舉一個簡單的例子[^ref_2]。app

hits1

圖中共有3個網頁,它們構成了一個有向圖。咱們設每一個網頁的初始hub值和authority值都爲1。記\(h(p)\)爲頁面\(p\)的hub值,\(a(p)\)爲頁面\(p\)的authority值。則有\(h(1)=h(2)=h(3)=1\)\(a(1)=a(2)=a(3)=1\)框架

HITS算法的計算過程也是一個迭代的過程。在第一次迭代中,有:分佈式

\[ a(1) = 0, a(2) = 0, a(3) = h(1) + h(2) = 2 (沒有頁面指向網頁1和網頁2)\\ h(1) = a(3) = 2, h(2) = a(3) = 2, h(3) = 0 (網頁3沒有指向任何頁面) \]ide

這裏就已經能夠看出網頁3是一個相對好的authority頁面,而網頁1和網頁2是相對好的hub頁面。其實到這裏迭代也能夠結束了,由於再迭代下去無非是\(a(3)\)\(h(1)\)\(h(2)\)的值不斷增大,而哪一個是hub頁面,哪一個是authority頁面並不會改變。

上面的簡單例子只是爲了幫助理解,省略掉了不少步驟和細節。下面將詳細地介紹HITS算法[^ref_3]:。

與PageRank算法不一樣,HITS算法是在用戶搜索後運行的,因此HITS算法的處理對象集合確定得小不少。

首先,咱們須要肯定這個集合。整個互聯網中的網頁之間的關係能夠抽象爲一個有向圖\(G = (V,E)\),當有一個搜索請求產生時(不妨設關鍵字爲\(\sigma\)),咱們能夠取全部包含關鍵字\(\sigma\)的網頁組成的集合\(Q_\sigma\)爲初始集合,並在這個集合上運行咱們的HITS算法。然而,這個集合卻有着明顯的缺陷:這個集合可能很是之大,大到包含了數百萬個網頁,而這顯然不是理想的集合大小。因而,咱們進而想找到一個更小的集合\(S_\sigma\),知足如下條件:

  1. \(S_\sigma\)確實足夠小。
  2. \(S_\sigma\)包含不少與查詢相關的頁面。
  3. \(S_\sigma\)包含不少高質量的authority頁面。

如何找到這個\(S_\sigma\)集合?咱們假設用戶輸入關鍵字搜索,搜索引擎使用一個基於文本的引擎進行搜索。而後咱們取排名(按照相關度排名)最靠前的t(t通常取200左右)個網頁做爲初始集合,記爲根集合\(R_\sigma\)。這個集合知足咱們上面提到的前兩個條件,可是還遠遠不能知足第三個條件。

因而,咱們須要擴展\(R_\sigma\)。通常認爲,一個與關鍵字相關的高質量的網頁即便不在\(R_\sigma\)中,那也極可能在\(R_\sigma\)中有某些網頁指向它。基於此,咱們擴展\(R_\sigma\)的過程以下(摘自Jon Kleinberg 的論文):

Subgraph(\(\sigma\), \(\psi\), t, d)
  \(\sigma\): a query string.
  \(\psi\): a text-based search engine.
  t, d: natural numbers.

  Let \(R_\sigma\) denote the top t results of \(\psi\) on \(\sigma\).
  Set \(S_\sigma\) := \(R_\sigma\)

  For each page p \(\in\) \(R_\sigma\)
    Let \(\Gamma^+(p)\) denote the set of all pages p points to.
    Let \(\Gamma^-(p)\) denote the set of all pages pointing to p.
    Add all pages in \(\Gamma^+(p)\) to \(S_\sigma\).
    If \(|\Gamma^-(p)| \leq d\), then
      Add all pages in \(\Gamma^-(p)\) to \(S_\sigma\).
    Else
      Add an arbitrary set of d pages from \(\Gamma^-(p)\) to \(S_\sigma\).
  End
  Return \(S_\sigma\)

一開始咱們令\(S_\sigma\) = \(R_\sigma\)。而後經過上面的方法,咱們將全部被\(R_\sigma\)中網頁所指向的網頁加入到\(S_\sigma\)中,再把必定數量的指向\(R_\sigma\)集合中網頁的那些網頁(每一個\(R_\sigma\)中網頁最多能添加d個指向它的網頁)加入到\(S_\sigma\)中。爲了保證\(S_\sigma\)集合的合適的大小,d不能太大,通常設置爲50左右。一般狀況下,擴展以後集合的大小爲1000~5000個網頁,知足上面的三個條件。

在計算hub值和authority值以前,咱們還須要對\(S_\sigma\)進行一下處理。咱們把同一個「域名」(域名指一個網站)下的網頁之間的連接所有刪除,由於一般這些連接只是爲了讓人能在這個網站下的不一樣網頁之間進行切換,例如網站內的導航連接。在HITS算法中,這些連接與不一樣網站之間的連接相比,確定是後者更能體現hub值和authority值的傳遞關係。因此咱們在\(S_\sigma\)集合中刪除這些連接,造成新集合\(G_\sigma\)

如今,就能夠開始計算hub值和authority值了[^ref_4]。咱們用\(h(p)\)表示頁面\(p\)的hub值,\(a(p)\)表示頁面\(p\)的authority值。首先令每一個頁面的初始hub值\(h(p)\)爲1,初始authority值\(a(p)\)也爲1。而後就開始迭代計算的過程(n爲\(G_\sigma\)中總的網頁數):

\[ \forall p, a(p) = \sum_{i = 1}^n h(i), \\ \forall p, h(p) = \sum_{i = 1}^n a(i) \]

每一輪迭代結束,都須要進行標準化,使\(\sum_{i = 1}^n h(i)^2 = \sum_{i = 1}^n a(i)^2 = 1\)。標準化的必要性將在算法證實部分解釋。

何時迭代結束呢?咱們能夠設置一個迭代次數上限k來控制,或者設定一個閾值,當變化小於閾值的時候迭代結束。而後只要返回給用戶authority值靠前的十幾個網頁就好了。

好了,HITS算法的原理其實就這麼點,十分通俗易懂。


3. 算法證實

上面說到如何控制迭代的終止,而這又有個前提條件,那就是通過不斷的迭代,每一個網頁的hub值和authority值最終會收斂。下面咱們就來證實HITS算法的收斂性。

爲了證實的方便,咱們用矩陣的方式來表示HITS算法。

對於初始集合\(G_\sigma\),用一個矩陣\(M\)表示\(G_\sigma\)中網頁之間的關係:\(m_{ij} = 1\)表示網頁\(i\)指向網頁\(j\),不然爲0。用向量\(H\)表示全部頁面的hub值,其中第i個份量表示網頁i的hub值;用向量\(A\)表示全部頁面的authority值,其中第i個份量表示網頁i的authority值。全部頁面的hub值和authority值初始都爲1。例如上面算法原理的例子中的圖就能夠表示爲:

\[ M = \left( \begin{array}{ccc} 0 & 0 & 1 \\ 0 & 0 & 1 \\ 0 & 0 & 0 \\ \end{array} \right) \]

而後能夠計算:

\[ A = M^T H = \left( \begin{array}{ccc} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 1 & 1 & 0 \\ \end{array} \right) \left( \begin{array}{ccc} 1 \\ 1 \\ 1 \\ \end{array} \right) = \left( \begin{array}{ccc} 0 \\ 0 \\ 2 \\ \end{array} \right), \\ H = M A = \left( \begin{array}{ccc} 0 & 0 & 1 \\ 0 & 0 & 1 \\ 0 & 0 & 0 \\ \end{array} \right) \left( \begin{array}{ccc} 0 \\ 0 \\ 2 \\ \end{array} \right) = \left( \begin{array}{ccc} 2 \\ 2 \\ 0 \\ \end{array} \right) \]

通常的,咱們有:

\[ A_k = M^T H_{k - 1}, \\ H_k = M A_k \\ (每一輪先計算A_k,而後根據A_k計算H_k) \]

更進一步,有:

\[ A_k = (M^T M)^{k - 1} M^T Z, \\ H_k = (M M^T)^k Z \\ (其中Z的全部份量都爲1) \]

在這裏咱們引用一些線性代數的知識:

定理1
: 一個矩陣與該矩陣的轉置的乘積是對稱矩陣。

定理2
: 實對稱矩陣的特徵值都是實數,且若矩陣大小爲n * n,則其必有n個實特徵值(包含重根)。

定理3
: 含有n個特徵值的n階矩陣,其主對角線元素之和等於其特徵值之和。

定義1
: 對於實數矩陣,絕對值最大的特徵值稱爲主特徵值,對應的特徵向量稱爲主特徵向量。

定理4
: 若是一個實對稱矩陣是非負矩陣,則其主特徵向量也是非負的,而且是非0向量。

定理5
: **令\(W\)爲一個n*n實對稱矩陣,\(v\)是一個n維向量且與\(W\)的主特徵向量\(\omega_w\)非正交,則一個n維單位向量將沿着\(W^k v\)的方向收斂至\(\omega_w\)。**

由定理1可知上面的\(M^T M\)\(M M^T\)都是對稱矩陣,且由定理2可知都有n個實特徵值。

\(H_k = (M M^T)^k Z\)中,\(Z\)\(M^T M\)的主特徵向量非正交,因此\(H\)向量最終將收斂至\(M^T M\)的主特徵向量。定理5中的單位向量指的就是\(H\)向量,爲了保證其每輪迭代時都是一個單位向量,咱們在每次迭代以後都對其進行標準化

\(A_k = (M^T M)^{k - 1} M^T Z\)中,\(M^T Z\)\(M^T M\)的主特徵向量非正交。證實以下(這部分證實在 Jon Kleinberg 的原論文中省略了,自行證實,僅供參考):

\[ 假設 M^T Z與M^T M的主特徵向量\omega_w正交,則有: \\ (M^T Z)^T \omega_w = 0 \\ \Rightarrow (Z^T M) \omega_w = 0 \\ \Rightarrow Z^T (M \omega_w) = 0 \\ \because M是非負矩陣,\omega_w也是非負矩陣 \\ \therefore M \omega_w是非負矩陣 \]

咱們只要再證\(M \omega_w\)不是\(0\)矩陣,就能夠推翻咱們的假設了。由於\(Z^T\)的全部份量都爲1,因此\(Z^T\)與非負且非0矩陣的內積必定不爲0。在進一步證實以前,咱們先證\(M^T M 的主特徵值 \lambda_w \neq 0\)

\[ 令m_{ij}爲矩陣M第i行第j列的元素 \\ 即M = \left( \begin{array}{ccc} m_{11} & \dots & m_{1n} \\ \vdots & m_{ij} & \vdots \\ m_{n1} & \dots & m_{nn} \\ \end{array} \right), M^T = \left( \begin{array}{ccc} m_{11} & \dots & m_{n1} \\ \vdots & m_{ji} & \vdots \\ m_{1n} & \dots & m_{nn} \\ \end{array} \right) \\ 則M^T M 的主對角線元素w_{ii} = \sum_{j = 1}^n m_{ji}^2 \\ \because M不是0矩陣 \\ \therefore w_{ii}不全爲0 \\ \therefore M^T M 主對角線元素之和必不爲0 \\ 又 \because 由定理2和定理3可知對稱矩陣的主對角線元素之和爲特徵值之和 \\ \therefore M^T M 的特徵值之和必不爲0 \\ \therefore M^T M 的主特徵值 \lambda_w \neq 0 \]

下面證實\(M \omega_w\)不是\(0\)矩陣:

\[ \because \omega_w是M^T M的主特徵向量 \\ \therefore 有 M^T M \omega_w = \lambda_w \omega_w,其中 \lambda_w 爲主特徵值 \\ 假設M \omega_w 是0矩陣,則: \\ M^T M \omega_w = 0 \Rightarrow \lambda_w \omega_w = 0 \\ 但這顯然不成立 \\ \because \lambda_w \neq 0 且 \omega_w是非負矩陣 \\ \therefore \lambda_w \omega_w \neq 0 \\ \therefore 假設不成立 \\ \therefore M \omega_w 不是0矩陣 \]

再結合上面的結論:\(M \omega_w是非負矩陣\),便可得出:\(M \omega_w\)是非負矩陣且不是\(0\)矩陣。

因此上面的假設:\(假設 M^T Z與M^T M的主特徵向量\omega_w 正交\) 不成立。因此\(M^T Z\)\(M^T M\)的主特徵向量非正交。也即\(A\)向量最終將收斂至\(M^T M\)的主特徵向量。一樣的,爲了保證其每輪迭代時都是一個單位向量,咱們在每次迭代以後都對其進行標準化

至此,咱們便證實了HITS算法的收斂性。


4. 算法實現

下面的代碼原理與個人另外一篇博客PageRank類似。

4.1 基於迭代法的簡單實現

用python實現,須要先安裝python-graph-core。

class HITSIterator:
    __doc__ = '''計算一張圖中的hub,authority值'''

    def __init__(self, dg):
        self.max_iterations = 100  # 最大迭代次數
        self.min_delta = 0.0001  # 肯定迭代是否結束的參數
        self.graph = dg

        self.hub = {}
        self.authority = {}
        for node in self.graph.nodes():
            self.hub[node] = 1
            self.authority[node] = 1

    def hits(self):
        """
        計算每一個頁面的hub,authority值
        :return:
        """
        if not self.graph:
            return

        flag = False
        for i in range(self.max_iterations):
            change = 0.0  # 記錄每輪的變化值
            norm = 0  # 標準化係數
            tmp = {}
            # 計算每一個頁面的authority值
            tmp = self.authority.copy()
            for node in self.graph.nodes():
                self.authority[node] = 0
                for incident_page in self.graph.incidents(node):  # 遍歷全部「入射」的頁面
                    self.authority[node] += self.hub[incident_page]
                norm += pow(self.authority[node], 2)
            # 標準化
            norm = sqrt(norm)
            for node in self.graph.nodes():
                self.authority[node] /= norm
                change += abs(tmp[node] - self.authority[node])

            # 計算每一個頁面的hub值
            norm = 0
            tmp = self.hub.copy()
            for node in self.graph.nodes():
                self.hub[node] = 0
                for neighbor_page in self.graph.neighbors(node):  # 遍歷全部「出射」的頁面
                    self.hub[node] += self.authority[neighbor_page]
                norm += pow(self.hub[node], 2)
            # 標準化
            norm = sqrt(norm)
            for node in self.graph.nodes():
                self.hub[node] /= norm
                change += abs(tmp[node] - self.hub[node])

            print("This is NO.%s iteration" % (i + 1))
            print("authority", self.authority)
            print("hub", self.hub)

            if change < self.min_delta:
                flag = True
                break
        if flag:
            print("finished in %s iterations!" % (i + 1))
        else:
            print("finished out of 100 iterations!")

        print("The best authority page: ", max(self.authority.items(), key=lambda x: x[1]))
        print("The best hub page: ", max(self.hub.items(), key=lambda x: x[1]))


if __name__ == '__main__':
    dg = digraph()

    dg.add_nodes(["A", "B", "C", "D", "E"])

    dg.add_edge(("A", "C"))
    dg.add_edge(("A", "D"))
    dg.add_edge(("B", "D"))
    dg.add_edge(("C", "E"))
    dg.add_edge(("D", "E"))
    dg.add_edge(("B", "E"))
    dg.add_edge(("E", "A"))

    hits = HITSIterator(dg)
    hits.hits()

程序中給出的網頁之間的關係以下:

hits2

運行結果以下:

This is NO.9 iteration
authority {'E': 0.7886751345855355, 'C': 0.2113248654398108, 'B': 0.0, 'A': 7.119870133749228e-06, 'D': 0.5773502691457247}
hub {'E': 3.6855159786102477e-06, 'C': 0.40824829046663563, 'B': 0.7071067811721405, 'A': 0.40824829046663563, 'D': 0.40824829046663563}
finished in 9 iterations!
The best authority page:  ('E', 0.7886751345855355)
The best hub page:  ('B', 0.7071067811721405)

4.2 MapReduce實現

MapReduce是一個高效的分佈式計算框架,在這裏就很少作介紹了(若還不怎麼了解MapReduce能夠參考我另外一篇博客PageRank,裏面有簡單的原理介紹和代碼展現)。

下面是實現HITS算法的類,其中註釋較爲詳細,就很少作解釋了:

class HITSMapReduce:
    __doc__ = '''計算一張圖中的hub,authority值'''

    def __init__(self, dg):
        self.max_iterations = 100  # 最大迭代次數
        self.min_delta = 0.0001  # 肯定迭代是否結束的參數

        # graph表示整個網絡圖。是字典類型。
        # graph[i][authority][0] 存放第i網頁的authority值
        # graph[i][authority][1] 存放第i網頁的入鏈網頁,是一個列表
        # graph[i][hub][0] 存放第i網頁的hub值
        # graph[i][hub][1] 存放第i網頁的出鏈網頁,是一個列表
        self.graph = {}
        for node in dg.nodes():
            self.graph[node] = {"authority": [1, dg.incidents(node)], "hub": [1, dg.neighbors(node)]}

    @staticmethod
    def normalize(ah_list):
        """
        標準化
        :param ah_list: 一個列表,其元素爲(網頁名,數值)
        :return: 返回一個標準化的列表,其元素爲(網頁名,標準化的數值)
        """
        norm = 0
        for ah in ah_list:
            norm += pow(ah[1], 2)

        norm = sqrt(norm)
        return [(ah[0], ah[1] / norm) for ah in ah_list]

    def hits_authority_mapper(self, input_key, input_value):
        """
        用於計算每一個頁面能得到的hub值,這個hub值將傳遞給頁面的authority值
        :param input_key: 網頁名,如 A
        :param input_value: self.graph[input_key],即這個網頁的相關信息,包含兩個字典,{a...}和{h...}
        :return: [(網頁名, 0.0), (出鏈網頁1, A的hub值), (出鏈網頁2, A的hub值)...]
        """
        return [(input_key, 0.0)] + \
               [(out_link, input_value["hub"][0]) for out_link in input_value["hub"][1]]

    def hits_hub_mapper(self, input_key, input_value):
        """
        用於計算每一個頁面能得到的authority值,這個authority值將傳遞給頁面的hub值
        :param input_key: 網頁名,如 A
        :param input_value: self.graph[input_key],即這個網頁的相關信息,包含兩個字典,{a...}和{h...}
        :return: [(網頁名, 0.0), (入鏈網頁1, A的authority值), (入鏈網頁2, A的authority值)...]
        """
        return [(input_key, 0.0)] + \
               [(in_link, input_value["authority"][0]) for in_link in input_value["authority"][1]]

    def hits_reducer(self, intermediate_key, intermediate_value_list):
        """
        統計每一個網頁得到的authority或hub值
        :param intermediate_key: 網頁名,如 A
        :param intermediate_value_list: A全部得到的authority值或hub值的列表:[0.0,得到的值,得到的值...]
        :return: (網頁名,計算所得的authority值或hub值)
        """
        return intermediate_key, sum(intermediate_value_list)

    def hits(self):
        """
        計算authority值與hub值,各須要調用一次mapreduce模塊
        :return: self.graph,其中的 authority值與hub值 已經計算好
        """
        iteration = 1  # 迭代次數
        change = 1  # 記錄每輪迭代後的PR值變化狀況,初始值爲1保證至少有一次迭代
        while change > self.min_delta:
            print("Iteration: " + str(iteration))

            # 計算每一個頁面的authority值並標準化
            # new_authority爲一個列表,元素爲:(網頁名,此輪迭代所得的authority值)
            new_authority = HITSMapReduce.normalize(
                MapReduce.map_reduce(self.graph, self.hits_authority_mapper, self.hits_reducer))

            # 計算每一個頁面的hub值並標準化
            # new_hub爲一個列表,元素爲:(網頁名,此輪迭代所得的hub值)
            new_hub = HITSMapReduce.normalize(
                MapReduce.map_reduce(self.graph, self.hits_hub_mapper, self.hits_reducer))

            # 計算此輪 authority值+hub值 的變化狀況
            change = sum(
                [abs(new_authority[i][1] - self.graph[new_authority[i][0]]["authority"][0]) for i in range(len(self.graph))])
            change += sum(
                [abs(new_hub[i][1] - self.graph[new_hub[i][0]]["hub"][0]) for i in range(len(self.graph))])
            print("Change: " + str(change))

            # 更新authority值與hub值
            for i in range(len(self.graph)):
                self.graph[new_authority[i][0]]["authority"][0] = new_authority[i][1]
                self.graph[new_hub[i][0]]["hub"][0] = new_hub[i][1]

            iteration += 1
        return self.graph

下面是一個測試用例:

if __name__ == '__main__':
    dg = digraph()

    dg.add_nodes(["A", "B", "C", "D", "E"])

    dg.add_edge(("A", "C"))
    dg.add_edge(("A", "D"))
    dg.add_edge(("B", "D"))
    dg.add_edge(("C", "E"))
    dg.add_edge(("D", "E"))
    dg.add_edge(("B", "E"))
    dg.add_edge(("E", "A"))

    h = HITSMapReduce(dg)
    hits_result = h.hits()

    print("The final iteration result is")
    for key, value in hits_result.items():
        print(key + " authority: ", value["authority"][0], " hub: ", value["hub"][0])

    max_authority_page = max(hits_result.items(), key=lambda x: x[1]["authority"][0])
    max_hub_page = max(hits_result.items(), key=lambda x: x[1]["hub"][0])
    print("The best authority page: ", (max_authority_page[0], max_authority_page[1]["authority"][0]))
    print("The best hub page: ", (max_hub_page[0], max_hub_page[1]["hub"][0]))

運行結果爲:

The final iteration result is
E authority:  0.7886751345948128  hub:  8.64738646858812e-10
A authority:  7.060561487452559e-10  hub:  0.408267180858587
C authority:  0.2113248654051872  hub:  0.40823884510260666
B authority:  0.0  hub:  0.7071067809972986
D authority:  0.5773502691896258  hub:  0.40823884510260666
The best authority page:  ('E', 0.7886751345948128)
The best hub page:  ('B', 0.7071067809972986)

以上即是HITS算法的MapReduce實現。


5. HITS算法的缺點

  • 計算效率低

這裏說的「效率低」是針對其實時計算的特色而提出的。HITS算法是在用戶提出搜索請求以後纔開始運行的,然而計算出結果又須要屢次迭代計算,因此就這點上來講HITS算法效率仍然較低。

  • 主題漂移

在算法原理部分咱們介紹了HITS算法是如何生成初始集合\(G_\sigma\)。從根集合\(R_\sigma\)咱們經過連接添加網頁的方法進行擴展,但這也極可能添加進與搜索主題無關的網頁。如果這部分網頁中又偏偏有着一些高質量的authority頁面,則頗有可能返回給用戶,下降用戶的搜索體驗。

  • 做弊網頁

試想咱們弄一個頁面指向不少高質量的authority頁面,那麼這個頁面就成爲了一個高質量的hub頁面。而後再弄個連接指向本身的搓網頁,按照HITS算法,將大大提高本身的搓網頁的authority值。

  • 穩定性差

對於一個網頁集合,如果刪除其中的某條連接,就有可能形成一些網頁的hub值和authority值發生巨大變化。


6. 寫在最後

之後想到什麼再寫上來吧。


參考資料

1:《這就是搜索引擎:核心技術詳解》,張俊林

2:The Mathematics of Web Search,這個網站上有HITS和PageRank的一些數學知識。

3:原論文:Authoritative Sources in a Hyperlinked Environment

4:「標準參考文獻」:維基百科

相關文章
相關標籤/搜索