在o(1)中生成集合的所有组合

时间:2017-02-26 15:40:39

标签: algorithm generator combinations pseudocode gray-code

int {1,2,3}集合的组合是:

  

{},{1},{2},{1,2},{3},{1,3},{2,3},{1,2,3}

常见的解决方案是跟踪索引的代表位掩码。

从我的0-7中的数字示例:

  

000,001,010,011,100,101,110,111

该算法允许迭代解决方案, 每次发生器越过每一位并决定是否插入物品, 所以它创建了O(n)运行时的下一个组合。

格雷码:(来自维基百科) 反射二进制代码(RBC),也称为Frank Grey之后的格雷码, 是一个二进制数字系统,其中两个连续值仅相差一位

如果我们只使用灰色数字而得到这个数字范围:

  

000,001,011,010,110,111,101,100

结果的顺序不同:

  

{},{1},{1,2},{2},{2,3},{1,2,3},{1,3},{3}

但在这里我们看到差异只是一个项目。 是否可以使用格雷码范围在O(1)中实现生成器。

我知道如果迭代,返回的列表无论如何都会有O(n)运行时。

1 个答案:

答案 0 :(得分:2)

增量和格雷码解决方案都是摊销的O(1),我相信这样你就可以得到。 “分摊的O(1)”意味着您偶尔会在一次调用中获得更长的时间,但它们非常罕见,k连续调用的总时间为O(k),因此一次调用的平均时间是O(1)。

这是一个比“预期的O(1)”更强的主张,因为摊销的执行时间是在足够的调用中聚合的保证,而如果您非常不幸,预期的执行时间可能会产生意外的总时间。 (实际上,差异通常并不重要,但请参阅DoS针对“预期的O(1)”散列表查找的攻击。)

要了解增量算法为何摊销O(1),请查看每次迭代中翻转的位数。每隔一次迭代(数字以0结尾),恰好翻转一位。每四次迭代,恰好两位被翻转。每第八次迭代,恰好三位被翻转。等等。很容易证明,在k次迭代中,最多总共2k +log2 k位将被翻转。

对于格雷码算法,每次迭代只翻转一位,但必须找到该位。格雷码递增的简单迭代实现是维持当前选择的奇偶校验。由于奇偶校验在每次迭代时都会发生变化,因此无需重新计算;它只是倒置了。然后算法在两个规则之间交替:

  1. 如果奇偶校验是偶数,则翻转低位。

  2. 如果奇偶校验是奇数,则将该位翻转到最低位1位的左侧。

  3. 显然,选项1适用于一半的迭代,它只清楚地检查一位。对于选项2,我们需要确定每次迭代检查的位数。由于每个可能的位组合恰好出现一次,所以尾随零的数量遵循与增量的整数序列相同的频率:每隔一次,没有尾随零,每四次有一次,每八次有两次,等等。

    因此,最后,我们检查两种解决方案中相同数量的低阶位。但是,格雷码解决方案在每次迭代时仅更改集合中的一个元素,而增量解决方案将分摊的平均值更改为2。因此,如果设置修改很昂贵,格雷码算法可能会更好。

    后记

    1. 您可以将格雷码算法直接应用于集合而不通过位字符串,但它的性能将取决于集合实现的一些细节。奇偶校验继续如上所述交替(它也是模2的集合的大小,因此如果你的集合具有o(1)大小的操作,你可以使用它而不是维持状态)。我们假设你有一些o(1)函数将每个可能的set元素映射到它的后继(在Universe中,而不是选择),这可能类似于位映射到set元素的方式。然后两个可能的算法动作是:

      1. 偶校验:如果集合中的最小元素是Universe中的最小元素,则删除该元素;否则加上它。

      2. 奇数奇偶校验:如果集合中最小元素的后继也存在,则将其删除;否则,加上它。

    2. 如果你需要在每次迭代时创建一个新的集合,而不是修改当前的选择集,那么当然你不能做得比O(n)好,因为选择的摊销平均大小是n / 2,创建一个集合所花费的时间不会超过其大小。