我有一个列表,其中包含按字典顺序排列的集合{1,2,...,n}中的大小为k的所有子集,例如,集合中的所有2个子集{1,2,3,4是{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)+ ... +(N-X选择K-1)子集。 如果X是第一个数字,则子集中的第二个数字Y至少为X + 1。现在我们可以跳过(N- [X + 1]选择K-2)+(N- [X + 2]选择K-2)+ ... +(NY选择K-2)等等。
在算法的代码中,Skip表示我们跳过的子集数量,last表示我们在子集中考虑的最后一个数字(初始化为0,因为集合以1开头),深度是我们子集的深度,以及K是所有子集的长度。
如果二项式系数计算为O(N)
(如果是预处理的)或O(1)
(如果不是),则此算法的问题是O(N*k)
时运行),实际上可以非常快速地计算一些子集。我试图找到一种方法来缩短时间限制。
只要不使用超过O(N chooke K)
内存(即子集数量),就可以进行任何预处理。
答案 0 :(得分:2)
您想要的答案是组合编号系统。见http://en.wikipedia.org/wiki/Combinatorial_number_system
你可以找到找到大小为k的第n个子集的指令,或者找到大小为k的子集,找到它的索引。