用c ++生成组合

时间:2012-02-24 12:12:52

标签: c++ algorithm combinations

我一直在搜索使用c ++生成组合的源代码。我找到了一些高级代码,但这仅适用于特定数量的预定义数据。任何人都可以给我一些提示,或者也许是一些想法来产生组合。例如,假设集合S = {1,2,3,....,n},我们选择r = 2。输入将是nr。在这种情况下,程序将生成长度为2的数组,如5 2输出1 2,1 3等。我很难构建算法。我花了一个月的时间思考这个问题。

15 个答案:

答案 0 :(得分:105)

使用std::next_permutation的简单方法:

#include <iostream>
#include <algorithm>
#include <vector>

int main() {
    int n, r;
    std::cin >> n;
    std::cin >> r;

    std::vector<bool> v(n);
    std::fill(v.end() - r, v.end(), true);

    do {
        for (int i = 0; i < n; ++i) {
            if (v[i]) {
                std::cout << (i + 1) << " ";
            }
        }
        std::cout << "\n";
    } while (std::next_permutation(v.begin(), v.end()));
    return 0;
}

或略微变化,以更容易遵循的顺序输出结果:

#include <iostream>
#include <algorithm>
#include <vector>

int main() {
   int n, r;
   std::cin >> n;
   std::cin >> r;

   std::vector<bool> v(n);
   std::fill(v.begin(), v.begin() + r, true);

   do {
       for (int i = 0; i < n; ++i) {
           if (v[i]) {
               std::cout << (i + 1) << " ";
           }
       }
       std::cout << "\n";
   } while (std::prev_permutation(v.begin(), v.end()));
   return 0;
}

一点解释:

它的工作原理是创建一个“选择数组”(v),我们放置r个选择器,然后我们创建这些选择器的所有排列,并打印相应的set成员(如果在在v的当前排列中。希望这会有所帮助。

答案 1 :(得分:6)

如果您注意到每个级别 r ,您可以选择一个从1到 n 的数字。

在C ++中,我们需要“手动”保持产生结果的调用之间的状态(组合):因此,我们构建一个在构造时初始化状态的类,并且有一个成员在每次调用时返回组合而有解决方案:例如

#include <iostream>
#include <iterator>
#include <vector>
#include <cstdlib>

using namespace std;

struct combinations
{
    typedef vector<int> combination_t;

    // initialize status
   combinations(int N, int R) :
       completed(N < 1 || R > N),
       generated(0),
       N(N), R(R)
   {
       for (int c = 1; c <= R; ++c)
           curr.push_back(c);
   }

   // true while there are more solutions
   bool completed;

   // count how many generated
   int generated;

   // get current and compute next combination
   combination_t next()
   {
       combination_t ret = curr;

       // find what to increment
       completed = true;
       for (int i = R - 1; i >= 0; --i)
           if (curr[i] < N - R + i + 1)
           {
               int j = curr[i] + 1;
               while (i <= R-1)
                   curr[i++] = j++;
               completed = false;
               ++generated;
               break;
           }

       return ret;
   }

private:

   int N, R;
   combination_t curr;
};

int main(int argc, char **argv)
{
    int N = argc >= 2 ? atoi(argv[1]) : 5;
    int R = argc >= 3 ? atoi(argv[2]) : 2;
    combinations cs(N, R);
    while (!cs.completed)
    {
        combinations::combination_t c = cs.next();
        copy(c.begin(), c.end(), ostream_iterator<int>(cout, ","));
        cout << endl;
    }
    return cs.generated;
}

测试输出:

1,2,
1,3,
1,4,
1,5,
2,3,
2,4,
2,5,
3,4,
3,5,
4,5,

答案 2 :(得分:5)

          #include<iostream>
          using namespace std;

          for(int i=1;i<=5;i++)
             for (int j=2;j<=5;j++) 
                if (i!=j)
                  cout<<i<<","<<j<<","<<endl;

           //or instead of cout... you can put them in a matrix n x 2 and use the solution

答案 3 :(得分:3)

基于algorithms from Prof. Nathan Wodarz的简单而有效的解决方案:

// n choose r combination
#include <vector>
#include <iostream>
#include <algorithm>

struct c_unique {
  int current;
  c_unique() {current=0;}
  int operator()() {return ++current;}
} UniqueNumber;

void myfunction (int i) {
  std::cout << i << ' ';
}

int main()
{
    int n=5;
    int r=3;

    std::vector<int> myints(r);
    std::vector<int>::iterator first = myints.begin(), last = myints.end();

    std::generate(first, last, UniqueNumber);

    std::for_each(first, last, myfunction);
    std::cout << std::endl;

    while((*first) != n-r+1){
        std::vector<int>::iterator mt = last;

        while (*(--mt) == n-(last-mt)+1);
        (*mt)++;
        while (++mt != last) *mt = *(mt-1)+1;

        std::for_each(first, last, myfunction);
        std::cout << std::endl;
    }
}

然后输出是:
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5

答案 4 :(得分:2)

我建议你弄清楚如何在纸上自己做,并从中推断伪代码。之后,您只需要决定编码和存储操纵数据的方式。

例如:

For each result item in result array // 0, 1, ... r
    For each item possible // 0, 1, 2, ... n
        if current item does not exist in the result array
            place item in result array
            exit the inner for
        end if
    end for
end for

答案 5 :(得分:2)

您可以使用递归来选择N + 1种组合,然后选择N组合,然后加1。您添加的1必须始终位于N的最后一个之后,因此如果您的N包含最后一个元素,则没有与之关联的N + 1个组合。

也许不是最有效的解决方案,但它应该有效。

基础案例将选择0或1.您可以选择0并获得空集。从空集中,您可以假设迭代器在元素之间工作而不是在它们之间。

答案 6 :(得分:2)

代码类似于生成二进制数字。保留一个额外的数据结构,一个数组perm [],其索引i的值将告诉是否包含第i个数组元素。并且还保留计数变量。每当count ==组合长度时,都会根据perm []打印元素。

#include<stdio.h>

// a[] : given array of chars 
// perm[] : perm[i] is 1 if a[i] is considered, else 0
// index : subscript of perm which is to be 0ed and 1ed
// n     : length of the given input array
// k     : length of the permuted string
void combinate(char a[], int perm[],int index, int n, int k)
{
   static int count = 0;

   if( count == k )
   { 
      for(int i=0; i<n; i++)
        if( perm[i]==1)
          printf("%c",a[i]);
      printf("\n");

    } else if( (n-index)>= (k-count) ){

         perm[index]=1;
         count++;
         combinate(a,perm,index+1,n,k);

         perm[index]=0;
         count--;
         combinate(a,perm,index+1,n,k);

   }
}
int main()
{
   char a[] ={'a','b','c','d'};
   int perm[4] = {0};
   combinate(a,perm,0,4,3);

   return 0;
}

答案 7 :(得分:2)

这是一个递归方法,您可以在任何类型上使用它。你可以迭代组合类的实例(例如,带有所有组合的get()向量,每个组合都是对象的向量。这是用C ++ 11编写的。

//combinations.hpp
#include <vector>

template<typename T> class Combinations {
// Combinations(std::vector<T> s, int m) iterate all Combinations without repetition
// from set s of size m s = {0,1,2,3,4,5} all permuations are: {0, 1, 2}, {0, 1,3}, 
// {0, 1, 4}, {0, 1, 5}, {0, 2, 3}, {0, 2, 4}, {0, 2, 5}, {0, 3, 4}, {0, 3, 5},
// {0, 4, 5}, {1, 2, 3}, {1, 2, 4}, {1, 2, 5}, {1, 3, 4}, {1, 3, 5}, {1, 4, 5}, 
// {2, 3, 4}, {2, 3, 5}, {2, 4, 5}, {3, 4, 5}

public:
    Combinations(std::vector<T> s, int m) : M(m), set(s), partial(std::vector<T>(M))
    {
        N = s.size(); // unsigned long can't be casted to int in initialization

        out = std::vector<std::vector<T>>(comb(N,M), std::vector<T>(M)); // allocate space

        generate(0, N-1, M-1);
    };

    typedef typename std::vector<std::vector<T>>::const_iterator const_iterator;
    typedef typename std::vector<std::vector<T>>::iterator iterator;
    iterator begin() { return out.begin(); }
    iterator end() { return out.end(); }    
    std::vector<std::vector<T>> get() { return out; }

private:
    void generate(int i, int j, int m);
    unsigned long long comb(unsigned long long n, unsigned long long k); // C(n, k) = n! / (n-k)!

    int N;
    int M;
    std::vector<T> set;
    std::vector<T> partial;
    std::vector<std::vector<T>> out;   

    int count (0); 
};

template<typename T> 
void Combinations<T>::generate(int i, int j, int m) {  
    // combination of size m (number of slots) out of set[i..j]
    if (m > 0) { 
        for (int z=i; z<j-m+1; z++) { 
            partial[M-m-1]=set[z]; // add element to permutation
            generate(z+1, j, m-1);
        }
    } else {
        // last position
        for (int z=i; z<j-m+1; z++) { 
            partial[M-m-1] = set[z];
            out[count++] = std::vector<T>(partial); // add to output vector
        }
    }
}

template<typename T> 
unsigned long long
Combinations<T>::comb(unsigned long long n, unsigned long long k) {
    // this is from Knuth vol 3

    if (k > n) {
        return 0;
    }
    unsigned long long r = 1;
    for (unsigned long long d = 1; d <= k; ++d) {
        r *= n--;
        r /= d;
    }
    return r;
}

测试文件:

// test.cpp
// compile with: gcc -O3 -Wall -std=c++11 -lstdc++ -o test test.cpp
#include <iostream>
#include "combinations.hpp"

struct Bla{
    float x, y, z;
};

int main() {

    std::vector<int> s{0,1,2,3,4,5};
    std::vector<Bla> ss{{1, .4, 5.0},{2, .7, 5.0},{3, .1, 2.0},{4, .66, 99.0}};

    Combinations<int> c(s,3);
    // iterate over all combinations
    for (auto x : c) { for (auto ii : x) std::cout << ii << ", "; std::cout << "\n"; }

    // or get a vector back
    std::vector<std::vector<int>> z = c.get();  

    std::cout << "\n\n";

    Combinations<Bla> cc(ss, 2);
    // combinations of arbitrary objects
    for (auto x : cc) { for (auto b : x) std::cout << "(" << b.x << ", " << b.y << ", " << b.z << "), "; std::cout << "\n"; }    

}

输出是:

0,1,2, 0,1,3, 0,1,4, 0,1,5, 0,2,3, 0,2,4, 0,2,5, 0,3,4, 0,3,5, 0,4,5, 1,2,3, 1,2,4, 1,2,5, 1,3,4, 1,3,5, 1,4,5, 2,3,4, 2,3,5, 2,4,5, 3,4,5,

(1,0.4,5),(2,0.7,5), (1,0.4,5),(3,0.1,2), (1,0.4,5),(4,0.66,99), (2,0.7,5),(3,0.1,2), (2,0.7,5),(4,0.66,99), (3,0.1,2),(4,0.66,99),

答案 8 :(得分:0)

void print(int *a, int* s, int ls)
{
    for(int i = 0; i < ls; i++)
    {
        cout << a[s[i]] << " ";
    }
    cout << endl;
}    
void PrintCombinations(int *a, int l, int k, int *s, int ls, int sp)
{
   if(k == 0)
   {
       print(a,s,ls);
       return;
   }
   for(int i = sp; i < l; i++)
   {

      s[k-1] = i;
      PrintCombinations(a,l,k-1,s,ls,i+1);
      s[k-1] = -1;

   }
}

int main()
{
 int e[] = {1,2,3,4,5,6,7,8,9};
 int s[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
 PrintCombinations(e,9,6,s,6,0);
}

答案 9 :(得分:0)

对于(n选择r)的特殊情况,其中r是固定常量,我们可以编写r个嵌套循环来得出这种情况。有时当r不固定时,我们可能会有另一个特殊情况(n选择n-r),其中r又是一个固定常数。这个想法是每个这样的组合是(n选择r)的组合的逆。所以我们可以再次使用r嵌套循环,但是反转解决方案:

// example 1: choose each 2 from given vector and apply 'doSomething'
void doOnCombinationsOfTwo(const std::vector<T> vector) {
   for (int i1 = 0; i1 < vector.size() - 1; i1++) {
      for (int i2 = i1 + 1; i2 < vector.size(); i2++) {
         doSomething( { vector[i1], vector[i2] });
      }
   }
}


// example 2: choose each n-2 from given vector and apply 'doSomethingElse'
void doOnCombinationsOfNMinusTwo(const std::vector<T> vector) {
   std::vector<T> combination(vector.size() - 2); // let's reuse our combination vector 
   for (int i1 = 0; i1 < vector.size() - 1; i1++) {
      for (int i2 = i1 + 1; i2 < vector.size(); i2++) {
         auto combinationEntry = combination.begin(); // use iterator to fill combination
         for (int i = 0; i < vector.size(); i++) {
            if (i != i1 && i != i2) {
               *combinationEntry++ = i;
            }
         }
         doSomethingElse(combinationVector);
      }
   }
}

答案 10 :(得分:0)

vector<list<int>> generate(int N, int K, int& count) {

    vector<list<int>> output;

    if(K == 1) {
        count = N;
        for(int i = 1; i <= N; i++) {
            list<int> l = {i};
            output.push_back(l);
        }
    } else {
        count = 0;
        int n;
        vector<list<int>> l = generate(N, K - 1, n);
        for(auto iter = l.begin(); iter != l.end(); iter++) {
            int last = iter->back();
            for (int i = last + 1; i <= N; ++i) {
                list<int> value = *iter;
                value.push_back(i);
                output.push_back(value);
                count++;
            }
        }
    }

    return output;
}

答案 11 :(得分:0)

这是我的尝试:

功能(可复制/粘贴),无需任何依赖

 template<class _Tnumber, class _Titerator >
      bool next_combination
       (
            _Titerator const& _First
          , _Titerator const& _Last
          , _Tnumber const& _Max //!< Upper bound. Not reachable
       )
       {
        _Titerator _Current = _First;
         if( _Current  == _Last )
          {
           return false;
          }
         *_Current += 1;
         if( *_Current < _Max )
          {
           return true;
          }
        _Titerator _Next = _Current + 1;
         if( _Next == _Last )
          {
           return false;
          }
         if( false == next_combination( _Next, _Last, _Max - 1 ) )
          {
           return false;
          }
         *_Current = *_Next + 1; 
         return *_Current < _Max;
        }

测试:

vector<int> vec({3,2,1}); // In descending order and different
do
 {
  copy( vec.begin(), vec.end(), ostream_iterator<int>(cout, ", " ) ); cout << endl;
 }while( ::math::algorithm::next_combination( vec.begin(), vec.end(), 6 ) );

并输出:

3, 2, 1,
4, 2, 1,
5, 2, 1,
4, 3, 1,
5, 3, 1,
5, 4, 1,
4, 3, 2,
5, 3, 2,
5, 4, 2,
5, 4, 3,

答案 12 :(得分:0)

这似乎可读,也可以用于std::vectorstd::liststd::deque甚至是静态声明的int intArray[]

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <list>
#include <set>
#include <iterator>

template<typename InputIt, typename T>
bool nextCombination(InputIt begin,
                     InputIt end,
                     T toElement) {
    /*
        Given sequence: 1 2 3 4 5
        Final sequence: 6 7 8 9 10

        -- Formally --
        Given sequence: 1 2 ... k-1 k
        Final sequence: (n-k+1) (n-k+2) ... (n-1) n

        lengthOfSubsequence = positionOf(5) - positionOf(1) = 5
        
        We look for an element that satisfies:
            seqeunce[pos] < n - k + pos

    */

    const auto lengthOfSubsequence = std::distance(begin, end);

    auto viewed_element_it = std::make_reverse_iterator(end);
    auto reversed_begin = std::make_reverse_iterator(begin);

    /*Looking for this element here*/

    while ((viewed_element_it != reversed_begin) && 
           (*viewed_element_it >= toElement -
                                  lengthOfSubsequence + 
                                  std::distance(viewed_element_it, reversed_begin))) {
        //std::distance shows position of element in subsequence here
        viewed_element_it++;
    }

    if (viewed_element_it == reversed_begin)
        return false;

    auto it = std::prev(viewed_element_it.base());

    /*
        Increment the found element. 
    The rest following elements we set as seqeunce[pos] = seqeunce[pos-1] + 1
    */

    std::iota(it, end, *it + 1);

    return true;
}

int main()
{
    std::list<int> vec = { 1, 2, 3 };

    do {
        std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(std::cout, " "));
        std::cout << std::endl;
    } while (nextCombination(vec.begin(), vec.end(), 10));
}

答案 13 :(得分:0)

以下是C ++中的一种算法,不使用,既不使用STL,也不使用递归或条件嵌套循环。这样更快,它不执行任何元素交换,并且不给递归带来负担,并且可以通过替换mallloc()free()和{{1}轻松地移植到ANSIC。 }分别用于printf()newdelete

如果要显示的元素从1开始,则更改std::cout函数。
即:OutputArray()代替cout << ka[i]+1...

请注意,我使用cout << ka[i]...而不是K

r

组合:超出“ 7选择4”。 Combinations of "7 Choose 4"

答案 14 :(得分:-2)

如果r很小,你可以使用for循环,这里r = 2,所以两个for循环:

unsigned int i, j, max=0;
for(i=1; i<=n; i++){
    for(j=i+1; j<=n; j++){
            int ans = (i & j);
            cout << i << " " << j << endl;     
     }
}