next_permutation 用于幂集中的组合或子集

发布于 2024-08-29 13:24:01 字数 63 浏览 12 评论 0 原文

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

Is there some equivalent library or function that will give me the next combination of a set of values like next_permutation in does for me?

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

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

发布评论

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

评论(6

小糖芽 2024-09-05 13:24:01

组合:从 Mark Nelson 关于同一主题的文章中,我们有 next_combination http://marknelson.us/ 2002/03/01/下一个排列
排列:从 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;
 }

Combinations: from Mark Nelson's article on the same topic we have next_combination http://marknelson.us/2002/03/01/next-permutation
Permutations: from STL we have 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;
 }
只有一腔孤勇 2024-09-05 13:24:01

我不知道其中之一。基本思想是将元素表示为位数组。例如,您有集合 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 <= 大小(S)

I am not aware of one. The basic idea is to represent your elements as a bit array. So for example, you have the set S:

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

To generate the Power Set of S(just generate all numbers that are of size == 3 bits by using the simple addition):

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

All what you have to do is to find what bits are set, and to relate them to your set's elements.

On final note, there is one combination you can produce when you want all elements to be used and that combination is the set it self, because in combinations the order doesn't matter so for sure we are talking about a number of elements n where 0 <= n <= size(S)

耳根太软 2024-09-05 13:24:01

在 Google 上搜索 C++“next_combination” 出现

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

Googling for C++ "next_combination" turned up this.

  • search from "mid" backwards until you find an element that is smaller
    than *(end - 1). This is the element
    we should increment. Call this
    "head_pos".
  • search from "end" backwards until you find the last element that is
    still larger than *head_pos. Call it
    "tail_pos".
  • swap head_pos and tail_pos. Re-order the elements from [head_pos + 1, mid[
    and [tail_pos + 1, end[ in increasing
    order.
宛菡 2024-09-05 13:24:01

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

I've used this library when I've needed to do this. It has an interface very similar to std::next_permutation so it will be easy to use if you've used that before.

素罗衫 2024-09-05 13:24:01

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

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 

Enumeration of the powerset (that is, all combinations of all sizes) can use an adaptation of the binary increment algorithm.

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

The requirement of a bidirectional iterator is unfortunate, and could be worked around.

I was going to make it handle identical elements (multisets), but I need to go to bed :v( .

Usage:

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

EDIT: Here is the version for multisets. The set doesn't have to be sorted but identical elements do have to be grouped together.

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

Output:

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 
梦幻之岛 2024-09-05 13:24:01

如果您别无选择,只能实现自己的功能,也许这种恐怖可以在该问题的答案中有所帮助,或者也许还有其他恐怖。

返回 k 元素的所有组合的算法来自 n

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

In case You have no choice, but to implement Your own function maybe this horror can help a bit or maybe other horrors among answers to that question.

Algorithm to return all combinations of k elements from n

I wrote it some time ago and the full picture eludes me now :), but the basic idea is this:
You have the original set and current combination is a vector of iterators to the elements selected. To get the next combination, You scan your set from right to left looking for a "bubble". By "bubble" I mean one or more adjacent elements not selected. The "bubble" might be immediately at the right. Then, in Your combination, You exchange the first element at the left of the "bubble" and all other elements from the combination, that are to the right in the set, with a subset of adjacent elements that starts at the beginning of the "bubble".

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