Enumerating a power set (i.e., 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 bidirectional iterator requirement is unsuccessful and can be worked out.
I was going to get it to handle the same elements (multisets), but I need to go to bed: v (.
Using:
#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 multiset version. The set does not need to be sorted, but identical elements need to be grouped.
#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