类动态规划求解较小规模的最大团问题(Python实现)

1.图:由点、边(点与点之间连线),组成的集合,如点集V=[0,1,2,3,4],边集E=[[1,3,4],[2,3,4],[4],[4],[]],则(V,E)就是一个图,其表达的意思如下:

《类动态规划求解较小规模的最大团问题(Python实现)》

该图中含有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,各位读者看着改改吧= =。提供一个思路,仅作参考。

    原文作者:动态规划
    原文地址: https://blog.csdn.net/hahajinbu/article/details/53818662
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞