SparkGraphX加权最短路径算法实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wcandy001/article/details/51613674

版本:spark 1.6
该版本自带的最短路径算法shortestPaths没办法自定义权重(默认每条边的权重都一样),不符合现实生活,比如在地图中计算两个位置的最短路线,要考虑线路的长度,线路的拥堵情况等多方面,所以写了个加权最短路径算法加入距离属性

算法说明:
加权最短路径算法计算所有的顶点到特定点的距离:
1.初始化开始顶点的距离为0,所有其他节点距离设置为无穷大
2. 将当前顶点设为初始顶点
3. 所有与当前顶点相邻的顶点,将距离设置为当前顶点的距离加上将当前顶点连接到该其他顶点的边的长度之和的较小值。
4.将当前顶点标记为已访问
5.迭代停止条件, 如果没有未访问的顶点
6.迭代第三步
代码:

 def dijkstra[VD](g:Graph[VD,Double],origin:VertexId)={
    var g2=g.mapVertices((vid,vd)=> (false,if(vid == origin) 0 else Double.MaxValue)) //修改点的属性,如果是特殊顶点属性为0,其他为无穷,false标志着是否被访问过
    //每次循环都会访问一个点并标记为true,共循环vertices.count-1次(特定节点除外)
    for(i <- 1L to g.vertices.count-1){
      val currentVertexId=
        g2.vertices.filter(!_._2._1)
            .fold((0L,(false,Double.MaxValue)))((a,b)=>
              if(a._2._2 < b._2._2) a else b)._1
      //迭代每一个顶点,取出所有顶点的属性最小值(路径长度),currentVertexId,是属性最小值的顶点ID
      val newDistances=g2.aggregateMessages[Double](
        ctx=> if (ctx.srcId == currentVertexId)//找到srcId是最小属性的那条关系
              ctx.sendToDst(ctx.srcAttr._2+ctx.attr), //把最小路径和边的路径合在一起发给dstId
        (a,b)=>math.min(a,b))//收到消息取最小
      g2=g2.outerJoinVertices(newDistances)((vid,vd,newSum)=>
        (vd._1 || vid ==currentVertexId,math.min(vd._2,newSum.getOrElse(Double.MaxValue))))
      //这里走过的点这设为true,把每个点的路径长度属性更新
    }
    //g2里面存的都是每个顶点到指定顶点的最小距离
    g.outerJoinVertices(g2.vertices)((vid,vd,dist)=>
      (vd,dist.getOrElse((false,Double.MaxValue))._2))
  }

准备数据
vertices
(1L, “A”),
(2L, “B”),
(3L, “C”),
(4L, “D”),
(5L, “E”),
(6L, “F”),
(7L, “G”)
edges(边属性是两点间距离)
Edge(1L, 2L, 7.0),
Edge(1L, 4L, 5.0),
Edge(2L, 3L, 8.0),
Edge(2L, 4L, 9.0),
Edge(2L, 5L, 7.0),
Edge(3L, 5L, 5.0),
Edge(4L, 5L, 15.0),
Edge(4L, 6L, 6.0),
Edge(5L, 6L, 8.0),
Edge(5L, 7L, 9.0),
Edge(6L, 7L, 11.0)
用Gephi绘图(后面附赠graph对象生成.gexf文件方法):
这里写图片描述
步骤:
1.把边和顶点数据加载生成graph对象
2.调用dijkstra方法传入图对象和要算的顶点(本例用的顶点”1L”)
例:
dijkstra(myGraph, 1L)
3.取出顶点,最终的长度在顶点属性的第二个参数
.vertices.map(_._2).collect
结果为每个点到A(“1L”)的最短距离
这里写图片描述
大家可以根据上面的gephi绘图查看结果(最短距离)是否正确

最短路径算法只需在顶点属性中加一个list记录路径就好,剩下其他的和之前的一样
代码:

def dijkstra[VD](g:Graph[VD,Double], origin:VertexId) = {
    var g2 = g.mapVertices(
      (vid,vd) => (false, if (vid == origin) 0 else Double.MaxValue,
        List[VertexId]()))//新加入了一个字段用来记录路线
    for (i <- 1L to g.vertices.count-1) {
      val currentVertexId =
        g2.vertices.filter(!_._2._1)
          .fold((0L,(false,Double.MaxValue,List[VertexId]())))((a,b) =>
            if (a._2._2 < b._2._2) a else b)
          ._1 //找到最短路径的节点
      val newDistances = g2.aggregateMessages[(Double,List[VertexId])](
        ctx => if (ctx.srcId == currentVertexId)
          ctx.sendToDst((ctx.srcAttr._2 + ctx.attr,
            ctx.srcAttr._3 :+ ctx.srcId)), //这里把顶点id加入list
        (a,b) => if (a._1 < b._1) a else b)
      g2 = g2.outerJoinVertices(newDistances)((vid, vd, newSum) => {
        val newSumVal =
          newSum.getOrElse((Double.MaxValue,List[VertexId]()))
        (vd._1 || vid == currentVertexId,
          math.min(vd._2, newSumVal._1),
          if (vd._2 < newSumVal._1) vd._3 else newSumVal._2)})
    }
    g.outerJoinVertices(g2.vertices)((vid, vd, dist) =>
      (vd, dist.getOrElse((false,Double.MaxValue,List[VertexId]()))
        .productIterator.toList.tail))
  }

最终结果
这里写图片描述
list中记载的是每个顶点到达A(“1L”)的最短路径

.gexf文件生成方式

def toGexf[VD,ED](g:Graph[VD,ED])=
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +"<gexf xmlns=\"http://www.gexf.net/1.2draft\" version=\"1.2\">\n" +" <graph mode=\"static\" defaultedgetype=\"directed\">\n" +" <nodes>\n" +g.vertices.map(v => " <node id=\"" + v._1 + "\" label=\"" +v._2 + "\" />\n").collect.mkString +" </nodes>\n" +" <edges>\n" +g.edges.map(e => " <edge source=\"" + e.srcId +"\" target=\"" + e.dstId + "\" label=\"" + e.attr +"\" />\n").collect.mkString +" </edges>\n" +" </graph>\n" +"</gexf>"

val pw=new java.io.PrintWriter("myGraph.gexf")
pw.write(toGexf(toGexf(myGraph)))
pw.close

.gexf可用gephi直接使用

猜你喜欢

转载自blog.csdn.net/wcandy001/article/details/51613674