类动态规划求解较小规模的最大团问题(Python实现)
2016-12-22 18:56
886 查看
1.图:由点、边(点与点之间连线),组成的集合,如点集V=[0,1,2,3,4],边集E=[[1,3,4],[2,3,4],[4],[4],[]],则(V,E)就是一个图,其表达的意思如下:
该图中含有5个端点,分别为0,1,2,3,4,这些点存在V中,如端点1对应V[1]=1端点之间会连着线,称为边,如1和2之间连的边,就对应E中E[1]=[2,3,4]的元素2,如0和4之间连的边就对应E[0]=[1,3,4]含有元素4(E[4]=[]不含元素0是因为0<4,只需在E[0]中记录了4即可)
2.团:一个图可能会有多个团,团是V的子集,记为集合G,且保证G中任意两点之间都有连线如G=[1,3,4],其中1,3,4三点两两相连,一个图的包含元素最多的团称为最大团。
最大团问题网上的求解办法非常多(本身其实这是一个NP问题),这次比较特殊的地方是要求使用Python,而且如果有多个解要输出多个具体的解(也就是说中间结果要记录),总之题目描述就是:
输入:V,E
输出:最大团数目,最大团集合(如果有多个要求全部输出)
采用动态规划的思想就是将问题分段求解,可以将问题分为5段:求含有一个、两个、三个、四个、五个元素的团,其中含有N元素的团必包含含有N-1个元素的团,故只需在上一段问题的答案的基础上,尝试给团添加新的元素。
另外,实际上例如求解出两个元素的团,如本题中的[0,1],[0,3],[0,4],[1,2],[1,3],[1,4],[2,4],[3,4]时,已经裁掉了[0,2],[2,3]的下面的解了,所以也算有一点剪枝的意思。接下来直接看代码实现:
输出:
step0: [[0], [1], [2], [3], [4]]
step1: [[0, 1], [0, 3], [0, 4], [1, 2], [1, 4], [2, 4], [3, 4]]
step2: [[0, 1, 4], [0, 3, 4], [1, 2, 4]]
step3: []
step4: []
最大团数目是3个
最大团为:
[[0, 1, 4], [0, 3, 4], [1, 2, 4]]
step0: [[0], [1], [2], [3], [4]]
step1: [[0, 1], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
step2: [[0, 1, 3], [0, 1, 4], [0, 3, 4], [1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
step3: [[0, 1, 3, 4], [1, 2, 3, 4]]
step4: []
最大团数目是4个
最大团为:
[[0, 1, 3, 4], [1, 2, 3, 4]]
step0: [[0], [1], [2], [3], [4]]
step1: [[0, 1], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 4], [3, 4]]
step2: [[0, 1, 3], [0, 1, 4], [0, 3, 4], [1, 2, 4], [1, 3, 4]]
step3: [[0, 1, 3, 4]]
step4: []
最大团数目是4个
最大团为:
[[0, 1, 3, 4]]
其他的也没怎么测试了,如果有bug,各位读者看着改改吧= =。提供一个思路,仅作参考。
该图中含有5个端点,分别为0,1,2,3,4,这些点存在V中,如端点1对应V[1]=1端点之间会连着线,称为边,如1和2之间连的边,就对应E中E[1]=[2,3,4]的元素2,如0和4之间连的边就对应E[0]=[1,3,4]含有元素4(E[4]=[]不含元素0是因为0<4,只需在E[0]中记录了4即可)
2.团:一个图可能会有多个团,团是V的子集,记为集合G,且保证G中任意两点之间都有连线如G=[1,3,4],其中1,3,4三点两两相连,一个图的包含元素最多的团称为最大团。
最大团问题网上的求解办法非常多(本身其实这是一个NP问题),这次比较特殊的地方是要求使用Python,而且如果有多个解要输出多个具体的解(也就是说中间结果要记录),总之题目描述就是:
输入:V,E
输出:最大团数目,最大团集合(如果有多个要求全部输出)
采用动态规划的思想就是将问题分段求解,可以将问题分为5段:求含有一个、两个、三个、四个、五个元素的团,其中含有N元素的团必包含含有N-1个元素的团,故只需在上一段问题的答案的基础上,尝试给团添加新的元素。
另外,实际上例如求解出两个元素的团,如本题中的[0,1],[0,3],[0,4],[1,2],[1,3],[1,4],[2,4],[3,4]时,已经裁掉了[0,2],[2,3]的下面的解了,所以也算有一点剪枝的意思。接下来直接看代码实现:
#-*- coding=utf-8 -*- V=[0,1,2,3,4] # E=[[1,3,4],[2,4],[4],[4],[]] E=[[1,3,4],[2,3,4],[4],[4],[]] ###较小规模的最大团问题 import copy def isConnected(u,v): if u==-1 or v==-1:###虚拟节点-1与所有的节点都相连 return 1 edge_points=E[u] if v in edge_points: return 1 else: edge_points = E[v] if u in edge_points: return 1 else: return 0 def isConnectedAll(clique,v):#判断v是否和clique中所有节点相连 flag = 1 for i in clique: if not isConnected(i,v): flag =0 break return flag class Step: def __init__(self): self.maxClique = [] #计算完毕时的解集(每个阶段的实际结果) self.cliqueList = []#计算时用的解集 self.maxnC = 0 def maxCliqn(self):#计算当前阶段最大值 max = 0 for clique in self.cliqueList: if max < len(clique): max = len(clique) return max def isNew(self,clique): #判断一个解组合是否已经存在于该阶段的实际解集中 for cl in self.maxClique:#针对每个已存入的解集进行判断 diff = list(set(clique).difference(set(cl))) # 取解的差集 if (len(diff)): continue #差集不为空,说明不同,继续循环 else: return False # 差集为空,说明有个解完全一样,返回False return True def updateMaxClique(self):#更新当前阶段的最大团数目 self.maxnC= self.maxCliqn() for clique in self.cliqueList: if(len(clique)==self.maxnC): if self.isNew(clique): self.maxClique.append(clique) if __name__ == "__main__": n = len(V) solutions = {} for i in range(0,n): solutions[i]= Step() #初始化n个阶段 for v in V: a = [] a.append(v) solutions[0].cliqueList.append(a) solutions[0].updateMaxClique()#设置初始值 for i in range(1,n): #cliqList= solutions[i-1].maxClique preData = solutions[i-1] cliqList = preData.maxClique preMax = preData.maxnC for clique in cliqList:#针对前一阶段的每个clique求解 for v in V:#针对所有的点 tempclique = copy.deepcopy(clique)##必须使用深拷贝 if not v in tempclique:#如果该clique没有包含v if isConnectedAll(tempclique,v):#如果v与clique的所有点相连 tempclique.append(v)#加入该点 solutions[i].cliqueList.append(tempclique)#加入这个解 solutions[i].updateMaxClique() if not len(solutions[i].maxClique):#如果已经找不到更多的点加入团,那么后面的也不用计算了(比如找不到4个的团,那么5个的团也没必要再尝试计算) break for i in range(0,n): print("step"+str(i)+": "+str(solutions[i].maxClique)) for i in range(n-1,-1,-1): solution = solutions[i] if len(solution.maxClique): maxn = solution.maxnC print("最大团数目是"+ str(maxn)+"个") print("最大团为:") print(solution.maxClique) break值得注意的一点是,python里面直接用赋值符号得到的对象实例是使用的浅拷贝,跟java类似,所以要使用copy模块的deepcopy函数来建立临时列表做各种判断以及修改添加(因为我也对python不太熟悉,而且原来没仔细考虑过拷贝引用带来的问题,中间错了好一阵)。
输出:
V=[0,1,2,3,4] E=[[1,3,4],[2,4],[4],[4],[]]
step0: [[0], [1], [2], [3], [4]]
step1: [[0, 1], [0, 3], [0, 4], [1, 2], [1, 4], [2, 4], [3, 4]]
step2: [[0, 1, 4], [0, 3, 4], [1, 2, 4]]
step3: []
step4: []
最大团数目是3个
最大团为:
[[0, 1, 4], [0, 3, 4], [1, 2, 4]]
E=[[1,3,4],[2,3,4],[3,4],[4],[]]
step0: [[0], [1], [2], [3], [4]]
step1: [[0, 1], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
step2: [[0, 1, 3], [0, 1, 4], [0, 3, 4], [1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
step3: [[0, 1, 3, 4], [1, 2, 3, 4]]
step4: []
最大团数目是4个
最大团为:
[[0, 1, 3, 4], [1, 2, 3, 4]]
E=[[1,3,4],[2,3,4],[4],[4],[]]
step0: [[0], [1], [2], [3], [4]]
step1: [[0, 1], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 4], [3, 4]]
step2: [[0, 1, 3], [0, 1, 4], [0, 3, 4], [1, 2, 4], [1, 3, 4]]
step3: [[0, 1, 3, 4]]
step4: []
最大团数目是4个
最大团为:
[[0, 1, 3, 4]]
其他的也没怎么测试了,如果有bug,各位读者看着改改吧= =。提供一个思路,仅作参考。
相关文章推荐
- Python实现求解最大子数组问题
- 连续子向量的最大和问题(Python实现)
- [leetcode]分治法求解最大子序列问题——Java实现
- 实践多种搜索算法求解八数码问题python实现
- 动态规划求解连续子数组最大和问题(应该是新的描述方法?)
- python实现遗传算法求解TSP旅行商问题(详细解释)
- Python实现最大子树问题
- 动态规划求解最大字段和及其变种问题
- Python实现Json结构对比的小工具兼谈编程求解问题
- 使用Python求解最大公约数的实现方法
- python实现求解最长公共子序列LCS问题
- 计算4000000000以内最大的f(n)=n的值---字符串问题python实现(五)
- python使用分治法实现求解最大值的方法
- 动态规划 数塔问题求解 C++实现
- 动态规划求解最大字段和及其变种问题
- 01背包问题(动态规划)python实现
- 动态规划求解矩阵连乘问题Java实现
- python使用分治法实现求解最大值的方法
- <笔记><算法导论> 假设求解问题的算法需要f(n)毫秒,对下表中的每个函数f(n)和时间t,确定可以在时间t内求解的问题的最大规模n。
- 实践多种搜索算法求解八数码问题python实现