单源最短路径问题
是指从一个指定顶点s到其他所有顶点i之间的距离,因为是单一顶点到其他顶点的距离,所以称为单源。
设图G<V,E>是一个有向加权网络,其中V和E分别为顶点集合和边集合,其边权邻接矩阵为W,边上权值w(i,j)>0,i,j∈V,V={0,1,…,N-1}。
设dist(i)为最短的路径长度,即距离,其中s∈V且i≠s。这里我是学习的Dijkstra算法,并将其并行化。
最短路径串行算法
Dijkstra算法是单源最短路径问题的经典解法之一,还有很多单源最短路径的算法,比如Bellman-ford、spfa,下面说说Dijkstra算法的基本思想:
假定有一个待搜索的顶点表VL,初始化时做:dist(s)<-0,dist(i)=∞(i≠s),VL=V。每次从VL(非空集)中选取这样的一个顶点u,它的dist(u)最小。将选出的u点作为搜索顶点,对于其他还在VL内的顶点v,若
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<math.h>
using namespace std;
const int maxn = 10000;
int dist[maxn];
int mp[maxn][maxn];
bool vis[maxn];
int n,m;
const int oo = INT_MAX;
void Dijkstra(int s)
{
for(int i=1; i<=n; i++)
{
dist[i] = mp[s][i];
vis[i]=false;
}
dist[s] = 0;
vis[s] = true;
int u;
for(int i=2; i<=n; i++)
{
int minn = oo;
for(int j=1; j<=n; j++)
if(!vis[j]&&minn>dist[j])
{
minn = dist[j];
u=j;
}
vis[u]=true;
for(int j=1; j<=n; j++)
if(!vis[j]&&mp[u][j]<oo)
{
if(dist[u]+mp[u][j]<dist[j])
dist[j]=dist[u]+mp[u][j];
}
}
}
int main()
{
for(int i=0; i<maxn; i++)
for(int j=0; j<maxn; j++)
{
if(i!=j)mp[i][j]=oo;
else mp[i][j]=0;
}
scanf("%d%d",&n,&m);
for(int i=0; i<m; i++)
{
int s,t,len;
scanf("%d%d%d",&s,&t,&len);
mp[s][t]=len;
}
int st;
scanf("%d",&st);
Dijkstra(st);
for(int i=1; i<=n; i++)
printf("%d ",dist[i]);
puts("");
return 0;
}
以下这段代码我们可以进行并行化,每个处理器分派n = N/p(注意这里的n跟我程序中写的n不是一个n)个节点进行初始化
for(int i=1; i<=n; i++)
{
dist[i] = mp[s][i];
vis[i]=false;
}
dist[s] = 0;
下面这段代码可以并行化为:首先每个处理器分派n个节点分别求局部最小值,然后再p个处理器合作求全局最小值,最后再将这一最小值广播出去。p个处理器合作方法如下:当p为偶数时,后p/2个处理器将自己的局部最小值分别发送到对应的前p/2个处理器中,由前p/2个处理器比较出2个局部最小值中相对较小者并保留。当p为奇数时,设p=2h+1,则后h个处理器的值分别发送到前h个处理器中,比较并保留当前最小值。一次这样的循环过后,p个最小值减少了一半,两次后变为1/4,如此一层一层的比较,logp次循环后,就可以得出唯一的全局最小值。
for(int j=1; j<=n; j++)
if(!vis[j]&&minn>dist[j])
{
minn = dist[j];
u=j;
}
下面的这段代码,可以每个处理器分配n个顶点,然后独立进行更新dist的操作。
for(int j=1; j<=n; j++)
if(!vis[j]&&mp[u][j]<oo)
{
if(dist[u]+mp[u][j]<dist[j])
dist[j]=dist[u]+mp[u][j];
}
根据以上思路,最短路径的并行算法就很明了了,使用了p个处理器,那么时间复杂度就是O(N^2/p+Nlogp)。
我们能够进行并行的方法就比较多了,可以用openmp或者mpi等等。最近在学习mpi并行编程,学的还不是很6,具体并行的程序就不贴啦。