【问题标题】:a non recursive approach to the problem of generating combinations at fault一种非递归方法来生成故障组合问题
【发布时间】:2010-05-01 00:09:06
【问题描述】:

我想要一种非递归方法来解决生成某些字符或数字组合的问题。

因此,给定数字 n 的子集 k,生成所有可能的组合 n!/k!(n-k)!

递归方法会给出一个组合,给定前一个组合。

非递归方法将生成循环索引 i 的给定值的组合。

我用这段代码解决了这个问题:

用 n = 4 和 k = 3 测试,它可以工作,但如果我将 k 更改为 > 3 的数字,它就不起作用。

是不是因为 (n-k)!如果 n = 4 且 k = 3 为 1。如果 k > 3 它会大于 1?

谢谢。

int facto(int x);

int len,fact,rem=0,pos=0;
int str[7];
int avail[7];


   str[0] = 1;
   str[1] = 2;
   str[2] = 3;
   str[3] = 4;
   str[4] = 5;
   str[5] = 6; 
   str[6] = 7;




  int tot=facto(n) / facto(n-k) / facto(k);




for (int i=0;i<tot;i++)
{


       avail[0]=1;
       avail[1]=2;
       avail[2]=3;
       avail[3]=4;
       avail[4]=5; 
       avail[5]=6;
avail[6]=7;



    rem = facto(i+1)-1;
    cout<<rem+1<<". ";
    for(int j=len;j>0;j--)
    {
        int div = facto(j); 
        pos = rem / div; 
        rem = rem % div; 
        cout<<avail[pos]<<" "; 
        avail[pos]=avail[j];

    }
    cout<<endl;
}

int facto(int x)
{
    int fact=1;
    while(x>0) fact*=x--;
    return fact;
}

【问题讨论】:

标签: c++ c combinatorics non-recursive


【解决方案1】:

Err.. 为什么不使用std::next_permutation?它完全符合您的要求,并且不需要您自己编写(以及调试和维护)。

【讨论】:

  • 好吧,这不是我正在寻找的排列,而是我正在寻找的组合,因此例如在 1 2 3 4 中,4 中的 3 将是 1 2 3 1 2 4 1 3 4 2 3 4
  • @mark:您可以使用长度为 n 的二进制排列作为过滤器。
  • @Vicente Botet Escriba:我误解了“组合”一词的含义,但他显示的示例输出是他应该编辑的内容。
【解决方案2】:

这大约是可以计算的速度 - 实际的组合功能是使用两行代码完成的。 但是,这并不是最直观容易理解的!
这项工作是通过实现格雷码序列来完成的。

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <stdint.h>
using namespace std;

//'Combinations' over a set of n objects with k bins, eg n=3,k=2 = 3

//The combination function.
//It takes a combination and returns the next combination.
//It uses GCC's '__builtin_ctzll' which returns the number of
//trailing 0-bits in v, starting at the least significant bit position.
uint64_t combination(uint64_t v) {
    uint64_t t = v | (v - 1ULL); // t gets v's least significant 0 bits set to 1
    return (t + 1ULL) | (((~t & -~t) - 1ULL) >> (__builtin_ctzll(v) + 1ULL));
}

//arg 1 is number of bins (n) arg 2 is number of samples (k/r)
int main (int argc, char *argv[]) {
    uint64_t n = min(64ULL,argc > 1ULL ? atoi(argv[1]) : 3ULL); //max bins = 63
    uint64_t k = min( n,argc > 2 ? atoi(argv[2]) : 2ULL);       //max samples = bins.
    uint64_t v = (1ULL << k) - 1;       //start value;
    uint64_t m = n == 64 ? UINT64_MAX: (1ULL << n) - 1ULL;  //size of n is used as a mask.
    string index = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz+*";
    cout << index.substr(0,n) << endl;
    do {
        cout << bitset<64>(v & m).to_string().substr(64ULL-n) << endl;
        v=combination(v);
    } while (v < m);
    return 0;
}

【讨论】:

    【解决方案3】:

    假设您的迭代器是基数为 n 的 k 位数。在 C/C++ 中,您可以将其表示为大小为 k 的 ints 数组,其中每个元素都在从 0n-1 的范围内。

    然后,要从一个位置迭代到下一个位置,您只需增加数字即可。

    这会给你所有的排列。为了获得组合,您必须施加一个附加条件,即数字必须按升序排列。

    例如k = 3, n = 3: 000 001 002 011 012 022 111 112 122 222

    在 C 中实现该约束也很简单,在用于迭代的增量操作中,当有进位时,您必须将它们设置为与最左边的数字更改时相同的值,而不是将最右边的数字设置为零。

    更新:一些代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAXK 100
    
    int
    main(int argc, char *argv[]) {
        int digits[MAXK];
    
        int k = atol(argv[1]);
        int n = atol(argv[2]);
        int i, left;
    
        memset(digits, 0, sizeof(digits));
    
        while(1) {
            for (i = k; i--; ) {
                printf("%d", digits[i]);
                printf((i ? "-" : "\n"));
            }
    
            for (i = k; i--; ) {
                left = ++digits[i];
                if (left < n) {
                    while (++i < k) digits[i] = left;
                    break;
                }
            }
            if (i < 0) break;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-23
      • 2014-08-14
      • 1970-01-01
      • 2021-11-26
      • 1970-01-01
      • 1970-01-01
      • 2019-09-15
      相关资源
      最近更新 更多