【问题标题】:Optimization problem - finding a maximum优化问题 - 找到最大值
【发布时间】:2010-07-21 18:43:03
【问题描述】:

我手头有一个问题,可以简化为这样的:

假设在二维平面 X-Y 中有一堆随机点,其中每个 Y 上可能有多个点,而每个 X 上可能有多个点。

只要选择了一个点 (Xi,Yi),就不能选择 X = Xi OR Y = Yi 的其他点。我们必须选择最大的点数。

【问题讨论】:

  • 您尝试了哪些方法,您在哪里遇到了问题?请出示您的代码。
  • 不,先生,这不是家庭作业。除了尝试用简单的语言重新表述之外,我无法提供更多细节。
  • 糟糕的问题标题。我对图表的了解不够,无法生成更好的图表 - 有什么建议吗?
  • 图形可能会令人困惑,因为在 X-Y 平面中有图形,而其他图形则有顶点和边,两者都适用于此处,具体取决于人们如何看待这个问题。
  • “我们”?对我来说听起来像是家庭作业。

标签: algorithm optimization discrete-mathematics


【解决方案1】:

这可以简化为简单的最大流量问题。如果你有一个点(xi,yi),在图中它应该用从源 S 到点 xi、从 xi 到 yi 以及从 yi 到最后一个节点(汇)T 的路径表示。

注意,如果我们有点 (2, 2) 和 (2, 5),那么从 S 到 x2 的路径仍然只有一条。所有路径(边)的容量为 1。

这个网络中的流量就是答案。

关于一般问题
http://en.wikipedia.org/wiki/Max_flow

更新
我现在没有图形编辑器来可视化问题,但是您可以轻松地手动绘制示例。比方说,点是 (3, 3) (3, 5) (2, 5)

那么边(路径)将是
S -> x2, S -> x3
y3 -> T, y5 -> T
x3 -> y3, x3 -> y5, x2 -> y5

流程:S -> x2 -> y5 -> T 和 S -> x3 -> y3 -> T
从源头到水槽的“水”量是 2,答案也是如此。

还有关于最大流量算法的教程
http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=maxFlow

【讨论】:

  • 不需要图形编辑器。这看起来优雅而简单。我自己尝试了贪婪的启发式方法,正如下面对示例数据集的回复之一所建议的那样,结果并不是最好的。非常感谢大家的回复。
  • 我希望您对我的算法提出意见来解决这个问题。发布了与以前不同的。
  • 只运行最大匹配算法而不是最大流算法可能更快和/或更简单。
  • @Moron 我猜,但在实践中,我从未见过最大匹配和最大流量算法在速度或简单性方面有太大差异。事实上,我所知道的所有最大匹配算法都实施起来有点“过于复杂”。
  • @Nikita:最大匹配除了更快之外还有另一个优势......假设我给点赋予了权重并想要一组具有最大权重。您现在可能需要最大加权匹配,而最大流量可能不起作用。至于“复杂”,即使是最大流量也很复杂!幸运的是,我们可能有两者的库。
【解决方案2】:

这不只是Hungarian algorithm吗?

创建一个 n×n 矩阵,标记顶点为 0,未标记顶点为 1。该算法将选择 n 个顶点,每行和每列一个,从而最小化它们的总和。只需计算所有选择的等于 0 的顶点,您就会得到答案。

from munkres import Munkres

matrix = [[0, 0, 1],
          [0, 1, 1],
          [1, 0, 0]]

m = Munkres()
total = 0
for row, column in m.compute(matrix):
    if matrix[row][column] == 0:
        print '(%i, %i)' % (row, column)
        total += 1

print 'Total: %i' % total

这在 O(n3) 时间内运行,其中 n 是矩阵中的行数。最大流量解在 O(V3) 中运行,其中 V 是顶点数。只要选择的交叉点超过 n 个,它就会运行得更快;事实上,随着所选顶点数量的增加,它的运行速度要快几个数量级。

【讨论】:

  • 可能是,但我不认为它是显而易见的。你将如何构建图表?你确定这是一个二分图吗?
  • @ivlad:我想听听您对我的新解决方案的看法。经过更多的思考,有一个更好的启发式方法。
  • 同意 IVlad,您打算如何将点映射到矩阵?
  • 点已经一个矩阵。在每个选定的交叉点上输入 0,在所有其他交叉点上输入 1。
  • @Chris:那么你并没有比 Nikita 更快地解决它......事实上你已经让它成倍增长了! Nikita 的解决方案在 O(n^3) 中运行(可以做得更好,但只是说明一个上限),其中 n 是 number 点。在退化的情况下,n=2。所以你的算法实际上要糟糕得多。
【解决方案3】:

不同的解决方案。事实证明有很多对称性,答案比我最初想象的要简单得多。您可以做的最大事情数是唯一 X 和唯一 Y 中的最小值,如果您只想要结果,则为 O(NlogN)。

每个其他形状都等效于包含点的矩形,因为从矩形中心拉出多少点无关紧要,顺序无关紧要(如果按以下方式处理)。从现在开始,任何形状都具有一个不那么独特的 X 和一个不那么独特的 Y,就像一个矩形。

所以最优解与连通性无关。选择 any 位于最小维度边缘的点(即,如果 len(unique-Xs)>len(unique-Ys),则选择任何具有最大或最小 X 的点)。它有多少连接无关紧要,只是哪个维度最大,这可以在查看上面创建的排序唯一列表时轻松完成。如果你保留一个 unique-x 和 unique-y 计数器,并在删除列表的该元素中的所有唯一节点时将它们递减,那么每次删除都是 O(1),重新计算长度是 O(1)。所以重复这 N 次最坏的情况是 O(N),最终的复杂度是 O(NlogN)(仅由于排序)。

您可以沿最短边选择任何点,因为:

  • 如果那个边缘只有一个,你最好现在就选择它,否则其他东西会消除它
  • 如果在那个边缘有多个,谁在乎,无论如何你都会用你的选择消除所有这些

基本上,您在每个点最大化“max(uniqX,uniqY)”。

更新:IVlad 发现了一个边缘情况:

如果尺寸相等,则取点数最少的边。即使它们不相等,也请取你要消除的唯一堆栈的顶部或底部,它的分数最少。

例子:

第 1 回合:

  • 积分:(1, 2); (3, 5); (10, 5); (10, 2); (10, 3)
  • 有 3 个唯一的 X:1, 3, 10
  • 有 3 个唯一的 Y:2, 3, 5
  • “边界框”是(1,5),(10,5),(10,2),(1,2)

反应1:

  • 具有最少点的“外边缘”(最外层的 uniqueX 或 uniqueY 点列表)位于左侧。基本上,查看 x=1,x=10 和 y=2,y=5 中的点集。 x=1 的集合是最小的:一个点。选择 x=1 -> (1,2) 的唯一点。
  • 这也消除了(10,2)

第 2 回合:

  • 积分:(3, 5); (10, 5); (10, 3)
  • 有 2 个唯一的 X:3, 10
  • 有 2 个唯一的 Y:3, 5
  • “边界框”是(3,5),(10,5),(10,3),(3,3)

反应2:

  • 边界框的“边缘”最少是底部或左侧。我们遇到了简单的情况 - 4 分意味着所有边缘都是外边缘。消除一个。说(10,3)
  • 这也消除了(10,5)

第 3 回合:

  • 积分:(3, 5)

反应3:

  • 删除(3,5)

【讨论】:

  • 你能在一个例子上运行它吗,我不确定我明白了。此输入:例如(1, 2); (3, 5); (10, 5); (10, 2); (10, 3)
  • @eruciform:这基本上是重新表述的最大匹配(在二分图中)问题。事实上,二分图中的最大匹配可以在 O(V+E) 时间内简化为这个问题。我相信(不完全确定)当前最知名的二分匹配算法比你似乎拥有的 O(VlogV) 更糟糕......所以如果它是正确的,你可能想要发布它:-)
  • @moron:谢谢!这个问题肯定让我很喜欢。我正在考虑在 python 中解决它,并让它使用这个算法和蛮力运行几千个随机点簇,看看蛮力是否会出现更多。这不是证据,但会是强有力的证据。如果我这样做,我会在这里发布。
  • 你能澄清一下第二段中的“形状”是什么吗?谢谢!而第一段提出的贪心方法有一个问题:try points (0, 0) (0, 1) (0, 2) (10, 10), (11, 10) (12, 10)。有 4 个唯一的 X 坐标,与 Y 相同。但答案是 2。
  • 我知道这是旧的,但这个答案是如此优雅、快速且易于理解。与邻接矩阵相比效果很好,可实现最大匹配。这是已知算法的一部分吗?
【解决方案4】:

对于每个点,确定因选择该点而被取消资格的其他点的数量 (N)(即具有相同 X 或 Y 值的点)。然后,按照 N 个不合格点的数量递增的顺序遍历非不合格点。完成后,您将删除最大点数。

【讨论】:

  • 您如何确保提前消除少量取消资格不会导致最终选择的数量略有不同?它肯定会得到一个近似值,但你能确定这是一个真正的最大值而不尝试所有这些吗?
  • 完全正确。在这种情况下,贪心方法不能保证最优性。
  • 这就是为什么你必须做一个完整的树搜索,但你可以修剪很多子树。
【解决方案5】:

XY 平面是一条红鲱鱼。将其表述为一组元素,每个元素都有一组互斥的元素。

然后该算法变为深度优先搜索。在每个级别,对于每个候选节点,计算排除元素的集合,当前排除的元素与候选节点排除的元素的并集。按排除元素最少到最多的顺序尝试候选节点。跟踪迄今为止的最佳解决方案(排除的节点最少)。修剪任何比当前最好的子树更差的子树。

作为以可能错过解决方案为代价的轻微改进,您可以使用布隆过滤器来跟踪排除的集合。

【讨论】:

    【解决方案6】:

    这似乎是一个可以通过dynamic programming 解决的问题。查看最长公共子串的算法,或knapsack problem

    【讨论】:

    • 动态规划肯定不是可行的方案
    【解决方案7】:

    根据IVlad 的推荐,我调查了Hopcroft–Karp algorithm。对于这个问题,它通常比最大流算法和匈牙利算法都要好,通常是显着的。一些比较:

    一般情况

    • 最大流量:O(V3) 其中V是顶点数。
    • 匈牙利语:O(n3) 其中 n 是矩阵中的行数
    • Hopcroft-Karp:O(V √2V),其中 V 是顶点数。

    对于一个 50×50 的矩阵,有 50% 的选定顶点

    • 最大流量:1,2503 = 1,953,125,000
    • 匈牙利语:503 = 125,000
    • Hopcroft-Karp:1,250 √2,500 = 62,500

    对于一个 1000×1000 的矩阵,有 10 个选定的顶点

    • 最大流量:103 = 1,000
    • 匈牙利语:10003 = 1,000,000,000
    • Hopcroft-Karp:10 √20 ≅ 44.7

    匈牙利算法更好的唯一情况是选择的点比例非常高。

    对于一个 100×100 的矩阵,有 90% 的选定顶点

    • 最大流量:9,0003 = 729,000,000,000
    • 匈牙利语:1003 = 1,000,000
    • Hopcroft-Karp:9,000 √18,000 ≅ 1,207,476.7

    Max Flow 算法从来没有更好。

    在实践中也很简单。此代码使用David Eppstein 的实现:

    points = {
        0 : [0, 1],
        1 : [0],
        2 : [1, 2],
    }
    
    selected = bipartiteMatch(points)[0]
    
    for x, y in selected.iteritems():
        print '(%i, %i)' % (x, y)
    
    print 'Total: %i' % len(selected)
    

    【讨论】:

    • 我认为这不对。我相当确定您可以在O(n^3) 的邻接矩阵上实现 edmonds-karp。您的测试归结为邻接列表与邻接矩阵。只是匈牙利算法绝对需要矩阵,但其他的并不绝对需要邻接表(虽然我个人从未见过用矩阵实现 hopcroft-karp,但我很确定它可以)
    • 原始问题中没有矩阵,顺便说一句:只有点。如果您从输入创建人为的大矩阵,则其大小可能与输入的大小没有有意义的相关。当然,除非您创建一个矩阵,其中仅包含那些存在点的“行”和“列”,在这种情况下,它是一个 mxn 矩阵,其中 m≤V,n≤V。
    猜你喜欢
    • 2021-04-07
    • 1970-01-01
    • 2016-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多