确定符号是否是第i个组合nCr的一部分

时间:2011-05-04 04:55:42

标签: algorithm function combinations combinatorics

更新: 组合和无人值守最终是我所需要的。 以下链接帮助了很多:

http://msdn.microsoft.com/en-us/library/aa289166(v=vs.71).aspx

http://www.codeproject.com/Articles/21335/Combinations-in-C-Part-2

问题
给出N个符号的列表,说{0,1,2,3,4 ...} 和NCr这些组合

例如。 NC3将生成:

0 1 2  
0 1 3  
0 1 4  
...  
...  
1 2 3  
1 2 4  
etc...  

对于第i个组合(i = [1 .. NCr])我想确定一个符号是否是其中的一部分。
Func(N,r,i,s)=真/假或0/1
例如。从上面继续 第一个组合包含0 1 2但不包含3

F(N,3,1,"0") = TRUE  
F(N,3,1,"1") = TRUE  
F(N,3,1,"2") = TRUE  
F(N,3,1,"3") = FALSE  

可能有帮助或相关的当前方法和措施。
与矩阵的关系 对于r = 2,例如。在图4C2中,组合是2D矩阵的上半部分(或下半部分)

    1,2 1,3 1,4  
    ----2,3 2,4  
    --------3,4  

对于r = 3,它是3D矩阵或立方体的一角 对于r = 4它是4D矩阵的“角”,依此类推。

另一种关系
理想情况下,解决方案的形式类似于以下答案: Calculate Combination based on position

长度为r的组合列表中的第n个组合(允许重复),可以计算第i个符号
使用整数除法和余数:

n / r ^ i%r =(0表示第0个符号,1表示第1个符号....等)

例如对于3个符号的第6个梳子,第0个第1个和第2个符号是:

i = 0 => 6 / 3^0 % 3 = 0   
i = 1 => 6 / 3^1 % 3 = 2   
i = 2 => 6 / 3^2 % 3 = 0   

第6梳子将是0 2 0

我需要类似的东西,但不允许重复。

感谢您关注此问题:] 凯文。

3 个答案:

答案 0 :(得分:1)

我相信您的问题是unranking combinations或子集的问题。

我将从包Combinatorica向您提供Mathematica中的实现,但上面的Google链接可能是一个更好的起点,除非您熟悉语义。

UnrankKSubset::usage = "UnrankKSubset[m, k, l] gives the mth k-subset of set l, listed in lexicographic order."

UnrankKSubset[m_Integer, 1, s_List] := {s[[m + 1]]}
UnrankKSubset[0, k_Integer, s_List] := Take[s, k]
UnrankKSubset[m_Integer, k_Integer, s_List] := 
       Block[{i = 1, n = Length[s], x1, u, $RecursionLimit = Infinity}, 
             u = Binomial[n, k]; 
             While[Binomial[i, k] < u - m, i++]; 
             x1 = n - (i - 1); 
             Prepend[UnrankKSubset[m - u + Binomial[i, k], k-1, Drop[s, x1]], s[[x1]]]
       ]

用法如下:

UnrankKSubset[5, 3, {0, 1, 2, 3, 4}]
   {0, 3, 4}

产生集合{0,1,2,3,4}的第6个(从0开始索引)长度为3的组合。

答案 1 :(得分:1)

这个问题有一个非常有效的算法,它也包含在最近发表的文章中:
Knuth,计算机程序设计的艺术,第4A卷(第7.2.1.3节)。 / p>

由于您不关心生成组合的顺序,因此我们使用组合的词典顺序,其中每个组合以降序顺序列出。因此,对于r = 3,3个符号的前11个组合将是:210,310,320,321,410,420,421,430,431,432,510。这种排序的优点是枚举独立于N;实际上它是对来自{0,1,2,...}的3个符号的所有组合的枚举。

有一种标准方法可以直接生成 i 组合给定i,以便测试符号s是否是的一部分我是组合,你可以简单地生成并检查。

方法

r符号的多少组合以特定符号s开头?好吧,其余的r-1位置必须来自s符号0,1,2,...,s-1,所以它是(s选择r-1),其中(s选择r-1)或C(s,r) -1)是二项式系数,表示从s对象中选择r-1个对象的方式的数量。对于所有s都是如此, i 组合的第一个符号是最小的s,这样

  

Σ k = 0 s (k选择r-1)≥i。

一旦你知道第一个符号,问题就会减少到找到(i - Σ k = 0 s-1 (k选择r-1)) - th r-1符号的组合,我们已经减去那些以小于s的符号开头的组合。

代码

Python代码(您可以更有效地编写C(n,r),但这对我们来说足够快了):

#!/usr/bin/env python

tC = {}
def C(n,r):
    if tC.has_key((n,r)): return tC[(n,r)]
    if r>n-r: r=n-r
    if r<0: return 0
    if r==0: return 1
    tC[(n,r)] = C(n-1,r) + C(n-1,r-1)
    return tC[(n,r)]

def combination(r, k):
    '''Finds the kth combination of r letters.'''
    if r==0: return []
    sum = 0
    s = 0
    while True:
        if sum + C(s,r-1) < k:
            sum += C(s,r-1)
            s += 1
        else:
            return [s] + combination(r-1, k-sum)

def Func(N, r, i, s): return s in combination(r, i)

for i in range(1, 20): print combination(3, i)
print combination(500, 10000000000000000000000000000000000000000000000000000000000000000)

注意这是多快:它在不到0.5秒的时间内找到500字母的100000000000000000000000000000000000000000000000000000000000000000000th组合(以542开头)。

答案 2 :(得分:0)

我编写了一个类来处理使用二项式系数的常用函数,这是您的问题所处的问题类型。它执行以下任务:

  1. 以任意N选择K到文件的格式输出所有K索引。 K索引可以用更具描述性的字符串或字母代替。这种方法使解决这类问题变得非常简单。

  2. 将K索引转换为已排序二项系数表中条目的正确索引。这种技术比依赖迭代的旧发布技术快得多。它通过使用Pascal三角形中固有的数学属性来实现。我的论文谈到了这一点。我相信我是第一个发现和发布这种技术的人,但我可能错了。

  3. 将已排序的二项系数表中的索引转换为相应的K索引。

  4. 使用Mark Dominus方法计算二项式系数,这样就不太可能溢出并使用更大的数字。

  5. 该类是用.NET C#编写的,它提供了一种通过使用通用列表来管理与问题相关的对象(如果有)的方法。此类的构造函数采用名为InitTable的bool值,当为true时,将创建一个通用列表来保存要管理的对象。如果此值为false,则不会创建表。不需要创建表来执行上述4种方法。提供访问者方法来访问该表。

  6. 有一个关联的测试类,它显示了如何使用该类及其方法。它已经过2个案例的广泛测试,并且没有已知的错误。

  7. 要阅读此课程并下载代码,请参阅Tablizing The Binomial Coeffieicent

    此类可以轻松应用于您的问题。如果您具有二项式系数表的等级(或索引),则只需调用返回数组中K索引的类方法。然后,遍历返回的数组以查看是否有任何K-index值与您拥有的值匹配。很直接......