幂集中组合或子集的 next_permutation

Posted

技术标签:

【中文标题】幂集中组合或子集的 next_permutation【英文标题】:next_permutation for combinations or subsets in powerset 【发布时间】:2011-02-10 17:54:23 【问题描述】:

是否有一些等效的库或函数可以为我提供一组值的下一个组合,例如 next_permutation in 对我有用吗?

【问题讨论】:

你需要更加具体 可能更具体一点也可以。 ***.com/questions/2211915/…的可能重复 【参考方案1】:

组合:来自 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;
 

【讨论】:

我发现很难相信该算法,因为它具有残暴的变量命名、明显的死代码等。 我仍然不确定正确性,但至少这里有一个稍微干净的版本paste.ubuntu.com/p/yXgtdjTyfD 惰性网络返回了这个实现,看起来它更多更加繁重。 howardhinnant.github.io/combinations.html /cc @howard-hinnant【参考方案2】:

我不知道。基本思想是将元素表示为位数组。例如,你有集合 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 &lt;= n &lt;= size(S)

【讨论】:

真的很喜欢这个主意! 不幸的是,在实现这个想法时遇到了麻烦。 @bobber205 你能解释一下你遇到了什么麻烦吗? 将整数转换为位数组。我会发誓我以前曾经为此使用过内置函数,所以我正在尝试自己编写它。 xD @bobber:这里的概念是一个powerset。它不同于通常所指的“组合”。而是搜索那个。另见***.com/questions/2506119/combinations-algorithm。【参考方案3】:

当我需要这样做时,我使用了this library。它的界面与std::next_permutation 非常相似,因此如果您以前使用过它,它将很容易使用。

【讨论】:

这似乎不是真正的 Boost 库…… 不,但它可以工作,并且在 boost 软件许可下获得许可,所以只需将头文件与源代码一起粘贴...【参考方案4】:

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

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.)

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

我打算让它处理相同的元素(多集),但我需要上床睡觉: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 

【讨论】:

【参考方案5】:

搜索C++ "next_combination" 出现this。

从“中间”向后搜索,直到找到更小的元素 比 *(结束 - 1)。这是元素 我们应该增加。调用这个 “head_pos”。 从“end”向后搜索,直到找到最后一个元素 仍然大于 *head_pos。称它为 “tail_pos”。 交换 head_pos 和 tail_pos。从 [head_pos + 1, mid[ 重新排序元素 和 [tail_pos + 1, end[ 在增加 订购。

【讨论】:

@bobber:你能说得更具体点吗?【参考方案6】:

如果您别无选择,只能实现您自己的功能,也许这种恐怖可以帮助解决该问题的一些问题或其他恐怖。

Algorithm to return all combinations of k elements from n

我前段时间写了它,现在我无法理解完整的图片:),但基本的想法是这样的: 您拥有原始集合,当前组合是所选元素的迭代器向量。要获得下一个组合,您从右到左扫描您的集合以寻找“气泡”。 “气泡”是指未选择的一个或多个相邻元素。 “泡沫”可能就在右边。然后,在您的组合中,您将“气泡”左侧的第一个元素和该组合中位于集合右侧的所有其他元素与从“气泡”开头开始的相邻元素的子集交换气泡”。

【讨论】:

以上是关于幂集中组合或子集的 next_permutation的主要内容,如果未能解决你的问题,请参考以下文章

next_permutatio

根据某些匹配变量分离数据集

矩阵快速幂专题

Java求解子集II

[JSOI2015] 子集选取

寻找子集组合以实现给定总和同时保持成本最小的算法