Bron-Kerbosh算法求解极大团

参考资料:https://www.jianshu.com/p/437bd6936dad

这篇文章讲得很好,本文的代码也是参照这篇文章,用python实现。

什么是极大团?

团、极大团、最大团的定义请问这篇文章:https://www.jianshu.com/p/dabbc78471d7

为什么会用到极大团?

了解到极大团这个知识,是在构建任务指派模型的时候接触到的。在对任务进行指派时,每个任务都有其开始时间、结束时间,在不考虑员工资质的情况下,任务之间可能会存在时间上的冲突,如下图所示。这幅图中存在哪些极大团
呢?


根据极大团的原理:如果一个团不被其他任一团所包含,即它不是其他任一团的真子集,则称该团为图G的极大团(maximal clique)。

怎么用到模型上?

  1. 回到上面两幅图,需要对第一幅图中的5个任务进行人员指派,应该需要以下约束条件:

当两任务存在时间冲突时:
xi,k+xj,k≤1,i,j∈N,k∈Kx_{i,k}+x_{j,k}\le 1 , i,j\in N, k\in K xi,k​+xj,k​≤1,i,j∈N,k∈K
其中,i,ji,ji,j是任务的序号,kkk是员工的序号。

展开看(这里先不管员工):
x1+x2≤1,x1+x3≤1,x1+x4≤1,x2+x3≤1,x2+x4≤1,x3+x4≤1,x3+x5≤1,x4+x5≤1x_1 + x_2 \le 1 \ , \ x_1 + x_3 \le 1 \ , \ x_1 + x_4 \le 1 \ , \ x_2 + x_3 \le 1 \ , \\x_2 + x_4 \le 1 \ , \ x_3 + x_4 \le 1 \ , \ x_3 + x_5 \le 1 \ , \ x_4 + x_5 \le 1 x1​+x2​≤1 , x1​+x3​≤1 , x1​+x4​≤1 , x2​+x3​≤1 ,x2​+x4​≤1 , x3​+x4​≤1 , x3​+x5​≤1 , x4​+x5​≤1

  1. 用极大团构建约束,第二幅图列出了5个任务的所有极大团,因为极大团中所包含的任务都是存在时间冲突的,所以在一个团中,只能有一个为1。

clique1,2,3,4→x1+x2+x3+x4≤1clique3,4,5→x3+x4+x5≤1clique{1,2,3,4} \to x_1 + x_2 + x_3 +x_4 \le 1 \\ clique{3,4,5} \to x_3 + x_4 + x_5 \le 1 clique1,2,3,4→x1​+x2​+x3​+x4​≤1clique3,4,5→x3​+x4​+x5​≤1

从上面的两种约束可以看出,使用极大团构建约束,约束的数量将会大大减少,特别是当任务的数量更大的时候。

现在就是如何找到极大团。

Bron-Kerbosch算法

算法的思路、过程等,我建议你看这篇文章:极大团(maximal clique)算法:Bron-Kerbosch算法

下面给出用python编写求解极大团的代码。

首先这是未改进版本的BK算法,以下也给出了几个小算例。

import copy
import time
import pandas as pd
import numpy as np# BronKerbosch,cnt记录目前是第几层
def BronKerbosch(d, rn, pn, xn):# 判断P、X是否为空,为空则找到最大值if pn == 0 and xn == 0:route.append(copy.deepcopy(R[d]))# 遍历P中的每一个v,len(P)==0时,搜索到终点for j in range(pn):# 取出P中的第j个点v = P[d][j]R[d+1] = []     # 因为后面是直接在list后面添加元素,所以先将下一层的list清空for k in range(rn):R[d+1].append(R[d][k])R[d+1].append(v)    # 将v节点,添加到R集合中# 用来分别记录下一层中P集合和X集合中节点的个数tp, tx = 0, 0# 更新X集合(下一层X集合),保证X集合中的点都能与R集合中所有的点相连接X[d + 1] = []   # 因为后面是直接在list后面添加元素,所以先将下一层的list清空for k in X[d]:if conf[v][k]:X[d + 1].append(k)tx += 1# 更新P集合时,同时以R、X两个集合作为依据,在X中的元素不能出现在P中P[d+1] = []     # 因为后面是直接在list后面添加元素,所以先将下一层的list清空for k in P[d]:if conf[v][k] and k not in X[d]:P[d+1].append(k)tp += 1# 递归进入下一层BronKerbosch(d+1, rn+1, tp, tx)# 完成后,将操作的节点,放入X中,开始下面的寻找X[d].append(v)xn += 1# 任务冲突矩阵
def conflict(data):m = len(data)M = [[0]*m]*mM = np.array(M)for k in range(m):for j in range(k+1,m):if data.taskStartTime[j] <= data.taskStartTime[k] <= data.taskEndTime[j] \or data.taskStartTime[j] <= data.taskEndTime[k] <= data.taskEndTime[j]:M[j,k] = 1M[k,j] = 1return M# 获取冲突矩阵
st = time.time()
data = pd.read_excel('data.xlsx')
data.taskStartTime = pd.to_datetime(data.taskStartTime)
data.taskEndTime = pd.to_datetime(data.taskEndTime)
conf = conflict(data)
# conf = [[0, 1, 1, 0], [1, 0, 1, 1], [1, 1, 0, 0], [0, 1, 0, 0]]
# conf = [[0, 1, 0, 1, 1], [1, 0, 1, 0, 1], [0, 1, 0, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 0]]
# conf = [[0,1,1,1,0,0,0],[1,0,1,1,1,0,0],[1,1,0,1,0,0,0],[1,1,1,0,1,1,0],[0,1,0,1,0,0,1],[0,0,0,1,0,0,0],[0,0,0,0,1,0,0]]
# 节点总数
n = len(conf)
# 定义三个集合,R已确定的极大团顶点的集合,P未处理顶点集,X以搜过的并且属于某个极大团的顶点集合
R, P, X = [[]]*n, [[]]*n, [[]]*n
P[0] = [i for i in range(n)]    # 初始化未处理集合
route = []
BronKerbosch(0, 0, len(conf), 0)
print(len(route))
for key in route:print(key)
print(time.time() - st)

下面是经过改进的代码,改进的原理也请看上面那篇文章。

import copy
import time
import pandas as pd
import numpy as np# BronKerbosch,cnt记录目前是第几层
def BronKerbosch(d, rn, pn, xn, u):# 判断P、X是否为空,为空则找到最大值if pn == 0 and xn == 0:route.append(copy.deepcopy(R[d]))if len(P[d]) > 0:u = P[d][0]  # 记录最近放入P集合中的元素# 遍历P中的每一个v,len(P)==0时,搜索到终点for j in range(pn):# 取出P中的第j个点v = P[d][j]# 判断u,v是否是邻居,是则跳过if u != -1 and conf[u][v] == 1: continueR[d + 1] = []  # 因为后面是直接在list后面添加元素,所以先将下一层的list清空for k in range(rn):R[d + 1].append(R[d][k])R[d + 1].append(v)  # 将v节点,添加到R集合中# 用来分别记录下一层中P集合和X集合中节点的个数tp, tx = 0, 0# 更新X集合(下一层X集合),保证X集合中的点都能与R集合中所有的点相连接X[d + 1] = []  # 因为后面是直接在list后面添加元素,所以先将下一层的list清空for k in X[d]:if conf[v][k]:X[d + 1].append(k)tx += 1# 更新P集合时,同时以R、X两个集合作为依据,在X中的元素不能出现在P中P[d + 1] = []  # 因为后面是直接在list后面添加元素,所以先将下一层的list清空for k in P[d]:if conf[v][k] and k not in X[d]:P[d + 1].append(k)tp += 1# 递归进入下一层BronKerbosch(d + 1, rn + 1, tp, tx, u)# 完成后,将操作的节点,放入X中,开始下面的寻找X[d].append(v)xn += 1# print("==========")# 任务冲突矩阵
def conflict(data):m = len(data)M = [[0]*m]*mM = np.array(M)for k in range(m):for j in range(k+1,m):if data.taskStartTime[j] <= data.taskStartTime[k] <= data.taskEndTime[j] \or data.taskStartTime[j] <= data.taskEndTime[k] <= data.taskEndTime[j]:M[j,k] = 1M[k,j] = 1return M# 获取冲突矩阵
st = time.time()
data = pd.read_excel('data.xlsx')
data.taskStartTime = pd.to_datetime(data.taskStartTime)
data.taskEndTime = pd.to_datetime(data.taskEndTime)
conf = conflict(data)
# conf = [[0, 1, 1, 0], [1, 0, 1, 1], [1, 1, 0, 0], [0, 1, 0, 0]]
# conf = [[0, 1, 0, 1, 1], [1, 0, 1, 0, 1], [0, 1, 0, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 0]]
# conf = [[0, 1, 1, 1, 0, 0, 0], [1, 0, 1, 1, 1, 0, 0], [1, 1, 0, 1, 0, 0, 0], [1, 1, 1, 0, 1, 1, 0],
#         [0, 1, 0, 1, 0, 0, 1], [0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0]]
# 节点总数
n = len(conf)
# 每个节点的边数
node_sum = []
for i in range(n):node_sum.append(sum(conf[i]))
# 定义三个集合,R已确定的极大团顶点的集合,P未处理顶点集,X以搜过的并且属于某个极大团的顶点集合
R, P, X = [[]] * n, [[]] * n, [[]] * n
# 初始化,以边数做倒序
P[0] = sorted(range(len(node_sum)), key=lambda k: node_sum[k], reverse=True)
route = []
BronKerbosch(0, 0, len(conf), 0, -1)
print(len(route))
for key in route:print(key)
print(time.time() - st)

任务数较小时,改进前后的求解时间上没有很大的差异,这里我用了一份其他的数据进行测试,任务数70多个,改进前求解需要24秒左右,改进后仅需0.4秒。

(本文为学习记录用)

代码中的数据:

链接:https://pan.baidu.com/s/1zHq-CQs3kts_Nfj4C_XM2Q
提取码:e5dq

Bron-Kerbosh算法求解极大团相关推荐

  1. Bron–Kerbosch算法-最大独立集与最大团

    Bron-Kerbosch 算法计算图的最大全连通分量(团clique) 最大独立集: 顶点集V中取 K个顶点,其两两间无连接. 最大团: 顶点集V中取 K个顶点,其两两间有边连接. 最大团中顶点数量 ...

  2. em算法python代码_EM 算法求解高斯混合模型python实现

    注:本文是对<统计学习方法>EM算法的一个简单总结. 1. 什么是EM算法? 引用书上的话: 概率模型有时既含有观测变量,又含有隐变量或者潜在变量.如果概率模型的变量都是观测变量,可以直接 ...

  3. All Friends 极大团

    搞懂了什么是团  什么是极大团  什么是最大团 极大团就是  不是任何团的真子集  最大团就是点数最多的极大团 这题就是求极大团的个数 用bk算法 #include <iostream> ...

  4. 【布局优化】基于粒子群算法求解集线器位置分配问题附matlab代码

    1 内容介绍 ​本文基于MATLAB软件结合粒子群算法,实现了粒子群算法中粒子的适应度计算,极大的减少了优化计算耗时,对适应度计算较为耗时的优化计算,有着明显的效果;最后本文采用粒子群优化算法优化计算 ...

  5. AlphaBeta剪枝算法求解博弈树最优选择 头歌实验平台

    AlphaBeta剪枝算法求解博弈树最优选择 头歌实验平台 前言 一.AlphaBeta剪枝是什么? 1.由来, 最大最小决策树 2.发展 3. AlphaBeta剪枝 二.实验算法伪代码 三.实验算 ...

  6. 利用HTML5 Canvas和Javascript实现的蚁群算法求解TSP问题演示

    HTML5提供了Canvas对象,为画图应用提供了便利. Javascript可执行于浏览器中, 而不须要安装特定的编译器: 基于HTML5和Javascript语言, 可随时编写应用, 为算法測试带 ...

  7. 计算机基础算法棋盘覆盖,分治算法求解棋盘覆盖问题互动教学过程.doc

    分治算法求解棋盘覆盖问题互动教学过程 分治算法求解棋盘覆盖问题互动教学过程 摘要:针对算法设计与分析课程难度较大.对学生编程能力要求较高的现状,通过对棋盘覆盖问题的分治算法求解过程进行互动教学设计,引 ...

  8. MATLAB实战系列(二十九)-头脑风暴优化(BSO)算法求解旅行商问题(TSP)-交叉算子

    前言 代码明细可参见 MATLAB实战系列(八)-头脑风暴优化(BSO)算法求解旅行商问题(TSP)(附MATLAB代码) 交叉算子的实现机制 我们还是以求解TSP问题为例,8个城市的坐标如下所示. ...

  9. matlab实战系列之人工鱼群算法求解TSP问题原理解析(下篇源码解析)

    从算法的名字中可以看出该算法是群体智能优化算法中的一种,人工鱼群算法通过模拟鱼群的觅食.聚群.追尾.随机等行为在搜索域中进行寻优. 人工鱼群算法有三个比较重要的概念:视野范围.k-距离邻域.多条鱼的中 ...

最新文章

  1. 想知道什么是“成员变量”吗?
  2. centos 删除crontab_centos crontab(定时任务) 使用
  3. .substr()在字符串每个字母前面加上一个1
  4. MoSonic:对SubSonic的分布式存储、缓存改进尝试(2)
  5. php Reportico 开源报表
  6. iOS socket 套接字编程
  7. php 跳转到指定url_PHP想要实现页面跳转功能具体怎么操作?(函数标签示例)...
  8. ubuntu100%快速安装搜狗输入法
  9. [转]为什么要使用框架
  10. 微信小程序云开发教程-微信小程序的JS高级-全局数据读写
  11. UltraEdit打开就报错,文件找不到
  12. 单例模式和内部类的初步认识
  13. 什么是Linux 软件源
  14. 北京地铁拥挤度实时查询
  15. 如何快速制作Png格式的图片(不需要PS软件)
  16. 听完网易大佬描述 “ 软件测试工程师的一生 ”,我哭了!
  17. 第二期预告|中国工程院院刊:信息与电子工程领域青年学术前沿论坛
  18. 史上最详尽的LCT讲解
  19. Python开发【第五章】:常用模块
  20. 薪火培训电控第一讲——单片机与GPIO

热门文章

  1. 绝地求生模拟登陆!高难度JS解密教程,Python高级爬虫开发,
  2. 基于java与mssql的合同管理系统设计文档_合同管理系统的设计与实现
  3. 详解MyBatis的Dao层实现和配置文件深入
  4. 20220714给AIO-3568J适配OpenHarmony-v3.1-beta(编译Buildroot)
  5. 互联网晚报 | 椰树推出猛男直播销售额不到1千;小鹏回应核心团队换血:部分信息存疑,有待考证;华为 P60 系列手机已量产...
  6. 每日一题--设计一个呼叫中心系统(Google面试推荐书Cracking the Coding Interview)
  7. MVC模式 和 三层架构——应用案例
  8. Python中标识符的详解
  9. 容器云系列之Docker镜像和仓库管理
  10. Python 也可以分析公众号