Spark系列 (七)SparkGraphX下的Pregel方法----完美解决单源最短路径的应用算法

Pregel框架:

一:Spark GraphX Pregel:

  • Pregel是google提出的用于大规模分布式图计算框架
    • 图遍历(bfs)
    • 单源最短路径(sssp)
    • pageRank计算
  • Pregel的计算有一系列迭代组成
  • Pregel迭代过程
    • 每个顶点从上一个superstep接收入站消息
    • 计算顶点新的属性
    • 在下一个superstep中向相邻的顶点发送消息
    • 当没有剩余消息时,迭代结束

二:Pregel计算过程:

Pregel函数源码及各个参数解析:
def pregel[A: ClassTag](
    // 图节点的初始信息
      initialMsg: A,
    // 最大迭代次数
      maxIterations: Int = Int.MaxValue,
    // 
      activeDirection: EdgeDirection = EdgeDirection.Either)(
      vprog: (VertexId, VD, A) => VD,
      sendMsg: EdgeTriplet[VD, ED] => Iterator[(VertexId, A)],
      mergeMsg: (A, A) => A)
    : Graph[VD, ED] = {
    Pregel(graph, initialMsg, maxIterations, activeDirection)(vprog, sendMsg, mergeMsg)
  }

参数说明
initialMsg图初始化的时候,开始模型计算的时候,所有节点都会先收到一个消息
maxIterations最大迭代次数
activeDirection规定了发送消息的方向
vprog节点调用该消息将聚合后的数据和本节点进行属性的合并
sendMsg激活态的节点调用该方法发送消息
mergeMsg如果一个节点接收到多条消息,先用mergeMsg 来将多条消息聚合成为一条消息,如果节点只收到一条消息,则不调用该函数

三:案例:单源最短路径

首先要清楚关于 顶点 的两点知识:

  1. 顶点 的状态有两种:
    (1)、钝化态【类似于休眠,不做任何事】
    (2)、激活态【干活】
  2. 顶点 能够处于激活态需要有条件:
    (1)、成功收到消息 或者
    (2)、成功发送了任何一条消息
第一步:调用pregel方法:

从5出发,除自身顶点外所有顶点都将接收一条初始消息initialMsg,使所有顶点处于激活态,并将属性改成无穷大。自身顶点为0。

第二步:第一次迭代:

所有顶点以EdgeDirection.Out的边方向调用sendMsg方法发送消息给目标顶点,如果 源顶点的属性+边的属性<目标顶点的属性,则发送消息。否则不发送。

之后只有两条边的信息发送成功了

5—>3(0+8<Double.Infinity , 成功),
5—>6(0+3<Double.Infinity , 成功)

此时只有5,3,6处于激活态了,3,6调用vprog方法,将属性合并。

第三步:第二次迭代:

处于激活态的3,6调用sendMsg方法发送消息。

最后只有3—>2(8+4<Double.Infinity,成功)

此时只有3,2处于激活态,2调用vprog方法将属性合并。

第四步:不断迭代,直至所有顶点处于钝化态

每个顶点的属性,就是顶点5到达各个顶点的最短距离。

案例代码如下:
package com.wyw
  import org.apache.spark.{SparkConf, SparkContext}
  import org.apache.spark.graphx._
  import org.apache.spark.rdd.RDD
object Pregel {

    //1、创建SparkContext
    val sparkConf = new SparkConf().setAppName("GraphxHelloWorld").setMaster("local[*]")
    val sparkContext = new SparkContext(sparkConf)

    //2、创建顶点
    val vertexArray = Array(
      (1L, ("Alice", 28)),
      (2L, ("Bob", 27)),
      (3L, ("Charlie", 65)),
      (4L, ("David", 42)),
      (5L, ("Ed", 55)),
      (6L, ("Fran", 50))
    )
    val vertexRDD: RDD[(VertexId, (String,Int))] = sparkContext.makeRDD(vertexArray)

    //3、创建边,边的属性代表 相邻两个顶点之间的距离
    val edgeArray = Array(
      Edge(2L, 1L, 7),
      Edge(2L, 4L, 2),
      Edge(3L, 2L, 4),
      Edge(3L, 6L, 3),
      Edge(4L, 1L, 1),
      Edge(2L, 5L, 2),
      Edge(5L, 3L, 8),
      Edge(5L, 6L, 3)
    )
    val edgeRDD: RDD[Edge[Int]] = sparkContext.makeRDD(edgeArray)


    //4、创建图(使用aply方式创建)
    val graph1 = Graph(vertexRDD, edgeRDD)

    /* ************************** 使用pregle算法计算 ,顶点5 到 各个顶点的最短距离 ************************** */

    //被计算的图中 起始顶点id,初始化把点属性全部换成正无穷
    val srcVertexId = 5L
    val initialGraph = graph1.mapVertices{
      case (vid,(name,age)) =>
        if (vid==srcVertexId)
          0.0
        else
          Double.PositiveInfinity
    }

    //5、调用pregel柯里化函数
    val pregelGraph: Graph[Double, PartitionID] = initialGraph.pregel(
      Double.PositiveInfinity,
      Int.MaxValue,
      EdgeDirection.Out
    )(
      // 传三个匿名函数参数
      // 我收到消息后与本节点判断
      (vid: VertexId, vd: Double, distMsg: Double) => {
        // 比较两者值
        val minDist = math.min(vd, distMsg)
        println(s"顶点$vid,属性$vd,收到消息$distMsg,合并后的属性$minDist")
        // 把小数据发送出去
        minDist
      },
      // 是不是要向下个点发数据
      (edgeTriplet: EdgeTriplet[Double,PartitionID]) => {
        // 检查起点+权重的值 和终点的值判断,小于才发送
        if (edgeTriplet.srcAttr + edgeTriplet.attr < edgeTriplet.dstAttr) {
          println(s"顶点${edgeTriplet.srcId} 给 顶点${edgeTriplet.dstId} 发送消息 ${edgeTriplet.srcAttr + edgeTriplet.attr}")

          Iterator[(VertexId, Double)]((edgeTriplet.dstId, edgeTriplet.srcAttr + edgeTriplet.attr))
        } else {
          Iterator.empty
        }
      },
      // 多个消息进行判断,取最小的消息发送,每次都处理2个
      (msg1: Double, msg2: Double) => math.min(msg1, msg2)
    )

    //6、输出结果
    //  pregelGraph.triplets.collect().foreach(println)
    //  println(pregelGraph.vertices.collect.mkString("\n"))

    //7、关闭SparkContext
    sparkContext.stop()
    
}

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Pregel模型是一种分布式图计算模型,用于处理大规模图数据。s-t最路径问题是在一个有向加权图中,给定起点s和终点t,求出从s到t的最路径。 下面是使用Pregel模型解决s-t最路径问题的思路: 1. 将图分成多个分区,每个分区包含若干个顶点和边。每个分区可以在不同的机器上运行,从而实现并行计算。 2. 每个顶点持有一个值,表示从起点s到该顶点的最距离。起点s的值为0,其他顶点的值为无穷大。 3. 以起点s为初始节点,向它的所有邻居节点发送消息,消息内容是从起点s到该节点的距离值。每个节点接收到来自邻居节点的消息后,将其与自己的值相加,如果和小于当前值,则更新自己的值。 4. 在下一轮迭代中,每个节点向它的邻居节点再次发送消息。如果节点的值发生了改变,则继续进行迭代,否则停止。 5. 最终,当所有节点的值不再发生改变时,算法结束。此时,终点t的值就是从起点s到终点t的最距离。 在Pregel模型中实现s-t最路径问题的关键是如何将消息传递和节点更新操作进行并行化。具体实现可以参考Pregel算法的框架,例如Google Pregel或Apache Giraph。 ### 回答2: Pregel模型是一种用于大规模图计算的并行计算模型,可以用于求解s-t最路径问题。下面是在Pregel模型下实现s-t最路径问题的思路: 1. 初始化:首先,将图中的所有节点的距离值初始化为无穷大(除了起始节点s的距离值初始化为0),将所有节点的状态设置为“活跃”。 2. 迭代计算:在每轮迭代中,对于所有活跃节点,处理如下步骤: a. 对于当前活跃节点v,计算v到其邻居节点u的距离(即通过v到达u的边的权重)加上v的当前距离,得到新的距离值dist。 b. 如果dist小于u的当前距离,则更新u节点的距离值为dist,并将u设置为“活跃”状态。 3. 终止条件:当没有节点被标记为“活跃”时,迭代结束。 4. 输出结果:最后,从终点t开始,沿着最路径依次回溯,输出到起点s的最路径。 在Pregel模型中,每轮迭代由一系列超步(superstep)组成,每个超步包含三个阶段:计算、通信和聚合。以上描述的算法思路可以转化为Pregel模型中的计算阶段的实现。具体实现时,可以使用消息传递的方式,将节点之间的计算结果进行通信和聚合。 总之,在Pregel模型下编程实现s-t最路径问题,需要先进行初始化,然后通过迭代计算每个节点的距离值,并将更新后的值进行消息传递和聚合,直到收敛为止。最终可以从终点t开始回溯,输出s-t的最路径。 ### 回答3: 在Pregel模型下编程实现s-t最路径问题的思路如下: 1. 初始步骤: - 将图中的每个节点初始化为一个无穷大的距离值,只有起点s的距离初始化为0。 - 将起点s标记为活跃点(active),其他节点标记为非活跃点。 2. 迭代步骤: - 对于每个活跃点,将其距离值发送给其邻居节点。 - 每个活跃点接收所有邻居节点发送的距离值,并更新自身的距离值为最小值。 - 将更新后的距离值发送给邻居节点。 - 重复上述步骤,直到所有节点都成为非活跃点,即没有进一步的更新可以进行。 3. 终止步骤: - 检查终点t的距离值,如果其仍然为无穷大,则表示起点s无法到达终点t,算法结束。 - 否则,根据距离值逆向追踪找到s到t的最路径。 在Pregel模型下,每个节点只需要关注与自己相关的信息,而不需要感知整个图,因此可以实现并行计算。每个节点在每个迭代步骤中,根据自身接收到的距离值来更新自己的距离值,并将更新后的距离值发送给邻居节点。这样,所有节点可以同时进行计算,直到没有进一步的更新可进行为止。 需要注意的是,Pregel模型需要一个消息缓冲区来存储各节点之间的消息传递。在每个迭代步骤中,所有节点都需要将自己的消息发送到缓冲区,并接收缓冲区中的消息。实现时可以使用分布式计算框架如Apache Giraph或GraphLab等。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值