【问题标题】:Solving a Maximum weight bipartite b-matching求解最大权重二分 b 匹配
【发布时间】:2018-11-27 05:29:30
【问题描述】:

我的问题是关于最大重量 B 匹配问题。

二分匹配问题将二分图中的两组顶点配对。 最大加权二分匹配 (MWM) 定义为匹配中边的值之和具有最大值的匹配。一个著名的 MWM 多项式时间算法是匈牙利算法。

我感兴趣的是一个特定的最大加权二分匹配,称为权重二分 B 匹配问题。权重二分 B 匹配问题 (WBM) 旨在匹配顶点,以便每个顶点匹配的顶点不超过其容量b 允许的数量。

此图(来自Chen et al.)显示了 WBM 问题。输入图的得分为 2.2,即所有边权重的总和。在满足红色度约束的所有子图中,解 H 的蓝色边缘得分最高,为 1.6。

虽然最近有一些解决 WBM 问题的作品(thisthis),但我找不到该算法的任何实现。有人知道 WBM 问题是否已经存在于 networkX 之类的任何库中?

【问题讨论】:

  • 不确定是否已实施 WBM。 This SO question/answer 应该会给你一些想法,如果你走自我实现的路线。
  • 谢谢,但不幸的是,这并没有帮助。 NetworkX 有二分图的匹配算法,但对于这种特殊情况,什么都没有。
  • 我不知道我是否完全了解MWM和WBM之间的区别。您能否添加一个说明性示例,说明两种解决方案的不同之处?然后可能会找到一种方法来实现,如果额外的工作最少
  • 匈牙利算法在不考虑每个顶点容量的情况下解决匹配问题。容量b 表示可以连接到该顶点的边数。我们可以说 MWM 是 WBM 的一个特定案例,其中每个顶点的容量为 1。使用顶点的容量对问题进行建模有助于我们解决一对多的问题,例如 1 个审阅者具有审阅@987654330 的能力@文章。
  • 抱歉,仍在尝试理解问题:这与knapsack problem (with multiple knapsacks) 有何不同?

标签: python graph networkx bipartite pulp


【解决方案1】:

让我们一步一步尝试,编写我们自己的函数来解决问题中指定的 WBM 问题。

使用pulp,当给定两组节点(u 和 v、边权重和顶点容量)时,制定和求解加权二分匹配 (WBM) 并不难。

在下面的第 2 步中,您会发现一个(希望很容易理解)函数将 WBM 表述为 ILP 并使用 pulp. 解决它,看看它是否有帮助。 (你需要pip install pulp

第 1 步:设置二分图容量和边权重

import networkx as nx
from pulp import *
import matplotlib.pyplot as plt

from_nodes = [1, 2, 3]
to_nodes = [1, 2, 3, 4]
ucap = {1: 1, 2: 2, 3: 2} #u node capacities
vcap = {1: 1, 2: 1, 3: 1, 4: 1} #v node capacities

wts = {(1, 1): 0.5, (1, 3): 0.3,
       (2, 1): 0.4, (2, 4): 0.1,
       (3, 2): 0.7, (3, 4): 0.2}

#just a convenience function to generate a dict of dicts
def create_wt_doubledict(from_nodes, to_nodes):

    wt = {}
    for u in from_nodes:
        wt[u] = {}
        for v in to_nodes:
            wt[u][v] = 0

    for k,val in wts.items():
        u,v = k[0], k[1]
        wt[u][v] = val
    return(wt)

第 2 步:求解 WBM(公式为整数程序)

为了让后面的代码更容易理解,这里有一些描述:

  • WBM 是指派问题的一种变体。
  • 我们将 RHS 的节点“匹配”到 LHS。
  • 边有权重
  • 目标是最大化所选边的权重总和。
  • 额外的一组约束:对于每个节点,所选边的数量必须小于其指定的“容量”。
  • PuLP Documentation如果你不熟悉puLP

.

def solve_wbm(from_nodes, to_nodes, wt):
''' A wrapper function that uses pulp to formulate and solve a WBM'''

    prob = LpProblem("WBM Problem", LpMaximize)

    # Create The Decision variables
    choices = LpVariable.dicts("e",(from_nodes, to_nodes), 0, 1, LpInteger)

    # Add the objective function 
    prob += lpSum([wt[u][v] * choices[u][v] 
                   for u in from_nodes
                   for v in to_nodes]), "Total weights of selected edges"


    # Constraint set ensuring that the total from/to each node 
    # is less than its capacity
    for u in from_nodes:
        for v in to_nodes:
            prob += lpSum([choices[u][v] for v in to_nodes]) <= ucap[u], ""
            prob += lpSum([choices[u][v] for u in from_nodes]) <= vcap[v], ""


    # The problem data is written to an .lp file
    prob.writeLP("WBM.lp")

    # The problem is solved using PuLP's choice of Solver
    prob.solve()

    # The status of the solution is printed to the screen
    print( "Status:", LpStatus[prob.status])
    return(prob)


def print_solution(prob):
    # Each of the variables is printed with it's resolved optimum value
    for v in prob.variables():
        if v.varValue > 1e-3:
            print(f'{v.name} = {v.varValue}')
    print(f"Sum of wts of selected edges = {round(value(prob.objective), 4)}")


def get_selected_edges(prob):

    selected_from = [v.name.split("_")[1] for v in prob.variables() if v.value() > 1e-3]
    selected_to   = [v.name.split("_")[2] for v in prob.variables() if v.value() > 1e-3]

    selected_edges = []
    for su, sv in list(zip(selected_from, selected_to)):
        selected_edges.append((su, sv))
    return(selected_edges)        

第 3 步:指定图形并调用 WBM 求解器

wt = create_wt_doubledict(from_nodes, to_nodes)
p = solve_wbm(from_nodes, to_nodes, wt)
print_solution(p)

这给出了:

Status: Optimal
e_1_3 = 1.0
e_2_1 = 1.0
e_3_2 = 1.0
e_3_4 = 1.0
Sum of wts of selected edges = 1.6

第 4 步:(可选)使用 Networkx 绘制图表

selected_edges = get_selected_edges(p)


#Create a Networkx graph. Use colors from the WBM solution above (selected_edges)
graph = nx.Graph()
colors = []
for u in from_nodes:
    for v in to_nodes:
        edgecolor = 'blue' if (str(u), str(v)) in selected_edges else 'gray'
        if wt[u][v] > 0:
            graph.add_edge('u_'+ str(u), 'v_' + str(v))
            colors.append(edgecolor)


def get_bipartite_positions(graph):
    pos = {}
    for i, n in enumerate(graph.nodes()):
        x = 0 if 'u' in n else 1 #u:0, v:1
        pos[n] = (x,i)
    return(pos)

pos = get_bipartite_positions(graph)


nx.draw_networkx(graph, pos, with_labels=True, edge_color=colors,
       font_size=20, alpha=0.5, width=3)

plt.axis('off')
plt.show() 

print("done")

蓝色边缘是为 WBM 选择的边缘。希望这可以帮助您继续前进。

【讨论】:

  • Ram,非常感谢您的详细解释。尽管您的解决方案确实很有趣,但它似乎忽略了问题的二分匹配特征。问题不仅在于优化参数,还在于在考虑每个顶点容量的同时保持图二分。否则,在双方不重要的情况下,您的解决方案可能是一个完美的解决方案。澄清一下,如果我们将每个顶点的容量设置为 1,我们应该在输出中有一个匹配的 bipartite 图,这不是您的解决方案的情况。
  • SIna,二分图(或二分匹配)有一些我必须遗漏的微妙属性。你能指出在上面的例子中哪些财产被侵犯了,为什么?我会解决的。
  • Ram,将顶点的容量设置为 1。您的解决方案应该给出一个类似于匈牙利算法输出的二分匹配图。您能否在匈牙利算法的输出上采用您的解决方案,以便针对容量进行优化?
猜你喜欢
  • 2011-04-26
  • 1970-01-01
  • 2011-05-24
  • 2013-01-27
  • 1970-01-01
  • 2014-05-09
  • 2011-07-09
  • 2019-12-18
  • 2017-11-22
相关资源
最近更新 更多