[关闭]
@SovietPower 2021-09-14T21:12:04.000000Z 字数 10488 阅读 1329

问题驱动的算法设计 作业:KMP证明;PageRank综述报告

ECNU


谷歌学术

谷歌学术:https://scholar.google.com.hk/
sci-hub:https://sci-hub.shop/
两者综合:https://gfsoso.99lb.net/

作业二选一,6月13前交:
1. 对一个具体问题设计一个算法
2. 对某类具体算法写一个综述报告


KMP算法证明

描述性的证明,非数学证明。

KMP的代码实现

  1. void GetFail(char *s)//pattern
  2. {
  3. int m=strlen(s+1);
  4. for(int i=2,j=0; i<=m; ++i)
  5. {
  6. while(j && s[i]!=s[j+1]) j=fail[j];
  7. fail[i]=s[i]==s[j+1]?++j:0;
  8. }
  9. }
  10. void Match(char *p,char *s)
  11. {
  12. int j=0,n=strlen(s+1),m=strlen(p+1);
  13. for(int i=1; i<=n; ++i)
  14. {
  15. while(j && s[i]!=p[j+1]) j=fail[j];
  16. if(s[i]==p[j+1]) ++j;
  17. if(j==m) printf("%d\n",i-m+1);
  18. }
  19. }

KMP正确性证明

首先说明一些定义:为文本串,为模式串,表示构成的子串。
字符串定义为:
定义中最长子串的长度,即最长的前缀等于后缀的子串长度,为:

GetFail的证明

GetFail函数用来求

总上,该函数正确。

Match的证明

Match函数利用,判断中出现的位置。
为当前已匹配的的前缀长度。

综上,该函数正确。

KMP复杂度证明

由上证明可知,两函数的计算过程本质是一样的。所以只对GetFail函数进行证明。

记势函数
对于算法的两种操作:
1. ,使增加
2. ,该操作会使减少)。

因为操作1有个,所以势函数的总变化量为,所以操作2的总次数也为,均摊复杂度

所以算法总复杂度为

PS

KMP算法不只能用于字符串字符的匹配,还能用于数列中数的相对大小关系的匹配,即将相等关系,替换为,串中的排名 等于 串中的排名。如这道题
这个想法感觉很妙,不知道在实际生活中,是否有这种相对大小关系的匹配需求,以及KMP的此类应用?


PageRank算法及优化 综述报告

看完这些论文的感觉:Google提出了PageRank,之后十几年 一人改一点,就能一人写一篇论文(雾)。
你一改 我一改 大家明天都能毕业
但WPR(VOL)和EWPR(VOL)真的毫无创新性,上一个算法提出后隔年就发出来了,感觉好水。

说是综述报告,其实就是为了作业随便写的,随便看看就好。


前言

关于PageRank的研究成果有很多,但与无权图相比带权图的算法仍然较少。
因为个人目前水平和时间问题,只对PageRank及Weighted PageRank优化方向的几个简单算法进行介绍。

Simplified PageRank

L. Page, S. Brin等1于1998年提出简化的PageRank算法:

其中:表示某个网页,分别为网页的排序值(网页的排名得分,即rank score),为连向的网页集合,的出度,factor used for normalization(标准化参数?)。

所有网页的排序值可以通过迭代若干次计算。
但是,当一个网络中存在两个点及以上的环,且该环没有出边时,这个环会积累其它网页的排序值而不会传递出去,使得所有连向该环的网页的排序值变为,不合实际。这个问题叫做rank sink

PageRank

为解决rank sinkL. Page, S. Brin等1根据随机游走将简化的PageRank改为:

其中:dampening factor(不跳出概率?),通常设为

相比Simplified PageRank,该算法考虑了用户在浏览网页时有概率跳出当前浏览的网页,而任意再选择其它网页的情况,从而避免了rank sink
作者测试了该算法,大概在次迭代后排序值会收敛。

Weighted PageRank (WPR)

PageRank算法的每次迭代,将每个网页的排序值平均分给了它指向的网页。但在实际中,同一网页指向的不同链接的重要性可能不同,分到的排序值也应不同。
Wenpu Xing, Ali Ghorbani2在2004年发表Weighted PageRank Algorithm,提出了Weighted PageRank(WPR)算法:

表示:考虑的入度和连向的所有点的入度,这条边该分配的多少权值:

其中:表示的入度,表示连向点的集合。

表示:考虑的出度和连向的所有点的出度,这条边该分配的多少权值:

其中:表示的出度,表示连向点的集合。

作者认为,一个网页越受欢迎,连向它的网页和它连出的网页越多。所以WPR根据指向网页集合中点的出度和入度,分配的排序值。这样排序值会更多地分配到更受欢迎的网页上,而不是均分。
作者实验表明:将WPR用于搜索,得到的结果的相关性高于传统PageRank算法。

PageRank based on Visits of Links (VOL)

Gyanendra Kumar等3在2011年发表Page Ranking Based on Number of Visits of Web Pages,提出了Page Ranking based on Visits of Links(VOL)算法:

其中表示用户通过链接访问的次数,表示,即用户通过上的链接访问其它网页的总次数。

该算法考虑了用户的浏览倾向,根据用户的实际浏览行为(链接的访问数)决定哪个网页更受欢迎,将更多的排序值分配到用户浏览最多(更受欢迎)的网页上。
VOL与之前算法相比具有如下特点:
1. 具有动态性,更符合网页搜索的特性。
2. 结果不是仅与网络结构相关,还与用户行为有关,更具目标导向性;而之前算法中,网页的排序值与用户实际访问该网页的情况无关。
3. 用户可以在搜索的同时提高该算法的准确性。
4. 一大问题是,需要动态统计每个网页上用户的浏览行为。

综上,VOL比传统PageRank的搜索结果更相关,但也带来了具体实现的问题。

Weighted PageRank based on Visits of Links (WPR(VOL))

Neelam Tyagi, Simple Sharma4在2012年发表Weighted Page Rank Algorithm Based on Number of Visits of Links of Web Page,提出了Weighted PageRank based on Visits of Links(WPR(VOL))算法:

其中:是WPR(VOL)算法计算出的的重要性是WPR中,考虑的入度和连向的所有点的入度,这条边该分配的多少权值:

因为VOL算法中考虑了出度影响的受欢迎程度(即),作者没有使用
该算法将WPR(网页的受欢迎程度)与VOL(用户的实际浏览行为/链接的访问数)结合,按照另一种比例分配排序值。
WPR(VOL)的特点、问题与VOL基本相同,但搜索结果相关性更强。

但是作者在论文所给例子的计算过程中,搞混了,结果都是错的,这也能发论文吗。(下面EWPR(VOL)中这里没有错)
该算法实际只是毫无技术水平的两算法结合。

Enhanced Weighted PageRank based on Visits of Links (EWPR(VOL))

Sonal Tuteja5在2013年发表Enhancement in Weighted PageRank Algorithm Using VOL,对WPR(VOL)算法进行了优化:

改进的原因是,作者认为WPR(VOL)算法中没有考虑(事实上考虑过这点),需要加上,于是就乘了个然后发了篇新论文。
但是,论文中表述有问题,公式中的是指WPR(VOL)还是EWPR(VOL)中的网页排序值?

如果是前者,那代入后完整公式应为:

为什么就要算两次?作者没有说明为什么不同。

如果是后者,那EWPR(VOL)与WPR算法不一样吗?

下面有作者给出的例子,例子与介绍VOL算法中的例子相同,包含visit of links。但是,给出的计算过程中没有出现形如的对visits的考虑。所以上面问题答案是后者,那例子中的EWPR(VOL)就是WPR(迷惑起来了)。
然后作者给出了WPR与EWPR(VOL)在取不同值时的不同结果,这个不同结果是如何得出来的?所以个人认为是作者的例子计算过程写错了,公式也有问题。
公式应为:

该算法实际又是毫无技术水平的两算法结合+无意义的改进。
可能是我水平低吧,我认为这篇论文/这算法和CtrlCV没区别,还有问题,这期刊随便发吗。

局部近似算法(基于操作的APXPR算法)

彭茂、张媛6在2016年发表《带权网络的个性化PageRank计算_彭茂》,提出了基于操作的PageRank计算方法,其时间复杂度优于传统迭代算法。

为PageRank中各点排序值构成的向量,为计算后的向量,为状态转移矩阵,为跳出概率。则可知为如下方程的唯一解:

传统迭代算法可表示为:

算法的误差限通常定义为:,时间复杂度为

作者给出了带权图的一个近似PageRank计算方法,复杂度上界为,其中为图顶点的最大出度。算法如下。

为非负向量,且对任意顶点,则称向量为PageRank向量近似。
该算法持续更新一个向量对,使其始终满足:


算法初始时通过如下操作更新(记操作后所得向量):

对某个点
1.
2.
3. (对所有满足
未更新的
为转移矩阵,则为预设值,默认为

可知
因为过程可以看做,中的某些概率转移到了中,所以对所有满足的点执行操作,可得到如下计算近似PageRank向量方法:

  1. ApxPR(s, α, ε)
  2. Let p=0 and r=s
  3. while r(u)>ε*outdgree(u) for some vertex u
  4. Pick any vertex u where r(u)>ε*outdgree(u)
  5. Apply Push(u)
  6. return p and r

作者证明了该APXPR算法时间复杂度为,其中为图顶点的最大出度,远优于迭代算法的

该算法也支持动态带权网络修改,更新网络一条边的复杂度为,且实际计算时间会小很多。而传统幂迭代法只能重新计算。动态网络的计算这里不再展开。

此外,作者介绍了蒙特卡洛策略计算PageRank向量的方法,也支持在线更新网络。对于一个个顶点、条边的图,保持条边持续更新的复杂度为

作者通过实验得出:
1. 静态图中算法与蒙特卡洛算法均优于幂迭代法;图的稠密度较高时蒙特卡洛算法优于算法。
2. 动态图中,在图的更新量较少时,算法优于蒙特卡洛方法。

代码实现

对WPR(VOL)算法与ApxPR算法的简单实现(节点数较小时)。
在节点数、边数不大时,算法实现很简单,但当节点数很大,即现实中网页数非常多时,可能需要改进实现方法。

每次迭代复杂度为

  1. /*
  2. Sample Input:
  3. 3 4
  4. 1 3 2
  5. 3 1 2
  6. 1 2 1
  7. 2 3 2
  8. Output:
  9. WPR[1]=0.6319057
  10. WPR[2]=0.2096800
  11. WPR[3]=0.5669479
  12. */
  13. #include <bits/stdc++.h>
  14. #define pc putchar
  15. #define gc() getchar()
  16. #define pb emplace_back
  17. typedef long long LL;
  18. const int N=1e3+5;
  19. const double d=0.85;
  20. inline int read()
  21. {
  22. int now=0,f=1; char c=gc();
  23. for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
  24. for(;isdigit(c);now=now*10+c-48,c=gc());
  25. return now*f;
  26. }
  27. struct Edge
  28. {
  29. int v,w;
  30. };
  31. struct Graph
  32. {
  33. std::vector<int> e[N];
  34. };
  35. struct PageRank
  36. {
  37. Graph R,B;
  38. int n,L[N][N],TL[N],I[N],O[N];
  39. double WPR[N],W_in[N][N],W_out[N][N];
  40. void AddEdge(int w,int v,int u)
  41. {
  42. R.e[u].pb(v), B.e[v].pb(u);
  43. ++I[v], ++O[u], L[u][v]+=w, TL[u]+=w;
  44. }
  45. void Output()
  46. {
  47. for(int i=1; i<=n; ++i) printf("WPR[%d]=%.7f\n",i,WPR[i]);
  48. }
  49. void WPRVOL()
  50. {
  51. for(int u=1; u<=n; ++u)
  52. {
  53. double sum=0;
  54. for(auto v:B.e[u]) sum+=L[v][u]*WPR[v]*W_in[v][u]/TL[v];
  55. WPR[u]=1-d+d*sum;
  56. }
  57. }
  58. void Calc()
  59. {
  60. //Init WPR
  61. for(int i=1; i<=n; ++i) WPR[i]=1;
  62. //Calc W_in W_out
  63. for(int u=1; u<=n; ++u)
  64. {
  65. if(!R.e[u].size()) continue;
  66. int sumI=0,sumO=0;
  67. for(auto v:R/*B*/.e[u]) sumI+=I[v], sumO+=O[v];//这里WPR(VOL)论文中使用的B(u)
  68. for(auto v:R.e[u]) W_in[u][v]=1.0*I[v]/sumI, W_out[u][v]=1.0*O[v]/sumO;
  69. }
  70. //Calc values iteratively
  71. for(int T=300,t=1; t<=T; ++t)//应该有一个校验误差是否满足的函数,不写了
  72. {
  73. WPRVOL();
  74. // printf("\nIteration %d:\n",t), Output();
  75. }
  76. Output();
  77. }
  78. void Init()
  79. {
  80. n=read();
  81. for(int m=read(); m--; AddEdge(read(),read(),read()));
  82. Calc();
  83. }
  84. }PR;
  85. int main()
  86. {
  87. PR.Init();
  88. return 0;
  89. }

局部近似算法

  1. /*
  2. Sample Input:
  3. 3 4
  4. 1 3 2
  5. 3 1 2
  6. 1 2 1
  7. 2 3 2
  8. Output:
  9. PR[1]=1.2303706
  10. PR[2]=0.4986050
  11. PR[3]=1.2710243
  12. */
  13. #include <bits/stdc++.h>
  14. #define pc putchar
  15. #define gc() getchar()
  16. #define pb emplace_back
  17. typedef long long LL;
  18. const int N=1e3+5;
  19. const double d=0.85;
  20. const double alpha=1-d, beta=0.5, epsilon=1e-8;
  21. inline int read()
  22. {
  23. int now=0,f=1; char c=gc();
  24. for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
  25. for(;isdigit(c);now=now*10+c-48,c=gc());
  26. return now*f;
  27. }
  28. struct Edge
  29. {
  30. int v,w;
  31. };
  32. struct Graph
  33. {
  34. std::vector<int> e[N];
  35. };
  36. struct PageRank
  37. {
  38. Graph R,B;
  39. int n,I[N],O[N],A[N][N];
  40. double p[N],r[N],w[N][N];
  41. void AddEdge(int w,int v,int u)
  42. {
  43. R.e[u].pb(v), B.e[v].pb(u);
  44. ++I[v], ++O[u], A[u][v]+=w;
  45. }
  46. void Output()
  47. {
  48. for(int i=1; i<=n; ++i) printf("PR[%d]=%.7f\n",i,p[i]);
  49. }
  50. int Exist(double e)
  51. {
  52. for(int u=1; u<=n; ++u)
  53. if(r[u]>e*O[u]) return u;
  54. return 0;
  55. }
  56. void Push(int u,double a,double b)
  57. {
  58. p[u]=p[u]+a*b*r[u];
  59. for(auto v:R.e[u]) r[v]=r[v]+(1-a)*w[u][v]*b*r[u];
  60. r[u]=r[u]-b*r[u]+(1-a)*0*b*r[u];
  61. }
  62. void ApxPR(double a,double e,double b)
  63. {
  64. //Init
  65. for(int i=1; i<=n; ++i) p[i]=0, r[i]=1;
  66. //APXPR
  67. int u;
  68. while(u=Exist(e),u) Push(u,a,b);
  69. }
  70. void Init()
  71. {
  72. n=read();
  73. for(int m=read(); m--; AddEdge(read(),read(),read()));
  74. //Calc wij
  75. for(int i=1; i<=n; ++i)
  76. {
  77. int D=0;
  78. for(int j=1; j<=n; ++j) D+=A[i][j];
  79. for(int j=1; j<=n; ++j) w[i][j]=1.0*A[i][j]/D;
  80. }
  81. //ApxPR
  82. ApxPR(alpha,epsilon,beta);
  83. Output();
  84. }
  85. }PR;
  86. int main()
  87. {
  88. PR.Init();
  89. return 0;
  90. }

总结

PageRank最初的几个算法虽然容易理解,但在当时确实很具有创新性,比如PageRank、Weighted PageRank和PageRank based on Visits of Links。
但是之后的提出的两个算法 WPR(VOL)和EWPR(VOL),只是简单地将前面的算法进行结合,没太有创新性,且内容都有很大的错误,合理怀疑只是在水论文。
除了幂迭代法,还有很多其它算法,也比迭代法更复杂、有创意。

参考

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注