powers_中的组合或子集的next_permutation

时间:2010-04-21 18:22:43

标签: c++ permutation combinations powerset

是否有一些等效的库或函数可以为我提供像next_permutation这样的一组值的下一个组合?

6 个答案:

答案 0 :(得分:10)

组合:来自Mark Nelson关于同一主题的文章,我们有next_combination http://marknelson.us/2002/03/01/next-permutation
排列:从STL我们有std :: next_permutation

 template <typename Iterator>
 inline bool next_combination(const Iterator first, Iterator k, const Iterator last)
 {
    if ((first == last) || (first == k) || (last == k))
       return false;
    Iterator itr1 = first;
    Iterator itr2 = last;
    ++itr1;
    if (last == itr1)
       return false;
    itr1 = last;
    --itr1;
    itr1 = k;
    --itr2;
    while (first != itr1)
    {
       if (*--itr1 < *itr2)
       {
          Iterator j = k;
          while (!(*itr1 < *j)) ++j;
          std::iter_swap(itr1,j);
          ++itr1;
          ++j;
          itr2 = k;
          std::rotate(itr1,j,last);
          while (last != j)
          {
             ++j;
             ++itr2;
          }
          std::rotate(k,itr2,last);
          return true;
       }
    }
    std::rotate(first,k,last);
    return false;
 }

答案 1 :(得分:7)

我不知道。基本思想是将元素表示为位数组。例如,你有集合S:

S = {a, b, c}
[i, j, k] // a is the first bit, b is the second bit, c is the third bit

生成S的幂集(只需使用简单的加法生成大小== 3位的所有数字):

000 // {}
001 // {c}
010 // {b}
011 // {b, c}
100 // {a}
101 // {a, c}
110 // {a, b}
111 // {a, b, c}

您所要做的就是找到设置的位,并将它们与您的集合元素相关联。

最后说明,当你想要使用所有元素时,你可以产生一种组合,并且这种组合就是它自己的设置,因为在组合中顺序无关紧要,所以我们肯定在谈论一些元素n 0 <= n <= size(S)

答案 2 :(得分:1)

当我需要这样做时,我已经使用了this library。它有一个非常类似于std::next_permutation的界面,所以如果您以前使用它,它将很容易使用。

答案 3 :(得分:1)

powerset的枚举(即所有大小的所有组合)可以使用二进制增量算法的自适应。

template< class I, class O > // I forward, O bidirectional iterator
O next_subset( I uni_first, I uni_last, // set universe in a range
    O sub_first, O sub_last ) { // current subset in a range
    std::pair< O, I > mis = std::mismatch( sub_first, sub_last, uni_first );
    if ( mis.second == uni_last ) return sub_first; // finished cycle

    O ret;
    if ( mis.first == sub_first ) { // copy elements following mismatch
        std::copy_backward( mis.first, sub_last, ++ (ret = sub_last) ); 
    } else ret = std::copy( mis.first, sub_last, ++ O(sub_first) ); 
    * sub_first = * mis.second; // add first element not yet in result
    return ret; // return end of new subset. (Output range must accommodate.)
}

双向迭代器的要求是不幸的,可以解决。

我打算让它处理相同的元素(multisets),但我需要去睡觉:v(。

用法:

#include <iostream>
#include <vector>
using namespace std;

char const *fruits_a[] = { "apples", "beans", "cherries", "durian" };
vector< string > fruits( fruits_a, fruits_a + sizeof fruits_a/sizeof *fruits_a );

int main() {
    vector< string > sub_fruits( fruits.size() );
    vector< string >::iterator last_fruit = sub_fruits.begin();

    while ( 
        ( last_fruit = next_subset( fruits.begin(), fruits.end(),
                     sub_fruits.begin(), last_fruit ) )
            != sub_fruits.begin() ) {
        cerr << "size " << last_fruit - sub_fruits.begin() << ": ";
        for ( vector<string>::iterator fit = sub_fruits.begin(); fit != last_fruit; ++ fit ) {
            cerr << * fit << " ";
        }
        cerr << endl;
    }
}

编辑:以下是多集的版本。该集合不必排序,但必须将相同的元素组合在一起。

#include <iterator>
#include <algorithm>
#include <functional>

template< class I, class O > // I forward, O bidirectional iterator
I next_subset( I uni_first, I uni_last, // set universe in a range
    O sub_first, O sub_last ) { // current subset in a range
    std::pair< O, I > mis = std::mismatch( sub_first, sub_last, uni_first );
    if ( mis.second == uni_last ) return sub_first; // finished cycle

    typedef std::reverse_iterator<O> RO;
    mis.first = std::find_if( RO(mis.first), RO(sub_first), std::bind1st(
        std::not_equal_to< typename std::iterator_traits<O>::value_type >(),
        * mis.second ) ).base(); // move mis.first before identical grouping

    O ret;
    if ( mis.first != sub_first ) { // copy elements after mismatch
        ret = std::copy( mis.first, sub_last, ++ O(sub_first) );
    } else std::copy_backward( mis.first, sub_last, ++ (ret = sub_last) );

    * sub_first = * mis.second; // add first element not yet in result
    return ret;
}

#include <vector>
#include <iostream>
using namespace std;

char const *fruits_a[] = { "apples", "apples", "beans", "beans", "cherries" };
vector< string > fruits( fruits_a, fruits_a + sizeof fruits_a/sizeof *fruits_a );

int main() {
    vector< string > sub_fruits( fruits.size() );
    vector< string >::iterator last_fruit = sub_fruits.begin();

    while (
        ( last_fruit = next_subset( fruits.begin(), fruits.end(),
                                    sub_fruits.begin(), last_fruit )
        ) != sub_fruits.begin() ) {
        cerr << "size " << last_fruit - sub_fruits.begin() << ": ";
        for ( vector<string>::iterator fit = sub_fruits.begin(); fit != last_fruit; ++ fit ) {
            cerr << * fit << " ";
        }
        cerr << endl;
    }
}

输出:

size 1: apples 
size 2: apples apples 
size 1: beans 
size 2: apples beans 
size 3: apples apples beans 
size 2: beans beans 
size 3: apples beans beans 
size 4: apples apples beans beans 
size 1: cherries 
size 2: apples cherries 
size 3: apples apples cherries 
size 2: beans cherries 
size 3: apples beans cherries 
size 4: apples apples beans cherries 
size 3: beans beans cherries 
size 4: apples beans beans cherries 
size 5: apples apples beans beans cherries 

答案 4 :(得分:0)

C++ "next_combination"上搜索了this

  
      
  • 从“mid”向后搜索,直到找到一个较小的元素   比*(结束 - 1)。这是元素   我们应该增加。叫这个   “head_pos”。
  •   
  • 向后搜索“end”,直到找到最后一个元素   仍然大于* head_pos。叫它   “tail_pos”。
  •   
  • 交换head_pos和tail_pos。重新排序[head_pos + 1,mid []的元素   和[tail_pos + 1,结束[增加   顺序。
  •   

答案 5 :(得分:0)

如果你别无选择,但要实现自己的功能,也许这个恐怖可以帮助解决这个问题的一些或者其他恐怖。

Algorithm to return all combinations of k elements from n

我前段时间写过它,现在我的全部图片都没有:),但基本的想法是这样的: 您有原始集合,当前组合是所选元素的迭代器向量。要获得下一个组合,请从右向左扫描您的设置,寻找“气泡”。 “气泡”是指未选择的一个或多个相邻元素。 “泡沫”可能会立即出现在右边。然后,在您的组合中,您交换“气泡”左侧的第一个元素和组合中右侧的所有其他元素,其中相邻元素的子集从“”开头“开始。泡”。