4

特定の文字または数字のセットの組み合わせを生成する問題に対する非再帰的なアプローチが必要でした。

したがって、数 n の部分集合 k が与えられた場合、考えられるすべての組み合わせ n!/k!(nk)! を生成します。

再帰的な方法は、前の 1 つの組み合わせが与えられると、組み合わせを提供します。

非再帰的な方法は、ループ インデックスiの指定された値の組み合わせを生成します。

私はこのコードで問題に取り組みました:

n = 4 および k = 3 でテストして動作しますが、k を 3 より大きい数値に変更すると動作しません。

それは(nk)という事実によるものですか?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;
}
4

3 に答える 3

2

これは、計算できる速度とほぼ同じです。実際の組み合わせ関数は、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;
}
于 2016-10-03T12:21:55.660 に答える
2

Err..なぜ使用しないのstd::next_permutationですか?探しているものを正確に実行し、独自のものを作成 (およびデバッグおよび保守) する必要はありません。

于 2010-05-01T00:11:56.810 に答える
0

イテレータは基数 n の k 桁の数字であると考えてください。C/C++ では、すべての要素が からまでintsの範囲にあるサイズ kの配列として表すことができます)。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;
    }
}
于 2011-01-18T22:52:59.677 に答える