我有一个按字典顺序排列的集合 {1, 2, ... , n} 中所有大小为 k 的子集的列表,例如集合 {1, 2, 3, 4} 中大小为 2 的所有子集是 { 1, 2}, {1, 3}, {1, 4}, {2, 3}, {2, 4}, {3, 4}。其中 {1, 2} 的索引为 0,{1, 3} 为 1,依此类推。
现在,我需要编写一个算法来接收一个子集(假设子集是有序的),并返回它在列表中的索引。
我写了以下算法:
int GetSubsetIndex(List<int> subset, int N)
{
int Skip = 0;
int Last = 0;
int Depth = 1;
int K = subset.Count;
while (Depth <= K)
{
for (int i = Last + 1; i < subset[Depth - 1]; i++)
{
Skip += BinomialCoefficient(N - i, K - Depth);
}
Last = subset[Depth - 1];
Depth++;
}
return Skip;
}
该算法使用子集字典顺序的特殊结构,解释如下:
假设我们有一组大小为 6(N=6)和长度为 3(K=3)的子集,那么我们有 6 个选择 3 个子集。现在,以 1 开头的子集数量为 5 选择 2,以 2 开头的子集数量为 4 选择 2,依此类推...
如果子集中的第一个数字是 X,我们可以跳过 (N-1 选择 K-1) + (N-2 选择 K-1) + ... + (NX 选择 K-1) 个子集。如果 X 是第一个数字,则子集中的第二个数字 Y 至少为 X+1。现在我们可以跳过 (N-[X+1] 选择 K-2) + (N-[X+2] 选择 K-2) + ... + (NY 选择 K-2) 等等。
在算法的代码中,Skip 表示我们跳过的子集的数量,last 表示我们在子集中考虑的最后一个数字(初始化为 0,因为集合以 1 开头),Depth 是我们在子集中的深度,K 是所有子集的长度。
这个算法的问题是,O(N)
如果二项式系数计算是O(1)
(如果它是预处理的)或O(N*k)
(如果不是),那么它会随着时间的推移而运行,实际上一些子集可能会很快计算出来。我试图想办法在较短的时间内获得这个索引。
只要您不使用超过O(N chooke K)
内存(即子集的数量),您就可以进行任何您想要的预处理。