是否有一些等效的库或函数可以为我提供一组值的下一个组合,例如 next_permutation in 对我有用吗?
6 回答
组合:来自 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;
}
我不知道一个。基本思想是将元素表示为位数组。例如,你有集合 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)
当我需要这样做时,我使用了这个库。它的界面非常相似,std::next_permutation
因此如果您以前使用过它,它将很容易使用。
幂集的枚举(即所有大小的所有组合)可以使用二进制增量算法的适配。
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
万一您别无选择,只能实现您自己的功能,也许这种恐怖可以帮助解决该问题的答案中的其他恐怖。
我前段时间写了它,现在我无法理解全貌:),但基本思想是:你有原始集合,当前组合是所选元素的迭代器向量。要获得下一个组合,您从右到左扫描您的集合以寻找“气泡”。“气泡”是指未选择的一个或多个相邻元素。“泡沫”可能就在右边。然后,在您的组合中,您将“气泡”左侧的第一个元素和该组合中位于集合右侧的所有其他元素与从“气泡”开头开始的相邻元素的子集交换气泡”。
谷歌搜索C++ "next_combination"
出现了这个。
- 从“mid”向后搜索,直到找到小于 *(end - 1) 的元素。这是我们应该增加的元素。称之为“head_pos”。
- 从“end”向后搜索,直到找到仍然大于 *head_pos 的最后一个元素。称之为“tail_pos”。
- 交换 head_pos 和 tail_pos。从 [head_pos + 1, mid[ 和 [tail_pos + 1, end[ 以升序重新排序元素。