解决错误生成组合问题的非递归方法

发布于 2024-08-30 13:59:07 字数 1086 浏览 9 评论 0原文

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

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

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

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

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

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

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

I wanted a non recursive approach to the problem of generating combination of certain set of characters or numbers.

So, given a subset k of numbers n, generate all the possible combination n!/k!(n-k)!

The recursive method would give a combination, given the previous one combination.

A non recursive method would generate a combination of a given value of loop index i.

I approached the problem with this code:

Tested with n = 4 and k = 3, and it works, but if I change k to a number > 3 it does not work.

Is it due to the fact that (n-k)! in case of n = 4 and k = 3 is 1. and if k > 3 it will be more than 1?

Thanks.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一张白纸 2024-09-06 13:59:07

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

Err.. why not use std::next_permutation? It does exactly what you're looking for and doesn't require you to write (and debug and maintain) your own.

客…行舟 2024-09-06 13:59:07

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

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

This is about as fast as it can be calculated - the actual combination function is done using two lines of code.
However, this isn't the most intuitively easy to understand!
The work is done by implementing a Gray code sequence.

#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;
}
单身狗的梦 2024-09-06 13:59:07

假设您的迭代器是以 n 为基数的 k 位数字。在 C/C++ 中,您可以将其表示为大小为 k 的 int 数组,其中每个元素的范围为 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;
    }
}

Consider that your iterator is a number of k digits in base n. In C/C++ you can represent it as an array of ints of size k where every element is in the range from 0 to n-1).

Then, to iterate from one position to the next you only need to increment the number.

That will give you all the permutations. In order to get combinations you have to impose an additional condition that is that digits must be in ascending order.

For instance with k = 3, n = 3: 000 001 002 011 012 022 111 112 122 222

Implementing that constraint in C is also pretty simple, on the increment operation used to iterate, instead of setting the rightmost digits to zero when there is a carry, you have to set them to the same value as the leftmost digit changed.

update: some code:

#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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文