您的位置:首页 > 其它

最短路径算法的命令式、函数式版本对比分析

2014-06-26 21:51 405 查看
C版本(来自 最短路径算法—Dijkstra(迪杰斯特拉)算法分析与实现(C/C++)

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
public class Dijkstra {
public static class PointInfo{
public int point;
public int prev_point;
public int dist;
public PointInfo(int p,int pp,int d){
point=p;
prev_point=pp;
dist=d;
}
}
private static PointInfo getPoint(Set<PointInfo> pset,int index){
for(PointInfo p:pset){
if(p.point==index){
return p;
}
}
return null;
}

private static void print(Set<PointInfo> knownSet){
for(int i=0;i<knownSet.size();i++){
int index=i;
log("node:"+(index+1)+",weight:"+getPoint(knownSet,index).dist);
while(index>=0){
log(""+(index+1));
index=getPoint(knownSet,index).prev_point;
}
}
}

public static void doit_functional_way(int[][]array){
int n=array[0].length;
Set<PointInfo> knownSet=new HashSet<PointInfo>();
Set<PointInfo> unknownSet=new HashSet<PointInfo>();
PointInfo source=new PointInfo(0,-1,0);
knownSet.add(source);
for(int p=1;p<n;p++){
unknownSet.add(new PointInfo(p,-1,Integer.MAX_VALUE));
}
PointInfo nearest=source;
Set<PointInfo> finalSet=iterate(knownSet, unknownSet, nearest, array);
print(finalSet);
}

private static Set<PointInfo> iterate(Set<PointInfo> knownSet,Set<PointInfo> unknownSet,PointInfo nearest,int[][]array){
if(unknownSet.isEmpty()){
return knownSet;
}
//update unkown points' dist
for(PointInfo p:unknownSet){
if(p.dist>nearest.dist+array[nearest.point][p.point]){
p.prev_point=nearest.point;
p.dist=nearest.dist+array[nearest.point][p.point];
}
}
//find minimum dist point
nearest=new PointInfo(-1,-1,Integer.MAX_VALUE);
for(PointInfo p:unknownSet){
if(p.dist<nearest.dist){
nearest=p;
}
}
//move from unknown to known
knownSet.add(nearest);
unknownSet.remove(nearest);

return iterate(knownSet, unknownSet, nearest, array);
}

public static int[][] makeUpGraph(){
int[][]array={
{0,         15,         10,         Infinite,   Infinite,   1},
{15,        0,          19,         33,         1,          Infinite},
{10,        19,         0,          27,         Infinite,   Infinite},
{Infinite,  33,         27,         0,          3,          45},
{Infinite,  1,          Infinite,   3,          0,          1},
{1,         Infinite,   Infinite,   45,         1,          0}

};
return array;
}
public static void main(String[]args){
doit_functional_way(makeUpGraph());
}
}


View Code
如果你认真分析最短路径算法,会发现它实际包含了三个核心的概念

已知最短路径的点集合A(每个点的信息包括:点的名字、路径上前一个点的名字、路径长度)

未知最短路径的点集合B(每个点的信息包括:点的名字、路径上前一个点的名字prev_point、路径长度distance)

刚刚从集合B中选出的距离最小的点nearest_point。

执行过程

初始状态

集合A:仅包含源点

集合B:包含除源点外的所有点

nearest_point:源点

迭代的过程

根据nearest_point,更新集合B中每个点的prev_point、distance

从集合B中选出distance最小的点,作为新的nearest_point

将nearest_point从集合B移动到集合A中

结束条件:当集合B为空时,算法结束

个人认为,除了代码运行速度,比较C、Java、Java函数式三个版本implementation的最重要标准是:是否能够直接有力的体现出以上三个核心概念(集合A、集合B、nearest_point)和执行过程。大家如果有耐心读完三个版本的代码,会发现Java两个在这一点上明显比C版本提上了一个层次。

Java普通版本和Java函数式版本的差别在于While循环iterate递归函数iterate递归函数更加直观了表现出了“状态变换”。实际上,整个迭代过程就是前一个状态(集合A,集合B,nearest_point)=>后一个状态(集合A、集合B、nearest_point)的变换。

很多函数式语言的拥护者都说:函数式语言的优点是无副作用。我觉得这完全是人与亦云的胡扯。恰恰相反,在使用函数式语言写代码时,它带给我最大的障碍就是无法使用副作用。目前在我眼中,它的最美之处是function as first class object。其次则是对抽象概念和状态变换过程的优雅表达方式。

(我本来用Haskell作为函数式版本的代码示例,但考虑到很多同学大概对这种语言不熟悉,于是使用Java,大家可以看到,即使在imperative语言的环境中,依然可以运用函数式语言的思维方式写出优雅的代码。)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: