在图中找到完美的匹配

时间:2020-01-27 14:20:26

标签: algorithm graph mathematical-optimization combinatorics max-flow

我有这个问题:

航空公司有N架不同的飞机和T架飞行员。每个飞行员都有他可以飞行的飞机清单。每个航班需要两名飞行员。该公司希望尽可能多地同时飞行。找到一种算法,该算法可以查找是否可以同时进行所有航班。

这是我想到的在此图上找到最大流量的解决方案: enter image description here

我只是不确定容量应该是多少。你能帮我吗?

2 个答案:

答案 0 :(得分:0)

找到最大流量的好主意。

  • 对于来自源->飞行员的每个边,分配一个容量1。每个飞行员一次只能飞行一个飞机,因为它们同时运行。
  • 对于来自飞行员->飞机的每个边缘,分配一个容量1。如果该边缘充满流量1,则表示给定的飞行员正在该飞机上飞行。
  • 对于飞机的每个边缘->接收器,分配2的容量。这表示每个飞机必须正好由2个飞行员提供。

现在,找到最大流量。如果最终的最大流量是平面数量的两倍,则有可能满足约束条件。在这种情况下,飞机与飞行员之间处于满负荷状态的边缘表示匹配。

答案 1 :(得分:0)

另一个答案很好,但是您实际上不需要涉及流,因为可以将其减少到普通的最大二分匹配:

  • 对于每个平面,将另一个辅助平面添加到平面分区,其边缘与第一个平面相同。
  • 找到最大的二分匹配 M
  • 现在且仅当 M = 2 N 时答案才是正确的。

如果愿意,可以这样想:每个飞机都需要一个飞行员和一个副驾驶员,并且与每个飞机相关的两个顶点现在代表着这两个角色。

最大二分匹配的减少是线性时间,因此请使用Hopcroft–Karp algorithm找到匹配项,您可以在 O (| E |√| V |)中解决问题, em> E 是分区之间的边数, V = T + N

在实践中,使用基于最大流量的方法的改进应取决于实现的质量以及图形表示的特定选择,但这种方法可能会更好。

实施示例

为说明最后一点,让我们对这两种减少量在实践中的外观有所了解。由于内置的​​内存位置,图的一种常用表示法是CSR matrix的表示法,因此让我们假设输入是这样一个矩阵,其行对应于平面,其列对应于飞行员。

我们将使用Python library SciPy,该算法随附了用于最大二部匹配和最大流量的算法,并且该算法与引擎盖下图形的CSR矩阵表示形式兼容。

在上面给出的算法中,我们将需要构造图的偏置矩阵,并添加其他顶点。这仅是将输入矩阵堆叠在其自身之上的结果,就CSR数据结构而言,这很容易表达:按照Wikipedia的表示法,应仅重复COL_INDEX,而应将ROW_INDEX替换为与副本连接的ROW_INDEX ROW_INDEX,其中所有元素都增加ROW_INDEX的最后一个元素。

在SciPy中,对OP中的问题回答是或否的完整实现如下所示:

import numpy as np
from scipy.sparse.csgraph import maximum_bipartite_matching

def reduce_to_max_matching(a):
    i, j = a.shape
    data = np.ones(a.nnz * 2, dtype=bool)
    indices = np.concatenate([a.indices, a.indices])
    indptr = np.concatenate([a.indptr, a.indptr[1:] + a.indptr[-1]])
    graph = csr_matrix((data, indices, indptr), shape=(2*i, j))
    return (maximum_bipartite_matching(graph) != -1).sum() == 2 * i

在@HeatherGuarnera答案给出的最大流量方法中,我们将需要设置新图的完整邻接矩阵。这也相对简单。输入矩阵将显示为邻接矩阵的某个子矩阵,我们需要为源顶点添加一行,为目标顶点添加一行。 SciPy的最大流量求解器的example section of the documentation实际上包含了实际情况的说明。通过这种方式,完整的解决方案如下所示:

import numpy as np
from scipy.sparse.csgraph import maximum_flow

def reduce_to_max_flow(a):
    i, j = a.shape
    n = a.nnz
    data = np.concatenate([2*np.ones(i, dtype=int), np.ones(n + j, dtype=int)])
    indices = np.concatenate([np.arange(1, i + 1),
                              a.indices + i + 1,
                              np.repeat(i + j + 1, j)])
    indptr = np.concatenate([[0],
                             a.indptr + i,
                             np.arange(n + i + 1, n + i + j + 1),
                             [n + i + j]])
    graph = csr_matrix((data, indices, indptr), shape=(2+i+j, 2+i+j))
    flow = maximum_flow(graph, 0, graph.shape[0]-1)
    return flow.flow_value == 2*i

让我们在一个由40个平面和100个飞行员组成的示例中,在边缘密度为0.1的图形上比较两种方法的时序:

from scipy.sparse import random
inp = random(40, 100, density=.1, format='csr', dtype=bool)
%timeit reduce_to_max_matching(inp)  # 191 µs ± 3.57 µs per loop
%timeit reduce_to_max_flow(inp)      # 1.29 ms ± 20.1 µs per loop

基于匹配的方法更快,但数量却很少。在更大的问题上,我们将开始看到使用匹配代替的好处。 400架飞机和1000名飞行员:

inp = random(400, 1000, density=.1, format='csr', dtype=bool)
%timeit reduce_to_max_matching(inp)  # 473 µs ± 5.52 µs per loop
%timeit reduce_to_max_flow(inp)      # 68.9 ms ± 555 µs per loop

同样,这种精确的比较依赖于SciPy中特定的预定义求解器的使用以及实现方式,但是如果没有别的,则表明更简单更好。