• PageRank 计算博客园用户排名


          PageRank 通过网页与网页之间的链接关系计算各网页权重,一般权重高的网页特点是:链接向它的网页数量多、链向它的网页其权重也较高。PageRank 就是通过这样的连接关系,一轮轮迭代计算后得出各网页的权重。

          思路拓展一下,其实人与人之间也是连接着的,在社会的人际关系网中,每个人的社会地位和身价也是不同的。以微博为例,我们都有关注者和粉丝(类似网页之间的链接),可以发现所谓的“大V”基本上粉丝数量多,并且粉丝里不乏很多其他“大V”,所以这个帐号的价值就大。

          同样博客园也具有类似的社交关系,用户可以选择“关注的人”以及“关注我的人”,理论上是可以用 PageRank 算法算出哪些用户更受大家欢迎,于是本文代大家八卦了一下,文章较长,只想看排名的同学请直接拉到末尾。。。

    PageRank 算法简介

    1. 数学模型

          《数学之美》第10章的延伸阅读部分,对 PageRank 的计算方法进行了简单介绍,但原书有些错误,修改后描述如下:

          我们设向量 B 为第一、第二…第N个网页的网页排名

          矩阵 A 代表网页之间的权重输出关系,其中 amn 代表第 m 个网页向第 n 个网页的输出权重。

          输出权重计算较为简单:假设 m 一共有10个出链,指向 n 的一共有2个,那么 m 向 n 输出的权重就为 2/10。

          现在问题变为:A 是已知的,我们要通过计算得到 B。

          假设 Bi 是第 i 次迭代的结果,那么

          初始假设所有网页的排名都是 1/N (N为网页总数量),即

          通过上述迭代计算,最终 Bi 会收敛,即 Bi 无限趋近于 B,此时 B = B × A。

    2. 具体示例

          假设有网页A、B、C、D,它们之间的链接关系如下图所示

          计算 B1 如下:

          不断迭代,计算结果如下:

    第 1次迭代: 0.125, 0.333, 0.083, 0.458
    第 2次迭代: 0.042, 0.500, 0.042, 0.417
    第 3次迭代: 0.021, 0.431, 0.014, 0.535
    第 4次迭代: 0.007, 0.542, 0.007, 0.444
    第 5次迭代: 0.003, 0.447, 0.002, 0.547
    第 6次迭代: 0.001, 0.549, 0.001, 0.449
    第 7次迭代: 0.001, 0.449, 0.000, 0.550
    第 8次迭代: 0.000, 0.550, 0.000, 0.450
    第 9次迭代: 0.000, 0.450, 0.000, 0.550
    第10次迭代: 0.000, 0.550, 0.000, 0.450
    ... ...

          我们可以发现,A 和 C 的权重变为0,而 B 和 D 的权重也趋于在 0.5 附近摆动。从图中也可以观察出:A 和 C 之间有互相链接,但它们又把权重输出给了 B 和 D,而 B 和 D之间互相链接,并不向 A 或 C 输出任何权重,所以久而久之权重就都转移到 B 和 D 了。

    PageRank 的改进

          上面是最简单正常的情况,考虑一下两种特殊情况:

       

          第一种情况是,B 存在导向自己的链接,迭代计算过程是:

    第 1次迭代: 0.125, 0.583, 0.083, 0.208
    第 2次迭代: 0.042, 0.833, 0.042, 0.083
    第 3次迭代: 0.021, 0.931, 0.014, 0.035
    第 4次迭代: 0.007, 0.972, 0.007, 0.014
    第 5次迭代: 0.003, 0.988, 0.002, 0.006
    第 6次迭代: 0.001, 0.995, 0.001, 0.002
    第 7次迭代: 0.001, 0.998, 0.000, 0.001
    第 8次迭代: 0.000, 0.999, 0.000, 0.000
    第 9次迭代: 0.000, 1.000, 0.000, 0.000
    第10次迭代: 0.000, 1.000, 0.000, 0.000
    ... ...

          我们发现最终 B 权重变为1,其它所有网页的权重都变为了0 =。=!

          第二种情况是 B 是孤立于其它网页的,既没有入链也没有出链,迭代计算过程是:

    第 1次迭代: 0.125, 0.000, 0.125, 0.250
    第 2次迭代: 0.063, 0.000, 0.063, 0.125
    第 3次迭代: 0.031, 0.000, 0.031, 0.063
    第 4次迭代: 0.016, 0.000, 0.016, 0.031
    第 5次迭代: 0.008, 0.000, 0.008, 0.016
    第 6次迭代: 0.004, 0.000, 0.004, 0.008
    第 7次迭代: 0.002, 0.000, 0.002, 0.004
    第 8次迭代: 0.001, 0.000, 0.001, 0.002
    第 9次迭代: 0.000, 0.000, 0.000, 0.001
    第10次迭代: 0.000, 0.000, 0.000, 0.000
    ... ...

          我们发现所有网页权重都变为了0 =。=!

          出现这种情况是因为上面的数学模型出现了问题,该模型认为上网者从一个网页浏览下一个网页都是通过页面的超链接。想象一下正常的上网情景,其实我们在看完一个网页后,可能直接在浏览器输入一个网址,而不通过上一个页面的超链接。

          我们假设每个网页被用户通过直接访问方式的概率是相等的,即 1/N,N 为网页总数,设矩阵 e 如下:

          设用户通过页面超链接浏览下一网页的概率为 α,则直接访问的方式浏览下一个网页的概率为 1 - α,改进上一节的迭代公式为:

          通常情况下设 α 为0.8,上一节”具体示例”的计算变为如下:

         迭代过程如下:

    第 1次迭代: 0.150, 0.317, 0.117, 0.417
    第 2次迭代: 0.097, 0.423, 0.090, 0.390
    第 3次迭代: 0.086, 0.388, 0.076, 0.450
    第 4次迭代: 0.080, 0.433, 0.073, 0.413
    第 5次迭代: 0.079, 0.402, 0.071, 0.447
    第 6次迭代: 0.079, 0.429, 0.071, 0.421
    第 7次迭代: 0.078, 0.408, 0.071, 0.443
    第 8次迭代: 0.078, 0.425, 0.071, 0.426
    第 9次迭代: 0.078, 0.412, 0.071, 0.439
    第10次迭代: 0.078, 0.422, 0.071, 0.428
    第11次迭代: 0.078, 0.414, 0.071, 0.437
    第12次迭代: 0.078, 0.421, 0.071, 0.430
    第13次迭代: 0.078, 0.415, 0.071, 0.436
    第14次迭代: 0.078, 0.419, 0.071, 0.431
    第15次迭代: 0.078, 0.416, 0.071, 0.435
    第16次迭代: 0.078, 0.419, 0.071, 0.432
    第17次迭代: 0.078, 0.416, 0.071, 0.434
    第18次迭代: 0.078, 0.418, 0.071, 0.432
    第19次迭代: 0.078, 0.417, 0.071, 0.434
    第20次迭代: 0.078, 0.418, 0.071, 0.433
    ... ...

    PageRank 算法实现

          互联网的网页数量是 Billion 级别的,所以不可能一下子初始化矩阵 A ,试想一下 10亿 × 10亿 的矩阵是什么概念!

          这时就用到稀疏矩阵了,定义稀疏矩阵的结构如下(其实是稀疏矩阵的一行):

    public class SparseMatrix<T>
    {
        public SparseMatrix(T head, double rank)
        {
            Head = head;
            LinkedItems = new List<T>();
            Rank = rank;
        }
    
        /// <summary>
        ///     稀疏矩阵头
        /// </summary>
        public T Head { get; private set; }
    
        public double Rank { get; set; }
    
        /// <summary>
        ///     稀疏矩阵链接的项目
        /// </summary>
        public List<T> LinkedItems { get; set; }
    
        public void AddLink(T linkedItem)
        {
            LinkedItems.Add(linkedItem);
        }
    }

          第一节中的链接示例图,就可以用如下代码表示:

    Dictionary<char, SparseMatrix<char>> matrix = new Dictionary<char, SparseMatrix<char>> 
    {
        {'A', new SparseMatrix<char>('A', 0.25){LinkedItems = new List<char>{'B', 'C', 'D'}}},
        {'B', new SparseMatrix<char>('B', 0.25){LinkedItems = new List<char>{'D'}}},
        {'C', new SparseMatrix<char>('C', 0.25){LinkedItems = new List<char>{'A', 'D'}}},
        {'D', new SparseMatrix<char>('D', 0.25){LinkedItems = new List<char>{'B'}}}
    };

          通过稀疏矩阵计算,与原先的整个矩阵行列式计算有较大不同,计算次序已经发生了变化。原先矩阵是一行乘以权重的竖列,稀疏矩阵则变为类似于按原先矩阵的竖列与权重相乘。矩阵运算中当然不允许 1 × N 矩阵与 1 × N 矩阵相乘的情况,我们能做的就是先将一项项算好,比如先算 A 这一行,算出 A 给 B、C、D输出多少权重,在一个类似字典类型的数据结构里,给 B、C和 D 各加上一笔,像是一个 Map-Reduce 过程。

    public class MapReduce<T>
    {
        private readonly Dictionary<T, double> _map;
    
        public MapReduce()
        {
            _map = new Dictionary<T, double>();
        }
    
        public double Reduce(T key, double value)
        {
            if (_map.ContainsKey(key))
            {
                _map[key] += value;
                return _map[key];
            }
            _map.Add(key, value);
            return value;
        }
    
        public double GetOrSetDefaultValue(T key)
        {
            if (_map.ContainsKey(key))
                return _map[key];
            _map.Add(key, 0.0);
            return 0.0;
        }
    }

          PageRank 设计如下:

    public class PageRank<T>
    {
        private MapReduce<T> _mapReduce;
        private readonly double _stayProbability;
        private readonly double _averageRank;
    
        /// <summary>
        /// 
        /// </summary>
        /// <param name="totalCount">项目总数</param>
        /// <param name="stayProbability">保持留在某个项目, 不跳转的概率</param>
        public PageRank(int totalCount, double stayProbability = 0.8)
        {
            _mapReduce = new MapReduce<T>();
            _stayProbability = stayProbability;
            _averageRank = 1.0 / totalCount;
        }
    
        /// <summary>
        ///     计算下一轮PageRank
        /// </summary>
        public void NextCircle()
        {
            _mapReduce = new MapReduce<T>();
        }
    
        /// <summary>
        ///     计算一条行列式
        /// </summary>
        /// <param name="sparseMatrix">稀疏矩阵</param>
        public void Calc(SparseMatrix<T> sparseMatrix)
        {
            var outputRank = 1.0 / sparseMatrix.LinkedItems.Count;
    
            foreach (var item in sparseMatrix.LinkedItems)
            {
                _mapReduce.Reduce(item,
                    _stayProbability * outputRank * sparseMatrix.Rank);
            }
            //当没有其它链接指向Head的时候, 以防漏项
            _mapReduce.Reduce(sparseMatrix.Head, (1 - _stayProbability) * _averageRank);
        }
    
        /// <summary>
        ///     一轮PageRank迭代之后, 获取最新的PageRank并更新
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public double GetCurrentRank(T key)
        {
            return _mapReduce.GetOrSetDefaultValue(key);
        }
    }

          调用示例:

    var matrix = new Dictionary<char, SparseMatrix<char>> 
    {
        {'A', new SparseMatrix<char>('A', 0.25){LinkedItems = new List<char>{'B', 'C', 'D'}}},
        {'B', new SparseMatrix<char>('B', 0.25){LinkedItems = new List<char>{'D'}}},
        {'C', new SparseMatrix<char>('C', 0.25){LinkedItems = new List<char>{'A', 'D'}}},
        {'D', new SparseMatrix<char>('D', 0.25){LinkedItems = new List<char>{'B'}}}
    };
    
    var pageRank = new PageRank<char>(matrix.Count);
    //计算30轮
    for (int i = 1; i <= 30; i++)
    {
        pageRank.NextCircle();
        foreach (var item in matrix)
        {
            pageRank.Calc(item.Value);
        }
        foreach (var item in matrix)
        {
            var cRank = pageRank.GetCurrentRank(item.Key);
            item.Value.Rank = cRank;
        }
        var str = string.Join(", ", matrix.Select(item => item.Value.Rank.ToString("N3")));
        Console.WriteLine(string.Format("第{0,2}次迭代: {1}", i, str));
    }

          开源地址:https://github.com/CreateChen/PageRank

    博客园用户权重计算

          写一个简单的网络爬虫,爬取博客园所有用户的 Id、关注的人等信息(过程略),最终得到如下结构的表格:

          首先八卦一下粉丝数量 Top 20,方便与 PageRank 算出的结果做对比。

          下面进入真正的 PageRank 计算了,利用第二节的程序计算,一次迭代计算速度很快,从数据库获取数据并且计算完毕只要6秒,更新数据库的 Rank 字段需要近30秒。下表展示的是第1轮迭代、第15轮的用户的权重:

    排名 NickName Rank NickName Rank
    1 梦想天空(山边小溪) 0.002165 梦想天空(山边小溪) 0.001346
    2 Fish Li 0.00192 dudu 0.001334
    3 汤姆大叔 0.001514 Artech 0.00102
    4 Jimmy Zhang 0.001281 Fish Li 0.000947
    5 M了个J 0.001244 司徒正美 0.000786
    6 Artech 0.001164 李永京 0.000746
    7 农民伯伯 0.001161 汤姆大叔 0.0007
    8 司徒正美 0.000987 趣味苹果开发 0.000677
    9 Vamei 0.000953 通用C#系统架 0.000601
    10 tornadomeet 0.000858 M了个J 0.00059
    11 伍华聪 0.000787 Jimmy Zhang 0.000586
    12 一线码农 0.000786 banban 0.000576
    13 吴秦 0.00075 Milo Yip 0.000532
    14 dudu 0.000729 张善友 0.000488
    15 虫师 0.000722 Jeffrey Zhao 0.00047
    16 小坦克 0.000691 觉先 0.000462
    17 Rollen Holt 0.000649 SoftwareTeacher 0.000444
    18 圣殿骑士 0.000545 博客园团队 0.000434
    19 CareySon 0.000538 TerryLee 0.000434
    20 文顶顶 0.000538 胡尐睿丶 0.00043
    21 小洋(燕洋天 0.000535 T2噬菌体 0.000422
    22 虾皮 0.000529 农民伯伯 0.000415
    23 JerryLead 0.000526 Anytao 0.000411
    24 李永京 0.000508 圣殿骑士 0.000411
    25 方倍工作室 0.000505 深蓝色右手 0.000406
    26 cloudgamer 0.000486 伍华聪 0.0004
    27 杨中科 0.000483 一线码农 0.000399
    28 TerryLee 0.000467 小洋(燕洋天 0.000388
    29 张善友 0.000466 Cat Chen 0.000374
    30 深蓝色右手 0.000457 CareySon 0.000371
    31 谦虚的天下 0.000445 Vamei 0.000365
    32 通用C#系统架 0.000435 ziqiu.zhang 0.000358
    33 胡尐睿丶 0.00042 周 金根 0.000351
    34 三生石上 0.000413 伍迷 0.000342
    35 KenshinCui 0.000397 xiaotie 0.000332
    36 博客园团队 0.000389 谦虚的天下 0.000331
    37 酸奶小妹 0.000387 冠军 0.000329
    38 Milo Yip 0.000372 吴秦 0.000327
    39 伍迷 0.00037 cloudgamer 0.000318
    40 子龙山人 0.000369 tornadomeet 0.000305
    41 Insus.NET 0.000369 酸奶小妹 0.0003
    42 菩提树下的杨 0.000369 小坦克 0.0003
    43 万一 0.000368 【当耐特】 0.000285
    44 _Luc_ 0.000367 chenkai 0.000277
    45 夏天的森林 0.000354 Justin 0.000274
    46 ziqiu.zhang 0.000351 装配脑袋 0.000272
    47 Jeffrey Zhao 0.000351 虫师 0.000271
    48 叶小钗 0.000343 菩提树下的杨 0.000271
    49 真 OO无双 0.000343 Gnie 0.00027
    50 SoftwareTeacher 0.000337 张逸 0.000252
    51 webabcd 0.000333 LeftNotEasy 0.000245
    52 T2噬菌体 0.000331 小静(Cathy 0.00024
    53 Ruthless 0.000328 Gray Zhang 0.000237
    54 peida 0.000327 Jesse Liu 0.000237
    55 陈梓瀚(vczh) 0.000324 JerryLead 0.000231
    56 聂微东 0.000322 webabcd 0.000228
    57 Jesse Liu 0.000317 fly in ocean 0.000225
    58 【当耐特】 0.000288 Anders Cui 0.000221
    59 西西吹雪 0.000286 代震军 0.000221
    60 snandy 0.000286 COM张 0.00022
    61 Phinecos(洞庭散人) 0.000286 楠小楠 0.00021
    62 David_Tang 0.000283 何戈洲 0.00021
    63 yangecnu 0.000282 三生石上 0.000207
    64 孤傲苍狼 0.000278 路过秋天 0.000204
    65 Learning hard 0.000277 Jake Lin 0.000203
    66 Stephen_Liu 0.000272 飞洋过海 0.000201
    67 Terry_龙 0.000269 陈希章 0.0002
    68 xiaotie 0.000269 叶小钗 0.000198
    69 Leo Chin 0.000269 eaglet 0.000197
    70 海 子 0.000267 陈梓瀚(vczh) 0.000197
    71 Barret Lee 0.000264 _Luc_ 0.000194
    72 路过秋天 0.000263 snandy 0.000191
    73 Dsp Tian 0.000255 新瓶老酒 0.000186
    74 Devin Zhang 0.000255 Bēniaǒ 0.000185
    75 苍梧 0.000252 dax.net 0.000185
    76 觉先 0.000251 麒麟.NET 0.000185
    77 周 金根 0.00025 方倍工作室 0.000183
    78 冠军 0.000249 万一 0.000181
    79 何戈洲 0.000242 陈硕 0.000179
    80 刘冬.NET 0.000237 任力 0.000177
    81 hoojo 0.000236 Franky 0.000177
    82 Orisun 0.000236 虾皮 0.000173
    83 CrazyBingo 0.000235 Stephen_Liu 0.000172
    84 代震军 0.000234 石破天惊 0.000172
    85 minglz 0.000231 岑安 0.000171
    86 Alexia(minmin) 0.000226 杨中科 0.00017
    87 Gnie 0.000226 iOS之旅 0.00017
    88 邹华栋 0.000225 横刀天笑 0.000169
    89 Samaritans 0.000224 邀月 0.000168
    90 Aaron艾伦 0.000222 jv9 0.000165
    91 邀月 0.000221 聂微东 0.000164
    92 Healtheon 0.000219 创想中国(羲闻) 0.000164
    93 李林峰的园子 0.000219 CoderZh 0.000163
    94 吕震宇 0.000218 Luminji 0.000163
    95 沈逸 0.000218 winter-cn 0.000163
    96 LeftNotEasy 0.000213 Rollen Holt 0.000161
    97 陈希章 0.000212 金色海洋(jyk)阳光男孩 0.000159
    98 Hongten 0.000211 重典 0.000159
    99 创想中国(羲闻) 0.000211 阿一(杨正祎) 0.000157
    100 Cat Chen 0.000208 夜里的烟 0.000157

          如果你对完整的计算过程感兴趣,可以下载完整表格。想了解自己的权重及排名请在下方留言。

          当然在实际计算排名过程中,不单单依据粉丝与关注者之间的关系,还需要结合文章的质量、更新频率及数量,最后给一个综合的评分。总之:被关注数量越多、经常写文章、文章被赞的次数越多、评论次数多,就越能提升社区影响力。

    本文链接:http://www.cnblogs.com/technology/p/PageRank.html

  • 相关阅读:
    QuickSort(Java)
    MergeSort(Java)
    Silverlight中Datagrid添加Button列用于控制单行对象
    二叉搜索树(BST)demo
    svn ignore使用方法
    海量数据处理面试题及解决方法
    Android中的单元测试
    Ubuntu下配置Intellij的Android开发环境
    urlrewrite地址重写之后丢失css和js解决方案
    修改后 简单的 TCP server
  • 原文地址:https://www.cnblogs.com/technology/p/PageRank.html
Copyright © 2020-2023  润新知