有效枚举子集

时间:2013-03-26 23:31:18

标签: algorithm optimization set

我目前正在研究数学优化问题的算法,并且必须处理以下情况。

在很多情况下,算法需要决定在这种情况下哪个子集S⊂N是最佳的 N = {0,1,2,...,126,127}
| S | ∈{0,1,2,3,4,5}(子集的大小介于0和5之间)

这给出了可能的子集总数265.982.833。 (binom(128,5)+ binom(128,4)+ ... + binom(128,0))

如果我预先计算所有可能的子集并将它们存储在一个数组中,那么这个数组将有265.982.833个条目和大约1,27 GB的内存占用,没有任何优化和子集作为字节数组的原始存储。

在这种情况下,当算法需要知道哪些元素在索引i的特定子集中时,只需要进行表查找。但是,巨大的内存要求是不可接受的。

所以我的问题基本上是,如果有人能够想到一个算法来有效地计算基于索引i的子集中的元素,而不是需要预先计算的数组。


编辑包括样本:
lookupTable [0] = {}
lookupTable [1] = {0}
...
lookupTable [127] = {126}
lookupTable [128] = {127}
lookupTable [129] = {0,1}
lookupTable [130] = {0,2}
...
lookupTable [265982832] = {123,124,125,126,127}

2 个答案:

答案 0 :(得分:5)

从前一个子集构造每个子集是很简单的。将子集表示为128位数字也很简单(尽管显然大多数值都不会映射到合格的子集上,我不知道问题中128的值是真实的还是仅仅是一个例子。)这是当然是我会使用的第一种方法;如果它工作,它都是O(1)并且存储成本不是极端的(对于索引而不是4个16字节)。

如果你真的想存储子集的简明索引,我会使用下面的递归,其中S(n,k)表示大小≤k的所有子集(或子集的计数),值<&lt; N:

s(n,0) = { {} }
s(n,k) = (s(n-1,k-1) ⊙ {n}) ⋃ s(n-1,k) if n ≥ k > 0
s(n,k) = {} if n < k

其中,运算符P ⊙ S表示“将S添加到P的每个元素”(因此结果与P的大小完全相同)。因此,作为计数算法,我们得到:

S(n,0) = 1
S(n,k) = S(n-1,k-1) + S(n-1,k) if n ≥ k > 0
S(n,k) = 0 if n < k

第二次递归可以重新表达为:

S(n,k) = Σni=kS(i-1,k-1)
(看起来jsMath会更好看,grrr。)

这是另一种说法,我们将按最大元素按顺序生成集合,因此我们从集合{0...k-1}开始,然后以{k}作为最大元素的所有集合开始,然后所有{k+1},等等。在每组集合中,我们递归查找(k-1)大小的集合,再次从最小值开始,并以比我们刚刚选择的最大值小一个的方式工作。

因此,我们可以通过从S(n,k)S(i-1, k-1)连续减去i的{​​{1}},找出k中索引的索引集中的最大值直到结果是负面的;然后我们将n添加到结果集中;将{i}减少1并重复k现在设置为n

如果我们预先计算i-1的相关表,其中大约有640个有效组合,我们可以使用二进制搜索而不是迭代来在每一步找到S(n,k),因此计算需要时间i,这不是很糟糕。

答案 1 :(得分:0)

天真的实现将使用位图(bitX == 1意味着项X存在于集合中)另一个约束是掩码中不超过5位可以是1。并且它需要128位来表示一组。

使用primenumbers的乘积来表示集合只需要每组64位(124 ... 128'的素数是{124:691,125:701,126:709,127:719,128 :727},它们的产品将适合64位IICC。它仍会浪费一些比特(如OQ所示,一个好的枚举将适合32位),但很容易检查“重叠”的常见项目通过他们的GCD两套。

两种方法都需要对值数组进行排序,并使用此数组中的集合的等级作为其枚举值。