使用穷举法求解权重图最大割问题

使用穷举法求解权重图最大割问题

在本文中,我们将介绍如何通过穷举法来计算权重图的最大割问题。权重图的构造大致为:

g = nx.Graph() # 先创建一个空图

# 添加边及其对应的权重
g.add_edge(0, 1, weight=0.1)
g.add_edge(1, 2, weight=0.2)
g.add_edge(2, 3, weight=0.3)
g.add_edge(3, 0, weight=0.1)

# g.add_weighted_edges_from([(0, 1, 0.1), (1, 2, 0.2), (2, 3, 0.3), (3, 0, 0.1)])

nx.draw(g, with_labels=True, font_weight='bold')

4顶点图

我们的目的是将图中四个顶点 0,1, 2, 3分为两组,使得连接两个组的所有边的权重和最大。

比如在此图中,如果我们将 02 划为一组,而 13 划为另一组,可以得到组间边为 01, 12, 2330。权重和为 0.1 + 0.2 + 0.3 + 0.1 = 0.7 0.1+0.2+0.3+0.1=0.7 0.1+0.2+0.3+0.1=0.7。但如果我们将 0 划为一组,而 1, 23 划为另一组,可以得到组间边为 0130。权重和为 0.1 + 0.1 = 0.2 0.1+0.1=0.2 0.1+0.1=0.2

最大割问题就是讨论如何将顶点划分,所得到的割数,也就是组间权重和最大。

我们将整个求解过程分步进行分析。

最核心的部分是,如何将一个图以指定方式分为两组,并得到割数。我们以一个三顶点的图为例:

edges = {'12':0.5, '23':1} # 图的结构,两条边分别连接了 1 2 和 2 3 顶点,权重为 0.5 和 1

nodes = {1, 2, 3} # 图中所涉及到的顶点
nodes_left = {2} # 将顶点 2 放置于割图的一边,构成一个组
nodes_right = nodes - nodes_left  # 其余顶点放置于割图的另一边,构成另一个组

cut = 0 # 割数 初始化为 0

# 将割边权重累加求和
for edge in edges.keys(): # 遍历所有的边
    node_0 = int(edge[0]) # 边的两个顶点
    node_1 = int(edge[1]) 
    
    # 鉴别该边是否为割边:边的两个顶点是否分别在两个组里
    if (node_0 in nodes_left and node_1 in nodes_right) or (node_0 in nodes_right and node_1 in nodes_left):
        cut += edges[edge] # 如果为割边,就累加该权重
        
print(cut) # 打印割边权重和
1.5

对于一个稍复杂的图,比如四个顶点,存在顶点两两成组的情况。我们采用嵌套 for 循环来进行遍历。

edges = {'01':0.1, '12':0.2, '23':0.3, '03':0.4} # 定义构成图的边及其所对应的权重,每个边由其连接的两个顶点构成

nodes = {0, 1, 2, 3}

for i in list(nodes):
    nodes_left = {i} 
    nodes_right = nodes - nodes_left  

    cut = 0
    cut_edges = [] # 所有的割边
    for edge in edges.keys():
        node_0 = int(edge[0])
        node_1 = int(edge[1])
        if (node_0 in nodes_left and node_1 in nodes_right) or (node_0 in nodes_right and node_1 in nodes_left):
            cut += edges[edge]
            cut_edges.append(edge)

    print('one size:', [i],  '\tcut =', "%.1f" % cut, ' \tcut_edges are', cut_edges)
    
    
    for j in range(i):
        nodes_left = {i, j}
        nodes_right = nodes - nodes_left 

        cut = 0
        cut_edges = []
        for edge in edges.keys():
            node_0 = int(edge[0])
            node_1 = int(edge[1])
            if (node_0 in nodes_left and node_1 in nodes_right) or (node_0 in nodes_right and node_1 in nodes_left):
                cut += edges[edge]
                cut_edges.append(edge)

        print('two size:', [i, j],  'cut =', "%.1f" % cut, ' \tcut_edges are', cut_edges)
one size: [0] 	cut = 0.5  	cut_edges are ['01', '03']
one size: [1] 	cut = 0.3  	cut_edges are ['01', '12']
two size: [1, 0] cut = 0.6  	cut_edges are ['12', '03']
one size: [2] 	cut = 0.5  	cut_edges are ['12', '23']
two size: [2, 0] cut = 1.0  	cut_edges are ['01', '12', '23', '03']
two size: [2, 1] cut = 0.4  	cut_edges are ['01', '23']
one size: [3] 	cut = 0.7  	cut_edges are ['23', '03']
two size: [3, 0] cut = 0.4  	cut_edges are ['01', '23']
two size: [3, 1] cut = 1.0  	cut_edges are ['01', '12', '23', '03']
two size: [3, 2] cut = 0.6  	cut_edges are ['12', '03']

为了便于使用,我们也可以进一步,根据边信息来自动确认所涉及的顶点。就得到了我们最终的求解程序。

edges = {'01':0.1, '12':0.2, '23':0.3, '30':-0.4} 
nodes = set(int(i[0]) for i in edges.keys()) | set(int(i[1]) for i in edges.keys()) # 节点集为边中出现的所有顶点, | 操作为求并集
nodes_list = list(nodes)

for i in nodes_list:
    nodes_left = {i}
    nodes_right = nodes - nodes_left

    cut = 0
    cut_edges = []
    for edge in edges.keys():
        node_0 = int(edge[0])
        node_1 = int(edge[1])
        if (node_0 in nodes_left and node_1 in nodes_right) or (node_0 in nodes_right and node_1 in nodes_left):
            cut += edges[edge]
            cut_edges.append(edge)

    print('one size:', [i],  '\tcut =', "%.1f" % cut, ' \tcut_edges are', cut_edges)
    
    for j in nodes_list[:nodes_list.index(i)]:
        nodes_left = {i, j}
        nodes_right = nodes - nodes_left

        cut = 0
        cut_edges = []
        for edge in edges.keys():
            node_0 = int(edge[0])
            node_1 = int(edge[1])
            if (node_0 in nodes_left and node_1 in nodes_right) or (node_0 in nodes_right and node_1 in nodes_left):
                cut += edges[edge]
                cut_edges.append(edge)

        print('two size:', [i, j],  'cut =', "%.1f" % cut, ' \tcut_edges are', cut_edges)
one size: [0] 	cut = -0.3  	cut_edges are ['01', '30']
one size: [1] 	cut = 0.3  	cut_edges are ['01', '12']
two size: [1, 0] cut = -0.2  	cut_edges are ['12', '30']
one size: [2] 	cut = 0.5  	cut_edges are ['12', '23']
two size: [2, 0] cut = 0.2  	cut_edges are ['01', '12', '23', '30']
two size: [2, 1] cut = 0.4  	cut_edges are ['01', '23']
one size: [3] 	cut = -0.1  	cut_edges are ['23', '30']
two size: [3, 0] cut = 0.4  	cut_edges are ['01', '23']
two size: [3, 1] cut = 0.2  	cut_edges are ['01', '12', '23', '30']
two size: [3, 2] cut = -0.2  	cut_edges are ['12', '30']
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值