演算法學習-從賭錢遊戲看PageRank演算法

來源:互聯網
上載者:User
文章目錄
  • 由於PageRank演算法有非常高的知名度和普及度,我們接下來以PageRank演算法為例講述“並行計算+資料演算法”的經典搭配,並且這種“海量資料平行處理、迭代多輪後收斂”的分析過程也跟其他的資料採礦或者機器學習演算法應用類似,能起到很好的參考作用。

談到並行計算應用,會有人想到PageRank演算法,我們有成千上萬的網頁分析連結關係確定排名先後,藉助並行計算完成是一個很好的情境。長期以來,Google的創始發明PageRank演算法吸引了很多人學習研究,據說當年Google創始者興奮的找到Yahoo!公司,說他們找到一種更好的搜尋引擎演算法,但是被Yahoo!公司技術人員潑了冷水,說他們關心的不是更好的技術,而是搜尋的盈利。後來Google封裝成了“更先進技術的新一代搜尋引擎”的身份,逐漸取代了市場,並實現了盈利。

由於PageRank演算法有非常高的知名度和普及度,我們接下來以PageRank演算法為例講述“並行計算+資料演算法”的經典搭配,並且這種“海量資料平行處理、迭代多輪後收斂”的分析過程也跟其他的資料採礦或者機器學習演算法應用類似,能起到很好的參考作用。

下面是PageRank演算法的公式:

我們其實可以直接闡述該公式本身,並介紹如何使用並行計算套用上面公式得到各網頁的PageRank值,這樣雖然通過並行計算方式完成了PageRank計算,但是大家仍然不明白上面的PageRank公式是怎麼來的。

我們把這個PageRank演算法公式先放在一邊,看看一個賭錢的遊戲:
有甲、乙、丙三個人賭錢,他們的輸贏關係如下:

甲的錢輸給乙和丙
乙的錢輸給丙
丙的錢輸給甲

例如,甲、乙、丙各有本錢100元,按照以上輸贏關係,玩一把下來:

甲輸給乙50元、輸給丙50元
乙輸給丙100元
丙輸給甲100元

如果僅是玩一把的話很容易算出誰輸誰贏
但如果他們幾個維持這樣的輸贏關係,贏的錢又投進去繼續賭,這樣一輪一輪賭下去的話,最後會是什麼樣子呢?

我們可以寫個單機程式看看,為了方便計算,初始本錢都設為1塊錢,用x1,x2,x3代表甲、乙、丙:
double x1=1.0,x2=1.0,x3=1.0;
用x1_income,x2_income,x3_income代表每賭一把後各人贏的錢,根據輸贏關係:

double x2 income =x1/2.0;
double x3
 income =x1/2.0+x2;
double x1_ income =x3;

最後再把各人贏的錢覆蓋掉本錢,繼續往下算。完整程式如下:

// Gamble單機程式
public class Gamble
{
public static double x1=1.0,x2=1.0,x3=1.0;
public static void playgame(){

       double x2_income=x1/2.0;
       double x3_income=x1/2.0+x2;
       double x1_income=x3;
       x1=x1_income;
       x2=x2_income;
       x3=x3_income;
       System.out.println("x1:"+x1+", x2:"+x2+", x3:"+x3);
}               public static void main(String[] args){
       for(int i=0;i<500;i++){
       System.out.print("第"+i+"輪 ");
       playgame();
       }
}
}

我們運行500輪後,看到結果如下:

我們發現,從107輪後,各人的輸贏結果就一直是
x1:1.2000000000000002, x2:0.6000000000000001, x3:1.2000000000000002
…...

可能你都沒想到會有這麼個規律,這樣一直賭下去,雖然各人每輪有輸有贏,但是多輪後的輸贏結果居然保持平衡,維持不變了。用技術術語來說就是多輪迭代後產生了收斂,用俗話來講,就是玩下去甲和丙是不虧的,乙不服輸再繼續賭下去,也不會有扳本的機會的。

我們再把輸贏關係稍微改一下:丙的錢輸給甲和乙
double x2_income=x1/2.0+x3/2.0;
double x3_income=x1/2.0+x2;
double x1_income=x3/2.0;

運行10000輪後,發現又收斂了:
x1:0.6666666666666667, x2:1.0, x3:1.3333333333333333

不過這次就變成了“甲是輸的,乙保本,丙是贏的”,我們發現收斂的結果可用於排名,如果給他們做一個賭王排名的話,很顯然:“丙排第一,乙第二、甲第三”。

那麼這樣的收斂是在所有情況下都會發生嗎,什麼情況不會收斂呢?
我們回過頭觀察上面的輸贏關係,甲、乙、丙三人互相各有輸贏,導致錢沒有流走,所以他們三人才一直可以賭下去,如果把輸贏關係改一下,讓甲只輸錢,不贏錢,如下:
double x2_income=x1/2.0+x3/2.0;
double x3_income=x1/2.0+x2;
double x1_income=0;

那麼運行下來會是什麼結果呢?

我們發現很多輪後,全部為0了。我們分析一下過程,第一輪後,甲的錢就輸光了,沒有贏得一分錢。但是乙和丙各有輸贏,他們一直賭到2000多輪時,乙的錢全部輸光了,甲乙都沒錢投進來賭了,導致丙再也贏不到錢了,最後所有人結果都變為0了。

我們再分析一下輸贏關係,甲的錢全部輸給丙和乙後,丙跟乙賭,贏的多輸的少,於是所有的錢慢慢都被丙贏走了,導致最後無法維持一個平衡的輸贏結果。因此,如果我們要維持平衡和收斂,必須保證贏了錢的人不準走,必須又輸給別人才行,讓錢一直在三人圈裡轉不流失。換句話說,如果存在某人只輸不贏,那麼這個遊戲就玩不下去。

賭錢遊戲講完了,我們再看看PageRank演算法的公式:

上面的L(B)代表頁面B指向其他頁面的串連數,我們舉個例子:

假設有A、B、C三張網頁,他們的連結關係如下:

A包含B和C的連結
B包含C的連結
C包含A的連結

根據上面的公式,得到各網頁PR值如下:

PR(B)=PR(A)/2;
PR(B)=PR(A)/2+PR(C);
PR(A)=PR(C);

可以回過頭對照一下,把A、B、C改成甲、乙、丙就是上面舉的賭錢遊戲例子。

那麼q是幹嗎的?公式裡的q叫做逃脫因子,名字很抽象,目的就是用於解決上面賭錢遊戲中“只輸不贏”不收斂的問題,1-q會保證其中一個PR值為0時計算下來不會全部為0,那麼加了這麼一個(…)*q+1-q的關係後,整體的PR值會變化嗎?

當每個頁面的初始PR值為1時,0<=q<=1(計算時通常取值0.8),我們把所有頁面的PR值相加看看,假設有n張網頁:

PR(x1)+ PR(x2)+ …+PR(xn)
=( (PR(x2)/ L(x2)+ … )q+1-q) + … + ( (PR(x1)/ L(x1)+ … )q+1-q)
=(PR(x1) L(x1)/L(x1) + PR(x2) L(x2)/L(x2) + … + PR(xn) L(xn)/L(xn))q + n(1-q)
=( PR(x1) + PR(x2) + … + PR(xn))
q + n - nq
=n
q + n – n*q
= n

由於初始PR值為1,所以最後所有頁面的PR值相加結果還是為n,保持不變,但是加上(…)*q+1-q的關係後,就避免了PR值為0可以尋求收斂進行排序。

當然實際應用中,這個公式還可以設計的更複雜,並可以通過高等代數矩陣旋轉求解,我們這裡只是為了理解原理,並不是為了做搜尋演算法,所以就不再深入下去了。

總結:世界的很多東西都是零和遊戲,就像炒股,股民賺的錢也就是機構虧的錢,機構賺的錢也就是股民虧的錢,也許股民們應該研究一下PageRank演算法,看看股市起起落落的背後是不是收斂了,收斂了說明炒下去永遠別想解套,而且機構永遠不會虧。

如何使用並行計算方式求PR值:
我們這裡通過fourinone提供的各種並行計算模式去設計,思路方法可以有很多種。
第一次使用可以參考分散式運算上手demo指南,開發包:http://www.skycn.com/soft/68321.html

思路一:可以採取工人互相合并的機制(工人互相合并及receive使用可參見sayhello demo),每個工人分析當前網頁連結,對每個連結進行一次PR值投票,通過receive直接投票到該連結對於網頁所在的工人機器上,這樣經過一輪工人的互相投票,然後再統計一下本機器各網頁所得的投票數得到新的PR值。但是這種方式,對於每個連結投票,都要調用一次receive到其他工人機器,比較耗用頻寬,網頁數量龐大連結眾多時要調用很多次receive,導致效能不高。

思路二:由於求PR值的特點是輸入資料大,輸出資料小,也就是網頁成千上萬占空間多,但是算出來的PR值占空間小,我們姑且用記憶體可以裝下。因此我們優先考慮每個工人統計各自機器上的網頁,計算各連結對應網頁的所得投票,然後返回工頭統一合并得到各網頁的PR值。可以採用最基本的“總—分—總”並行計算模式實現(請參考分散式運算上手demo指南)。
並行計算的拆分和合并設計如下:

可以看到:

工人負責統計各自機器上網頁的各個連結的PR得票。
工頭負責合并累加得到各連結對應網頁的新PR值,并迭代計算。

程式實現:

PageRankWorker:是一個PageRank工人實現,為了方便示範,它通過一個字串數組代表包括的連結(實際上應該從本地網頁檔案裡擷取)
links = new String[]{"B","C"};
然後對連結集合中的每個連結進行PR投票
for(String p:links)
              outhouse.setObj(p, pr/links.length);

PageRankCtor:是一個PageRank包工頭實現,它將A、B、C三個網頁的PageRank初始值設定為1.00,然後通過doTaskBatch進行階段計算,doTaskBatch提供一個柵欄機制,等待每個工人計算完成才返回,工頭將各工人返回的連結投票結果合并累加:
pagepr = pagepr+(Double)prwh.getObj(page);
得到各網頁新的PR值(這裡取q值為1進行計算),然後連續迭代500輪計算。

運行步驟:

1、  啟動ParkServerDemo(它的IP連接埠已經在設定檔指定)

java -cp fourinone.jar; ParkServerDemo

2、運行A、B、C三個PageRankWorker,傳入不同的IP和連接埠號碼
java  -cp fourinone.jar; PageRankWorker localhost 2008 A

java  -cp fourinone.jar; PageRankWorker localhost 2009 B

java  -cp fourinone.jar; PageRankWorker localhost 2010 C

3、運行PageRankCtor
java -cp fourinone.jar; PageRankCtor

我們可以看到跟開始的單機程式的結果是一樣的,同時各工人視窗依次輸出了各自的PR值:

完整demo源碼如下:

// ParkServerDemoimport com.fourinone.BeanContext;
public class ParkServerDemo{
       public static void main(String[] args){
              BeanContext.startPark();
        }
}

 

// PageRankWorker import com.fourinone.MigrantWorker;
import com.fourinone.WareHouse;
import com.fourinone.Workman;

public class PageRankWorker extends MigrantWorker{
       public String page = null;
       public String[] links = null;
       public PageRankWorker(String page, String[] links){
              this.page = page;
              this.links = links;
       }

              public WareHouse doTask(WareHouse inhouse) {
              Double pr = (Double)inhouse.getObj(page);
              System.out.println(pr);
              WareHouse outhouse = new WareHouse();
              for(String p:links)
                  outhouse.setObj(p, pr/links.length);//對包括的連結PR投票

                  return outhouse;
       }
       public static void main(String[] args){
              String[] links = null;
              if(args[2].equals("A"))
               links = new String[]{"B","C"};//A頁麵包括的連結
              else if(args[2].equals("B"))
               links = new String[]{"C"};
              else if(args[2].equals("C"))
                  links = new String[]{"A"};                                
              PageRankWorker mw = new PageRankWorker(args[2],links);
              mw.waitWorking(args[0],Integer.parseInt(args[1]),"pagerankworker");
        }
}

// PageRankCtor
import com.fourinone.Contractor;
import com.fourinone.WareHouse;
import com.fourinone.WorkerLocal;
import java.util.Iterator;

public class PageRankCtor extends Contractor{
       public WareHouse giveTask(WareHouse inhouse){
              WorkerLocal[] wks = getWaitingWorkers("pagerankworker");
              System.out.println("wks.length:"+wks.length);                    
              for(int i=0;i<500;i++){//500輪
                  WareHouse[] hmarr = doTaskBatch(wks, inhouse);
                  WareHouse prwh = new WareHouse();
                      for(WareHouse result:hmarr){
                          for(Iterator iter=result.keySet().iterator();iter.hasNext();){
                              String page = (String)iter.next();
                              Double pagepr = (Double)result.getObj(page);
                              if(prwh.containsKey(page))
                                  pagepr = pagepr+(Double)prwh.getObj(page);
                                  prwh.setObj(page,pagepr);
                            }
                        }
                   inhouse = prwh;//迭代
                   System.out.println("No."+i+":"+inhouse);
                }
                return inhouse;
              }        
      public static void main(String[] args){
             PageRankCtor a = new PageRankCtor();
             WareHouse inhouse = new WareHouse();
             inhouse.setObj("A",1.00d);//A的pr初始值
             inhouse.setObj("B",1.00d);//B的pr初始值
             inhouse.setObj("C",1.00d);//C的pr初始值
             a.giveTask(inhouse);
             a.exit();        }
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.