【问题标题】:Most effective way to find connected cells in matrix在矩阵中查找连接单元的最有效方法
【发布时间】:2016-05-10 12:04:00
【问题描述】:

我有一个包含数值的矩阵(最大大小为 100 x 100),需要在那里找到最有效(最小单元数)的一组 连接 单元格,总和为请求的值。该矩阵也可以具有负单元(矩阵的内壁),该算法应该能够四处走动。在查找连接的单元格时,我不需要考虑对角线单元格(仅上、下、左、右)。

例如我们有以下矩阵:

1 4 4

1 1 2

4 -1 1

如果我们正在寻找总和为的单元格集:

  • 9 正确的结果是:4(0x2), 4(0x1), 1(0x0 or 1x1)
  • 3 正确的结果是:2(1x2), 1(1x1 or 2x2)
  • 5 正确的结果是:4(0x1), 1(0x0 or 1x1)

当矩阵变得相对较大(如 50x50)时,递归会非常缓慢地找到这些连接单元格的最有效方法是什么?或者这些数据的矩阵表示不是这项任务的最佳方式?

谢谢

【问题讨论】:

  • 矩阵中的值有多大?您正在寻找的结果有多大?
  • 这看起来像是一个更难的Subset sum 问题,这意味着如果不对数据进行一些额外的假设,您将遇到(性能)问题。我的第一种方法是:获取所有唯一值;循环这些,计算所有connected_components并使用一些子集和算法(非常非正式的描述;假设:n_unique
  • @Sorin 值相对较小。大多数情况下,单元格值最多为 4,很少会达到 16,而且我认为我们要寻找的结果永远不会大于 20。
  • 在示例 A、C、D、E 和 F 中,-1 项将矩阵分隔为多个位置的连通分量。如果您不允许选择负数(即它们表示您无法选择的方格),那么您可以轻松发现这些组件并独立解决每个组件,从而节省大量成本时间。

标签: arrays algorithm matrix


【解决方案1】:

NP-硬度

正如 sascha 在 cmets 中所建议的,这个问题是 NP-hard,因此不太可能存在有效的(多项式时间)解决方案。这是Subset Sum 问题的一个简化,其中给定了一个目标值 m 和一个包含 n 个数字的多重集,并被询问是否有可能找到这些 n 个数字的子多重集,它们的总和为 m:

  1. 创建一个矩阵行,其中包含子集和问题的所有 n 个值,顺序不限。
  2. 在其下方创建第二个矩阵行,在每个单元格中包含值 0。
  3. 使用 m 作为目标值,在结果矩阵上解决您的问题。

现在任何解决方案都可以选取整行 0 值,然后选择顶行中的 任何子集 单元格,因为它们都连接到第二行。所以如果子集和问题有解,那么你的问题也有对应的解,反之亦然。

子集和是一个 NP-hard 问题,上面显示了一种解决任意子集和问题的方法,方法是将其转换为问题的实例,然后解决该问题,因此您的问题也必须是 NP-hard .

如果只能选择正数

我在上面假设零值可以出现在矩阵中(并且可以选择)。如果OTOH只能选择正数,则可以调整减少:

对于子集和问题,让 z 比所有 n 个输入数字的绝对值之和大一。代替单行 0 值,制作一行 z 值;而不是以m为目标,而是以m+nz。显然,如果 SS 问题有解决方案,那么我们可以通过选择 SS 解决方案选择的顶行中的特定数字以及底行中 z 值的所有 n 个副本来解决我们的问题.我现在将展示另一个方向也有效:也就是说,如果我们可以在您的问题的构造实例中获得 m+nz 的目标,那么它必须使用底行中 z 值的所有 n 个副本,留下正好顶行一共m,对应原始SS实例的一个解。

假设我们对构造的实例有一个解决方案,它击中了 m+nz 的目标。那么底行中的每个单元格都必须是该解决方案的一部分,因为即使一个这样的单元格不是,那么即使我们包含 顶行中的每个元素,因为(按设计)z > sum(顶行中的所有元素)。 (我假设 m 在这里是正数。)

【讨论】:

  • 是的,只能选择正数(0 - 定义占位符,-1 - 矩阵墙)。还有一个值(-2 - 以前使用单元格时标记),但它实际上与 -1 具有相同的目的。
【解决方案2】:

为了尽量减少递归次数,我会尝试这种方法,它会查找从短到长加起来的单元格序列,以便您首先找到最短的解决方案:

给定一个目标,例如10,以及一个矩阵,例如

[1, 0, 1, 2, 3]
[6, 1, 4, 0, 2]
[1, 2, 4,-2, 3]
[1,-1, 2, 3, 1]
[0, 2, 3, 0, 1]
  • 计算矩阵中每个正值的出现次数:
1: 6, 2: 5, 3: 4, 4: 2, 5: 0, 6: 1
  • 使用可用值查找目标的所有分区:
6,4  
6,3,1
6,2,2
6,2,1,1
6,1,1,1,1
4,4,2
4,4,1,1
4,3,3
4,3,2,1
4,3,1,1,1
4,2,2,2
4,2,2,1,1
4,2,1,1,1,1
4,1,1,1,1,1,1
3,3,3,1
3,3,2,2
3,3,2,1,1
3,3,1,1,1,1
3,2,2,2,1
3,2,2,1,1,1
3,2,1,1,1,1,1
2,2,2,2,2
2,2,2,2,1,1
2,2,2,1,1,1,1
2,2,1,1,1,1,1,1

(由于target很小,partition的数量应该不会很大,如果target是20,最大partition数只有627)

  • 将分区从短到长排序:
6,4  
6,3,1
6,2,2
4,4,2
4,3,3
6,2,1,1
...
  • 从最短的分区开始,尝试在矩阵中定位:
    • 找到最大数字的位置,将单元格标记为包含在选择中,然后对分区的其余部分进行递归。
    • 尝试使用分区中的相邻数字来增加选择。
    • 如果找不到完整分区,请跳到下一个分区。

如果矩阵中有零可以包含在选择中,那么,每当分区长度增加时(例如,当您从 4,3,3 移动到 6,2,1,1 时,重试用零填充的较短分区(如果足够多的话)可用)。

如果矩阵中的值非常随机,即不是专门设计为难以找到解决方案,那么我认为这种方法应该会大大减少必要的递归次数。 (我没有尝试过编码,所以我不能给出一个好的估计。)

【讨论】:

  • 如果通常有一种方法可以使用少量单元格进行求和,我认为这肯定会有所帮助,因为它确实是避免枚举至少一些较长序列的唯一方法。 (顺便说一句,您为每个分区依次解决的子问题是 NP-hard Graph Motif 问题,因此没有理由期望它在最坏的情况下会很快。)
猜你喜欢
  • 1970-01-01
  • 2011-10-14
  • 1970-01-01
  • 2016-04-04
  • 1970-01-01
  • 2021-11-27
  • 2022-11-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多