【问题标题】:Fast and memory efficient binary matrix creation快速和内存高效的二进制矩阵创建
【发布时间】:2011-08-21 22:28:52
【问题描述】:

我在尝试解决一个比我想象的更难解决的问题时遇到了麻烦。给定一个 NxM 的二进制矩阵,我想在每列只有一个“1”的约束下生成所有可能的解决方案。对于 2x3,这将是:

1 1
0 0
0 0

1 0
0 1
0 0

1 0
0 0
0 1

0 1
1 0
0 0

0 0
1 1
0 0

0 0
1 0
0 1

0 1
0 0
1 0

0 0
0 1
1 0

0 0
0 0
1 1

在头疼之后,我在 C 语言中使用了一个递归算法,但我不太确定它是否正确,因为它显然在很长一段时间内都没有工作,缺少一些组合。更改递归后,至少组合的数量是正确的,对于 N x M,我可以手动检查它似乎是正确的。

但是:我一开始的内存消耗太可怕了

1 1
0 0
0 0

和最右边的列,将1设置为0,将下一行中的0设置为1,如果有任何行。然后我向左移动一列(将我所做的重置到最右边的列,比如考虑进位)并迭代 1 的位置,并且对于每次迭代,我再次向右递归,直到没有列可以迭代它的 1. 这种方法对你来说可能看起来很尴尬(代码对我来说),但我认为它就像在 polyad 系统中计数,其中列是数字,1 的位置给出了数字的值。

也许这只是我递归的坏方法,但目前我必须为每个递归复制当前数组,这当然很糟糕,但我发现的唯一方法是,例如

0 1
1 0
0 0

复制两次生成

0 0
1 1
0 0

0 0
1 0
0 1

彼此独立更容易。但是在递归的同时释放 C 中的内存似乎不符合我的经验水平。我已经用 Java 进行了重写,希望 GC 可以帮助我(不敢相信,但它看起来确实如此)但是再次,分析表明,复制数据当然会占用 99% 的周期。

您对我的问题有什么建议吗?甚至可能有一个名字,而不是考虑现有的算法?你手头有递归的伪代码吗?非常感谢吸烟的大脑!

我什至不确定这是组合问题还是置换问题。

【问题讨论】:

  • 也许我误解了你的问题,但是处理 1 所在的行而不是传递整个矩阵,然后基于此打印输出不是更容易吗?跨度>
  • 另外,我的直觉告诉我递归不是要走的路——子问题完全不依赖于以前的结果,反之亦然,问题是基本迭代,正如你正确地观察到的那样,它相当于在一个包含 N 位数字的 M 进制数系统中计数:这就是迭代!
  • 添加了标签 [sparse-matrix],因为这就是该进程正在创建的内容。
  • 好的,我已经解决了这个问题,但使用的是稀疏矩阵,但方式完全不同。但是我通过计算二进制来创建它们,我知道有多少组合,从零数组开始,我在循环中添加 1。进位处理很容易在一个while循环中完成,瞧。

标签: matrix permutation combinations combinatorics sparse-matrix


【解决方案1】:

好的,只是从我在 cmets 中的想法到你的问题,我认为你的问题可以归结为一些非常简单的迭代,基本上不会使用任何内存:

知道您所做的是简单的计数,我们可以设计一种迭代方法。如果你有,就像你的例子中的一个 2x3 矩阵,它相当于用 2 位数字计算三进制数。这意味着我们可以表示的最大值是 3^2 - 1 = 8。

这意味着我们将从 0 数到 8。现在转换为三进制,就像您将数字手动写入二进制一样。例如,7 中有多少个 3^1? 2!所以矩阵中最左边的 1 的位置是第 2 行(从 0 开始索引)。从我们要转换的数字中减去 3x2 = 6,剩下 1。有多少个 3^0? 1!所以最右边一列的 1 在第 1 行。

从这里我们有足够的信息来打印解决方案,并且通过对从 0 到 M^N - 1 的所有数字进行打印,您就有了所有解决方案!

【讨论】:

    【解决方案2】:

    这很简单,没有任何“聪明”的数学技巧。

    首先,表示稀疏矩阵的一种标准方法称为coordinate list:(index_row, index_column, value),用于非零条目。

    其次,您的设置非常基本:您的值都是 1。[所以,您只需要存储 (ix_row, ix_col)。]

    第三:变得更容易:每列只有一行。因此,对于给定的矩阵,大小为 N x P(行 x 列),我们可以假设有 P 个条目。让我们假设给定矩阵的坐标列表对于 index_column 条目具有 1:P,对于所有条目具有 value = 1。 [IE。 “完整”坐标列表为:(ix_row(1), 1, 1), (ix_row(2), 2, 1), ... , (ix_row(P), P, 1)]。

    因此,在填充坐标列表时,问题是创建所有可能的行索引,这只是从 1:N,P 次采样,替换。对于坐标列表中的每一行,您有 N 个选择。矩阵的数量是 N^P。

    创建所有 (1:N) x (1:N) x ... x (1:N) 向量的列表非常简单:从 (1, 1, 1, ...., 1) 开始并在最后一个元素中向上计数到 (1, 1, 1, ..., N) 并一次移动一步。

    这将生成所有必要的坐标列表来描述所有矩阵。

    【讨论】:

    • 谢谢,但我仍然不确定我是否得到它,它确实是我需要的。在我看来,在(向左)移动之后,右侧的所有元素都将固定为 N,失去了很多可能性。还是我弄错了?嗯,也许你的意思是有 (1, 1, ..., 1) 的 P (?) 个向量?
    • 它不固定在 N:你重新开始。例如。对于 N=3, P=2: (1,1), (1,2),(1,3),(2,1),(2,2),(2,3),(3,1) ,(3,2),(3,3)。它很像二进制或十进制计数(如里程表),并且可以在 P 深度递归中实现。
    • 好吧,不知何故,我明白你的方法无需递归即可工作。就我递归的单个向量而言,递归内存是否有效?我想尝试 N 和 P 的值,其中 N^P 有点大。
    • 您可以使用或不使用递归。您所需要的只是存储与前一个矩阵相关的 P 长序列(又名“数组”或“向量”)的能力,因为这些可以按顺序生成。我会保持简单并将行索引存储为向量,即给定矩阵的坐标列表中的第一列。它不能真正提高内存效率。在 16GB 的 RAM 中迭代 N = 40 亿 x P = 40 亿仍然是微不足道的。 (4 个字节为 N,40 亿个值......)。你的N和P有那么大吗?
    • (续)顺便说一句,如果你想产生所有这样的矩阵,唯一矩阵的数量是 N^P。对于 N = P = 4B,这将暂时无法完成,无论您创建什么存储或生成机制。 :)
    猜你喜欢
    • 2018-12-24
    • 1970-01-01
    • 1970-01-01
    • 2014-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多