一种非递归方法来解决在故障时产生组合的问题

时间:2010-05-01 00:09:06

标签: c++ c combinatorics non-recursive

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

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

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

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

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

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

是否是因为(n-k)!在n = 4且k = 3的情况下,如果k> 1,则k = 3。 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;
}

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:0)

考虑你的迭代器是基数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;
    }
}

答案 2 :(得分:0)

这与计算速度差不多 - 实际的组合功能是使用两行代码完成的。 但是,这并不是最直观易懂的! 这项工作是通过实现格雷码序列来完成的。

#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;
}