搜索引擎---PageRank算法

一、引言

谷歌從1998年至今逐漸成爲一家最有創造力、影響力的公司,其中離不開其創始人提出的PageRank算法。

在給出PageRank算法之前,我們先來回顧一下搜索引擎的工作。

PageRank算法的魅力在於提出了“鏈接關係”用來處理網頁之間的關係,而不是早 期搜索引擎的僅利用關鍵字匹配和簡單的布爾運算來給出搜索結果。

PageRank通過網絡浩瀚的超鏈接關係來確定一個頁面的等級。Google把從A頁面到B頁面的鏈接解釋爲A頁面給B頁面投票,Google根據投票來源(甚至來源的來源,即鏈接到A頁面的頁面)和投票目標的等級來決定新的等級。簡單的說,一個高等級的頁面可以使其他低等級頁面的等級提升。----來自維基百科http://zh.wikipedia.org/wiki/PageRank

至於外鏈的關係何以這樣重要也是好理解的,就像就好比一篇論文被諾貝爾獎得主所引用, 顯然要比被普通研究者所引用更說明其價值。

先給大家一個直觀的列子,網頁B,C,D都有外鏈到網頁A,同時網頁B也有外鏈到C, 且網頁D有外鏈到B和C.


這樣我們粗糙的得到A的PageRank的值,PR(A) = PR(B)/2 + PR(C) + PR(D)/3.

二、數學推導

假設一個虛擬用戶在互聯網上的漫遊過程。 假定: 虛擬用戶一旦訪問了一個網頁後, 下一步將有相同的機率訪問被該網頁所鏈接的任何一個其它網頁。 換句話說, 如果網頁 Wi 有 Ni 個對外鏈接, 則虛擬用戶在訪問了 Wi 之後, 下一步點擊這些鏈接中任何一個的機率均爲 1/Ni。 初看起來, 這一假設並不合理, 因爲任何用戶都有偏好, 怎麼可能以相同的機率訪問一個網頁的所有鏈接呢? 但虛擬用戶實際上是對互聯網上全體用戶的一種平均意義上的代表, 這條假設就不象初看起來那麼不合理了。 那麼網頁的排序由什麼來決定呢? 是由該用戶在漫遊了很長時間 (理論上爲無窮長時間) 後訪問各網頁的機率分佈來決定, 訪問機率越大的網頁排序就越靠前。

pi(n) 表示虛擬用戶在進行第 n 次瀏覽時訪問網頁 Wi 的概率,則



其中,Nj表示頁面Wj的所有外鏈數,Pj->i表示若Wj有到Wi的外鏈則爲1,否則爲0.

爲符號簡潔起見, 我們將虛擬用戶第 n 次瀏覽時訪問各網頁的機率合併爲一個列向量 ,並引進一個只與互聯網結構有關的矩陣 H, 它的第 i 行 j 列的矩陣元爲Hij = pj->i / Nj,上面公式,即:


上面的數學表達是個典型的馬爾可夫過程(維基百科上鏈接),即他的條件概率僅同系統的當前狀態相關,而與他的歷史與未來無關,矩陣H代表的概率就是一個轉移概率。

上面的公式還得解決幾個問題:

  • 是否能在N足夠大時得到收斂?
  • 若能收斂,和給定的初始值是否有關?
在回答這兩個問題之前,我們來看幾種特殊情況:
比如之前前言的A網頁,它沒有外鏈,也就是說這些頁面就像“黑洞”會吞噬掉用戶繼續向下瀏覽的概率。在這種情況下,我們要假設當用戶到達沒有外鏈的網頁時,他能夠以等概率的訪問其他網頁。我們用k來標識這樣的網頁,若k=1表示該網頁沒有外鏈,這樣對矩陣H修正爲:(e是單位矩陣)

還有一種情況是,我們之前假設的是用戶會順着鏈接一路訪問下去,但實際的情況是用戶中途能夠離開等概率的去其他網頁,設不離開的概率是a,把S修正爲:


不難看出,G矩陣一定是正的,也就是個素矩陣,按照馬爾可夫鏈基本定理,馬爾可夫過程中, 如果轉移矩陣是素矩陣,極限 limn→∞pn 存在,與 p0 的選取無關。這樣前面的兩個問題也有了解決。

至於矩陣G裏面a的選取,google給出的值是0.85。因爲a過大不易收斂,過小難以反映“鏈接分析”的本質,這是個精度和效率的折中。

這樣,所有的問題就是求解下面矩陣的解,


在實際的算法裏,當P(N)和P(N+1)的差值在給定的閾值內,就認爲完成了收斂。

三、算法實現

在介紹算法之前,先介紹一個分析網頁信息較好的一個jar包,htmlparser.jar,目前最新版是1.6。

先新建7個.html文件,只需反映其鏈接關係即可,如

<html>
<body>
  <h1>  Hello world </h1>
    To Test2: <a href="test2.html">Test2</a>
    <br/>
    To Test3: <a href="test3.html">Test3</a>
</body>
</html>

關係用表格展示如下:


載入頁面的函數:其中HtmlBean是筆者寫的一個Html的javaBean,內容很簡單,有該網頁的路徑,外鏈,排名。

public static void loadHtml() throws IOException, ParserException
	{
		System.out.println("載入頁面...");
		File file = new File(HtmlPath);
		// 過濾文件,只尋找html文件
		File[] htmlsFiles = file.listFiles(new FileFilter() {
			
			@Override
			public boolean accept(File pathname) {
				if(pathname.getName().endsWith(".html"))
					return true;
				return false;
			}
		});
		init = new double[htmlsFiles.length];
		for(int i = 0;i<htmlsFiles.length;i++)
		{
			File f = htmlsFiles[i];
			@SuppressWarnings("resource")
			BufferedReader bReader = new BufferedReader(new InputStreamReader(
					new FileInputStream(f)));
			//讀入文件
			String line = bReader.readLine();
			StringBuffer html = new StringBuffer();
			while(line!=null)
			{
				html.append(line);
				line = bReader.readLine();
			}
			
			HtmlBean bean = new HtmlBean();
			bean.setPath(f.getName());
		//	System.out.println("filename: "+f.getName());
		//	System.out.println("absPath: "+f.getAbsolutePath());
			bean.setContent(html.toString());
			//HtmlPage Parser處理
			Parser parser = Parser.createParser(html.toString(), "utf-8");
			HtmlPage page = new HtmlPage(parser);
			parser.visitAllNodesWith(page);
			NodeList nodeList = page.getBody();
			//在NodeList中執行過濾器時,第二個參數爲True 
			nodeList = nodeList.extractAllNodesThatMatch(new TagNameFilter("A"),
					true);// 尋找<a>節點
			for(int j=0;j<nodeList.size();j++)
			{
				LinkTag outLinkTag = (LinkTag) nodeList.elementAt(j);
				//尋找外鏈的href的值
				bean.getOutLinks().add(outLinkTag.getAttribute("href"));
				
			}
			map.put(bean.getPath(), bean);
			list.add(bean);
			// 初始值
			init[i] = 0.0;
		}
	}
執行PageRank的方法:其中DAMP的阻尼係數,0.85,即上文提到的概率a.

private static double[] doPageRank()
	{
		double[] pr = new double[init.length];
		for(int i=0;i<init.length;i++)
		{
			double temp = 0.0;
			HtmlBean htmlBean = list.get(i);
			for(int j=0;j<init.length;j++)
			{
				HtmlBean htmlBean2 = list.get(j);
				// 計算對本頁面的鏈接
				if(i!=j&&htmlBean2.getOutLinks().size()!=0
						&&htmlBean2.getOutLinks().contains(htmlBean.getPath()))
				{
					temp = temp + init[j]/htmlBean2.getOutLinks().size();
				}
			}
			pr[i] = DAMP + (1-DAMP)* temp;
		}
		return pr;
	}

/**
 * 若前後兩次的值相差小於閾值,認爲完成收斂	
 * @return
 */
	private static boolean check()
	{
		boolean flag = true;
		for(int i=0;i<pr.length;i++)
		{
			if(Math.abs(pr[i]-init[i])>MAX)
			{
				flag = false;
				break;
			}
		}
		return flag;
	}
主函數的核心代碼:

loadHtml();
			pr = doPageRank();
			while(!check())
			{
				System.arraycopy(pr, 0, init, 0, init.length); 
				pr = doPageRank();
			}


排序過程:

// 比較器的實現	
			Collections.sort(list,new Comparator() {

				@Override
				public int compare(Object o1, Object o2) {
					HtmlBean h1 = (HtmlBean) o1;
					HtmlBean h2 = (HtmlBean) o2;
					int em = 0;
					if(h1.getPr()>h2.getPr())
					{
						em = -1;
					}
					else
					{
						em = 1;
					}
					return em;
				}
				
			});

結果:


發佈了26 篇原創文章 · 獲贊 1 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章