我目前正在研究数学优化问题的算法,并且必须处理以下情况。
在很多情况下,算法需要决定在这种情况下哪个子集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的子集中的元素,而不是需要预先计算的数组。
答案 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两套。
两种方法都需要对值数组进行排序,并使用此数组中的集合的等级作为其枚举值。