Spark GraphX实现Bron–Kerbosch算法-极大团问题

6 篇文章 0 订阅
5 篇文章 0 订阅

首先,说明两个概念:团、极大团。

  • clique)是一个无向图(undirected graph )的子图,该子图中任意两个顶点之间均存在一条边。又叫做完全子图。
  • 极大团(maximal clique)是一个团,该团不能被更大的团所包含,换句话说,再也不存在一个点与该团中的任意顶点之间存在一条边。

研究极大团的问题对社区发现等场景有较高的理论价值和现实意义。求一个无向图中的极大团问题是一个经典的NP完全问题,1973年曾提出了一个Bron-Kerbosch算法用来解决该问题,其伪代码如下:

 BronKerbosch(R, P, X):
       if P and X are both empty:
           report R as a maximal clique
       for each vertex v in P:
           BronKerbosch(R ⋃ {v}, P ⋂ N(v), X ⋂ N(v))
           P := P \ {v}
           X := X ⋃ {v}

该算法中有四个集合:R,P,X,N(v),其中:

R:目前已经在团中的顶点的集合

P:可能在团中的顶点的集合

X:不被考虑的顶点的集合

N(v):顶点v的所有直接邻居


以一个6个顶点的图为例:


用Spark GraphX实现Bron Kerbosch算法,搜索该图的极大团,代码如下:

import org.apache.spark.graphx.{Edge, EdgeDirection, Graph, VertexId}
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.mutable
import scala.collection.mutable.Set

object FindMaximalCliques {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("findMaximalCliques").setMaster("local")
    val sc: SparkContext = new SparkContext(conf)
    //定义顶点
    val vertexArray = Array(
      (1L,null),
      (2L,null),
      (3L,null),
      (4L,null),
      (5L,null),
      (6L,null)
    )
    //定义边
    val edgeArray = Array(
      Edge(6L, 4L,null),
      Edge(4L, 3L,null),
      Edge(4L, 5L,null),
      Edge(5L, 2L,null),
      Edge(3L, 2L,null),
      Edge(5L, 1L,null),
      Edge(2L, 1L,null)
    )

    //顶点和边转化为RDD
    val vertexRDD = sc.parallelize(vertexArray)
    val edgeRDD  = sc.parallelize(edgeArray)

    //根据顶点和边创建图
    val graph= Graph(vertexRDD,edgeRDD)

    //创建一个Map集合。key是图中的所有顶点;value是一个Set集合,保存了该key的所有邻居顶点
    val map: Map[VertexId, Set[VertexId]] = graph.collectNeighborIds(EdgeDirection.Either).collect()
      .map(t => {
        var set: mutable.Set[VertexId] = Set[VertexId]()
        t._2.foreach(t=>{set+=t})
        (t._1, set)
      }).toMap

    //R集合,初始值为空
    var R = Set[VertexId]()
    //P集合,初始值为所有的顶点
    var P = Set[VertexId]()
    //将所有的顶点添加到P集合中
    vertexRDD.collect().foreach(t=>{P+=t._1})
    //X集合,初始值为空
    var X = Set[VertexId]()

    //搜索极大团
    bronKerboschl(R,P,X,map)
  }

  /**
    * 搜索极大团的方法
    * @param R 目前已经在团中的顶点的集合
    * @param P 可能在团中的顶点的集合
    * @param X 不被考虑的顶点的集合
    * @param map Map集合,通过顶点获取该顶点的所有邻居顶点集合
    */
  def bronKerboschl(R:Set[VertexId],P:Set[VertexId],X:Set[VertexId],map:Map[VertexId, Set[VertexId]]): Unit ={
    if(P.toList.length ==0 && X.toList.length ==0){
      println("find a maximal cilique:"+R)
    }else {
      for (v <- P) {
        var Nv: Set[VertexId] = map.get(v).get
        bronKerboschl(R+v, P.intersect(Nv), X.intersect(Nv), map)
        X += v
        P -= v
      }
    }
  }

}

结果为:

find a maximal cilique:Set(1, 5, 2)
find a maximal cilique:Set(5, 4)
find a maximal cilique:Set(2, 3)
find a maximal cilique:Set(6, 4)
find a maximal cilique:Set(3, 4)


极大问题是在一个无向图中寻找一个最大的完全子图,其中每个节点都连接到其他节点。下面是一个使用 C++ 实现极大问题的示例代码,它使用 Bron-Kerbosch 算法来求解。 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; const int MAXN = 100; int n, m; bool g[MAXN][MAXN]; vector<int> clique, candidates, not_candidates; void bron_kerbosch() { if (candidates.empty() && not_candidates.empty()) { if (clique.size() > 1) { cout << "Clique: "; for (int i = 0; i < clique.size(); i++) { cout << clique[i] << " "; } cout << endl; } return; } int pivot = candidates.empty() ? not_candidates[0] : candidates[0]; for (int i = 0; i < candidates.size(); i++) { int v = candidates[i]; if (!g[pivot][v]) { continue; } vector<int> new_candidates, new_not_candidates; for (int j = 0; j < candidates.size(); j++) { if (g[v][candidates[j]]) { new_candidates.push_back(candidates[j]); } } for (int j = 0; j < not_candidates.size(); j++) { if (g[v][not_candidates[j]]) { new_not_candidates.push_back(not_candidates[j]); } } clique.push_back(v); bron_kerbosch(); clique.pop_back(); candidates.erase(remove(candidates.begin(), candidates.end(), v), candidates.end()); not_candidates.push_back(v); bron_kerbosch(); not_candidates.pop_back(); candidates = new_candidates; not_candidates = new_not_candidates; } } int main() { cin >> n >> m; for (int i = 0; i < m; i++) { int u, v; cin >> u >> v; g[u][v] = g[v][u] = true; } for (int i = 0; i < n; i++) { candidates.push_back(i); } bron_kerbosch(); return 0; } ``` 该代码使用邻接矩阵存储图,并使用 Bron-Kerbosch 算法来求解极大。输入格式为: ``` n m u1 v1 u2 v2 ... um vm ``` 其中 n 表示节点数,m 表示边数,接下来的 m 行表示无向图的边。例如,下面是一个简单的输入样例: ``` 5 6 0 1 0 2 0 3 1 2 1 3 2 3 ``` 运行代码后,输出将显示所有的极大。例如,对于上面的例子,输出将是: ``` Clique: 0 1 2 3 Clique: 1 2 3 Clique: 0 2 3 Clique: 0 1 3 ``` 这些分别是 {0,1,2,3}、{1,2,3}、{0,2,3} 和 {0,1,3}。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值