大集合的第n个或任意组合

时间:2013-02-25 01:43:37

标签: c++ algorithm permutation combinations

说我有[0, ....., 499]的一组数字。目前正在使用C ++ std::next_permutation顺序生成组合。作为参考,我提取的每个元组的大小是3,所以我返回顺序结果,如[0,1,2], [0,1,3], [0,1,4], ... [497,498,499]

现在,我想并行处理它所代表的代码,因此这些组合的顺序生成将不再有效。是否存在用于计算500个数字中的ith 3个组合的现有算法?

我想确保每个线程,无论它获得的循环迭代如何,都可以根据迭代的i计算独立组合。因此,如果我想在线程1中使用i=38的组合,我可以计算[1,2,5],同时将线程2中的i=0计算为[0,1,2]

编辑以下声明无关紧要,我把自己搞砸了

我已经研究过利用因子来从左到右缩小每个元素的算法,但我不能将它们用作500!肯定不适合记忆。有什么建议吗?

3 个答案:

答案 0 :(得分:5)

这是我的镜头:

int k = 527; //The kth combination is calculated
int N=500; //Number of Elements you have
int a=0,b=1,c=2; //a,b,c are the numbers you get out

while(k >= (N-a-1)*(N-a-2)/2){
    k -= (N-a-1)*(N-a-2)/2;
    a++;
}
b= a+1;
while(k >= N-1-b){
    k -= N-1-b;
    b++;
}

c = b+1+k;


cout << "["<<a<<","<<b<<","<<c<<"]"<<endl; //The result

考虑到下一个数字增加之前有多少组合。但它只适用于三个元素。我无法保证这是正确的。如果你将它与你的结果进行比较并给出一些反馈,那就太酷了。

答案 1 :(得分:1)

如果您正在寻找获取词典索引或独特组合排名而不是排列的方法,那么您的问题属于二项式系数。二项式系数处理在K组中选择唯一组合以及总共N个项目的问题。

我在C#中编写了一个类来处理使用二项式系数的常用函数。它执行以下任务:

  1. 以任意N选择K到文件的格式输出所有K索引。 K-index可以用更具描述性的字符串或字母代替。

  2. 将K索引转换为正确的词典索引或排序二项式系数表中条目的等级。这种技术比依赖迭代的旧发布技术快得多。它通过使用Pascal三角形中固有的数学属性来实现这一点,并且与迭代集合相比非常有效。

  3. 将已排序的二项系数表中的索引转换为相应的K索引。我相信它也比旧的迭代解决方案更快。

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

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

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

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

    以下测试代码将遍历每个独特的组合:

    public void Test10Choose5()
    {
       String S;
       int Loop;
       int N = 500;  // Total number of elements in the set.
       int K = 3;  // Total number of elements in each group.
       // Create the bin coeff object required to get all
       // the combos for this N choose K combination.
       BinCoeff<int> BC = new BinCoeff<int>(N, K, false);
       int NumCombos = BinCoeff<int>.GetBinCoeff(N, K);
       // The Kindexes array specifies the indexes for a lexigraphic element.
       int[] KIndexes = new int[K];
       StringBuilder SB = new StringBuilder();
       // Loop thru all the combinations for this N choose K case.
       for (int Combo = 0; Combo < NumCombos; Combo++)
       {
          // Get the k-indexes for this combination.  
          BC.GetKIndexes(Combo, KIndexes);
          // Verify that the Kindexes returned can be used to retrive the
          // rank or lexigraphic order of the KIndexes in the table.
          int Val = BC.GetIndex(true, KIndexes);
          if (Val != Combo)
          {
             S = "Val of " + Val.ToString() + " != Combo Value of " + Combo.ToString();
             Console.WriteLine(S);
          }
          SB.Remove(0, SB.Length);
          for (Loop = 0; Loop < K; Loop++)
          {
             SB.Append(KIndexes[Loop].ToString());
             if (Loop < K - 1)
                SB.Append(" ");
          }
          S = "KIndexes = " + SB.ToString();
          Console.WriteLine(S);
       }
    }
    

    你应该可以很容易地将这个类移植到C ++上。您可能不必移植类的通用部分来实现您的目标。您的500选择3的测试用例产生20,708,500个唯一组合,这将适合4字节int。如果500选择3只是一个示例,你需要选择大于3的组合,那么你将不得不使用long或者固定点int。

答案 2 :(得分:0)

您可以将500个对象中的3个特定选项描述为三(i, j, k),其中i是0到499之间的数字(第一个数字的索引),{{1} }范围从0到498(第二个索引,跳过第一个数字),j范围从0到497(最后一个索引,跳过以前选择的数字)。鉴于此,实际上很容易枚举所有可能的选择:从k开始,递增(0,0,0)直到达到其最大值,然后递增k并重置j到0,依此类推,直到k达到最大值,依此类推,直到j达到自己的最大值;然后递增j并重置ij并继续。

如果这个描述听起来很熟悉,那是因为它与递增基数为10的数字完全相同,除了基数更加有趣,实际上基数从数字到数字变化 。您可以使用此洞察力来实现该概念的非常紧凑的版本:对于0到500 * 499 * 498之间的任何整数k,您可以获得:

n

现在要分片,你可以从0到500 * 499 * 498中取数字,以你想要的任何方式将它们分成子范围,并让每个分片计算其子范围中每个值的排列。

对于需要枚举子集的任何问题,此技巧非常方便。