I need an algorithm that, given an even number of elements, performs an evaluation on all combinations of the elements divided into two groups. Order within a group doesn't matter and so permutations within groups shouldn't be repeated. An example with N=4 elements would be the evaluations
e(12,34), e(13,24), e(14,32), e(32,14), e(34,12), e(24,13)
I thought I had it, with a recursive algorithm that works up to N=6, but it turns out it fails for N=8. This is the algorithm (this version just prints out the two groups; in my actual implementation it will perform a calculation):
// Class for testing algoritm
class sym {
private:
int N, Nhalf, combs;
VI order;
void evaluate();
void flip(int, int);
void combinations(int, int);
public:
void combinations();
sym(int N_) : N(N_) {
if(N%2) {
cout "Number of particles must divide the 2 groups; requested N = " << N << endl;
throw exception();
}
Nhalf=N/2;
order.resize(N);
for(int i=0;i<N;i++) order[i]=i+1;
}
~sym() {
cout << endl << combs << " combinations" << endl << endl;
}
};
// Swaps element n in group 1 and i in group 2
void sym::flip(int n, int i) {
int tmp=order[n];
order[n]=order[i+Nhalf];
order[i+Nhalf]=tmp;
}
// Evaluation (just prints the two groups)
void sym::evaluate() {
for(int i=0;i<Nhalf;i++) cout << order[i] << " ";
cout << endl;
for(int i=Nhalf;i<N;i++) cout << order[i] << " ";
cout << endl << "--------------------" << endl;
combs++;
}
// Starts the algorithm
void sym::combinations() {
cout << "--------------------" << endl;
combinations(0, 0);
}
// Recursive algorithm for the combinations
void sym::combinations(int n, int k) {
if(n==Nhalf-1) {
evaluate();
for(int i=k;i<Nhalf;i++) {
flip(n, i);
evaluate();
flip(n, i);
}
return;
}
combinations(n+1, k);
for(int i=k;i<Nhalf;i++) {
flip(n, i);
combinations(n+1, k+i+1);
flip(n, i);
}
}
If I run this with N=2 for example, I get the correct
--------------------
1 2
3 4
--------------------
1 3
2 4
--------------------
1 4
3 2
--------------------
3 2
1 4
--------------------
3 4
1 2
--------------------
4 2
3 1
--------------------
6 combinations
But it seems N>6 doesn't work. Is there a simple change that will fix this, or will I have to rethink the whole thing?
EDIT: It is better if every change just involves swapping two elements (like the failed attempt above); as this will make the code faster in the end I think.
EDIT: Just realised it fails for N=6 as well, sloppy testing.
std::next_permutation may help (without recursion):
#include <iostream>
#include <algorithm>
template<typename T>
void do_job(const std::vector<T>& v, const std::vector<std::size_t>& groups)
{
std::cout << " e(";
for (std::size_t i = 0; i != v.size(); ++i) {
if (groups[i] == 0) {
std::cout << " " << v[i];
}
}
std::cout << ",";
for (std::size_t i = 0; i != v.size(); ++i) {
if (groups[i] == 1) {
std::cout << " " << v[i];
}
}
std::cout << ")\n";
}
template<typename T>
void print_combinations(const std::vector<T>& v)
{
std::vector<std::size_t> groups(v.size() / 2, 0);
groups.resize(v.size(), 1); // groups is now {0, .., 0, 1, .., 1}
do {
do_job(v, groups);
} while (std::next_permutation(groups.begin(), groups.end()));
}
int main()
{
std::vector<int> numbers = {1, 2, 3, 4};
print_combinations(numbers);
}
Live Demo
// generate all combination that use n of the numbers 1..k
void sym::combinations(int n, int k) {
if (n>k) return; // oops
if (n==0) { evaluate(); return; }
combinations(n, k-1);
order[n-1] = k;
combinations(n-1,k-1);
}
Start that with combinations(N/2,N) No need to pre initialize order. But as coded it only fills the first half of order with the first group and you need to post process to get the second group.
With a moderate amount of extra logic, you could instead fill in the second half during combinations. I think this does it:
void sym::combinations(int n, int k) {
if (k==0) { evaluate(); return; }
if (n>0) {
order[n-1] = k;
combinations(n-1,k-1); }
if (n<k) {
order[Nhalf+k-n-1] = k;
combinations(n, k-1); }
}
I think a flip based design is uglier. But after more thought, it isn't actually difficult. So changing back to your design of starting at combinations(0,0) you could use:
// Generate all combinations subject to having already filled the first n
// of the first group and having already filled the last k of the second.
void sym::combinations(int n, int k) {
if(n==Nhalf) {
// Once the first group is full, the rest must be the second group
evaluate();
return;
}
// Since the first group isn't full, recursively get all combinations
// That make the current order[n] part of the first group
combinations(n+1,k);
if (k<Nhalf) {
// Next try all combinations that make the current order[n] part of
// the second group
std::swap(order[n], order[N-k-1]);
combinations(n,k+1);
// Since no one cares about the sequence of the items not yet chosen
// there is no benefit to swapping back.
}
}
To recursively list the n choose n/2 combinations you could use an algorithm that adds each value to either group:
f(n,k,A,B):
if k == 0:
output A,B with {n,n-1..1}
else if n == k:
output A with {n,n-1..1},B
else if k > 0:
f(n-1,k-1,A with n,B)
f(n-1,k,A,B with n)
Example below. To half the accumulating stack, one could skip one of the two first recursive calls and reverse the order of the pair during evaluation.
f(4,2,[],[])
f(3,1,[4],[])
f(2,0,[4,3],[]) => {[4,3],[2,1]}
f(2,1,[4],[3])
f(1,0,[4,2],[3]) => {[4,2],[3,1]}
f(1,1,[4],[3,2]) => {[4,1],[3,2]}
f(3,2,[],[4])
f(2,1,[3],[4])
f(1,0,[3,2],[4]) => {[3,2],[4,1]}
f(1,1,[3],[4,2]) => {[3,1],[4,2]}
f(2,2,[],[4,3]) => {[2,1],[4,3]}
Related
I try to implement Hierholzer's algorithm in C++. The underlying multigraph is represented using a two-dimensional map, which maps two vertices to the number of edges between them. My approach is to decrement this count of an edge every time it is traversed and completely removing it if the count is 0.
This is the function:
vector<int> euler_tour(map_2d edge_copies) {
map<int, int> available_nodes;
available_nodes.insert({ 0, 0 });
vector<int> tour = { 0 };
while (!edge_copies.empty()) {
int n0 = (available_nodes.begin())->first;
int n = n0;
vector<int> subtour = { n0 };
while (!edge_copies.at(n).empty()) {
int n_ = (edge_copies.at(n).begin())->first;
subtour.push_back(n_);
cout << n << " " << n_ << endl;
cout << "graph and soubtour: " << endl;
print_map_2d(edge_copies);
print_vector(subtour);
edge_copies.at(n).at(n_) -= 1;
edge_copies.at(n_).at(n) -= 1;
if (edge_copies.at(n).at(n_) == 0) {
edge_copies.at(n).erase(n_);
edge_copies.at(n_).erase(n);
}
if (edge_copies.at(n).empty()) {
edge_copies.erase(n);
available_nodes.erase(n);
} else {
available_nodes[n] = available_nodes.at(n0) + subtour.size() - 1;
}
if (edge_copies.at(n_).empty()) {
edge_copies.erase(n_);
available_nodes.erase(n_);
} else {
available_nodes[n_] = available_nodes.at(n0) + subtour.size();
}
n = n_;
}
tour.insert(tour.begin() + available_nodes[n0], subtour.begin(), subtour.end());
print_vector(tour);
}
}
When I execute it, i get munmap_chunk(): invalid pointer after the first iteration of the second while loop. I experimented a bit, and I think it breaks at the line subtour.push_back(n_);. But why? This is a standard operation. Or do you see any other problematic lines?
The functions print_map_2d and print_vector are implemented elsewhere, they are surely not the problem.
Let say I've a target number and a list of possibile values that I can pick to create a sequence that, once summed every picked number, will sum to the target:
target = 31
list = 2, 3, 4
possible sequence: 3 2 4 2 2 2 4 2 3 2 3 2
I'd like to:
first decide if there is any sequence that will reach the target
return one of the many (possible) sequence
This is my attempt:
#include <iostream>
#include <random>
#include <chrono>
#include <vector>
inline int GetRandomInt(int min = 0, int max = 1) {
uint64_t timeSeed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
std::seed_seq ss{ uint32_t(timeSeed & 0xffffffff), uint32_t(timeSeed >> 32) };
std::mt19937_64 rng;
rng.seed(ss);
std::uniform_int_distribution<int> unif(min, max);
return unif(rng);
}
void CreateSequence(int target, std::vector<int> &availableNumbers) {
int numAttempts = 1;
int count = 0;
std::vector<int> elements;
while (count != target) {
while (count < target) {
int elem = availableNumbers[GetRandomInt(0, availableNumbers.size() - 1)];
count += elem;
elements.push_back(elem);
}
if (count != target) {
numAttempts++;
count = 0;
elements.clear();
}
}
int size = elements.size();
std::cout << "count: " << count << " | " << "num elements: " << size << " | " << "num attempts: " << numAttempts << std::endl;
for (auto it = elements.begin(); it != elements.end(); it++) {
std::cout << *it << " ";
}
}
int main() {
std::vector<int> availableNumbers = { 2, 3, 4 };
CreateSequence(31, availableNumbers);
}
But it can loop infinitely if the list of number can't be appropriate to reach such sum; example:
std::vector<int> availableNumbers = { 3 };
CreateSequence(8, availableNumbers);
No sequence of 3 will sum to 8. Also, if the list is huge and the target number high, it can lead to a huge amount of processing (cause lots of while check fails).
How would you implement this kind of algorithm?
Your suggested code is possibly very fast, since it is heuristic. But as you said, it gets potentially trapped in a nearly endless loop.
If you want to avoid this situation, you have to search the complete set of possible combinations.
Abstraction
Let's define our algorithm as a function f with a scalar target t and a vector <b> as parameters returning a vector of coefficients <c>, where <b> and <c> have the same dimension:
<c> = f(t, <b>)
First the given set of numbers Sg should be reduced to their reduced set Sr so we reduce the dimension of our solution vector <c>. E.g. {2,3,4,11} can be reduced to {2,3}. We get this by calling our algorithm recursively by splitting Sg into a new target ti with the remaining numbers as the new given set Sgi and ask the algorithm, if it finds any solution (a non-zero vector). If so, remove that target ti from the original given set Sg. Repeat this recursively until no solutions found any more.
Now we can understand this set of numbers as a polynomial, where we are looking for possible coefficients ci to get our target t. Let's call each element in Sb bi with i={1..n}.
Our test sum ts is the sum over all i for ci * bi, where each ci can run from 0 to ni = floor(t/bi).
The number of possible tests N is now the product over all ni+1: N = (n1+1) * (n2+1) * ... * (ni+1).
Iterate now over all possibilities by representing the coefficient vector <c> as an vector of integers and incrementing c1 and carrying over an overrun to the next element in the vector, resetting c1 and so forth.
Example
#include <random>
#include <chrono>
#include <vector>
#include <iostream>
using namespace std;
static int evaluatePolynomial(const vector<int> &base, const vector<int> &coefficients)
{
int v=0;
for(unsigned long i=0; i<base.size(); i++){
v += base[i]*coefficients[i];
}
return v;
}
static bool isZeroVector(vector<int> &v)
{
for (auto it = v.begin(); it != v.end(); it++) {
if(*it != 0){
return false;
}
}
return true;
}
static vector<int> searchCoeffs(int target, vector<int> &set) {
// TODO: reduce given set
vector<int> n = set;
vector<int> c = vector<int>(set.size(), 0);
for(unsigned long int i=0; i<set.size(); i++){
n[i] = target/set[i];
}
c[0] = 1;
bool overflow = false;
while(!overflow){
if(evaluatePolynomial(set, c) == target){
return c;
}
// increment coefficient vector
overflow = true;
for(unsigned long int i=0; i<c.size(); i++){
c[i]++;
if(c[i] > n[i]){
c[i] = 0;
}else{
overflow = false;
break;
}
}
}
return vector<int>(set.size(), 0);
}
static void print(int target, vector<int> &set, vector<int> &c)
{
for(unsigned long i=0; i<set.size(); i++){
for(int j=0; j<c[i]; j++){
cout << set[i] << " ";
}
}
cout << endl;
cout << target << " = ";
for(unsigned long i=0; i<set.size(); i++){
cout << " +" << set[i] << "*" << c[i];
}
cout << endl;
}
int main() {
vector<int> set = {4,3,2};
int target = 31;
auto c = searchCoeffs(target, set);
print(target, set,c);
}
That code prints
4 4 4 4 4 4 4 3
31 = +4*7 +3*1 +2*0
Further Thoughts
productive code should test for zeros in any given values
the search could be improved by incrementing the next coefficient if the evaluated polynomial already exceeded the target value.
further speedup is possible, when calculating the difference of the target value and the evaluated polynomial when c1 is set to zero, and checking if that difference is a multiple of b1. If not, c2 could be incremented straight forward.
Perhaps there exist some shortcuts exploiting the least common multiple
As ihavenoidea proposed, I would also try backtracking. In addition, I will sort the numbers in decreasing order, il order to speed up the process.
Note: a comment would be more appropriate than an answer, but I am not allowed to. Hope it helps. I will suppress this answer if requested.
I have an array of pairs that represent a range of [begin,end). The array can be assumed to already sorted by the 'begin' field.
I want to generate a new array with all of the overlaps removed, and additional pairs created, as needed.
For example, let's say the array contained the following pairs:
[1,3],[2,5],[7,15],[8,9],[12,19]
The output should be as follows:
[1,2],[2,3],[3,5],[7,8],[8,9],[9,12],[12,15],[15,19]
Ultimately, the output array should contain no overlaps at all.
What's the most optimal solution that takes no more than O(m), where m is the number of entries needed in the output array? I think I see a way to do it in O(n^2), where n is the number of entries in the input array, but there's got to be a better way.
The final implementation will be in C++11, using vectors of pairs of doubles, although pseudocode solutions are fine.
EDIT:
I appreciate all responses, but I would politely request in advance to please not post any solutions that depend on particular frameworks or libraries unless such frameworks are part of standard c++11.
First I'll solve a related problem; generate merged intervals that cover the same area with no adjacency or overlap.
Walk the input array. Start with the first element. Record highwater (end of interval) and lowater (start of interval).
Proceed forward. Each element, if it overlaps the interval, extend highwater. If not, output highwater and lowater as an interval, then record a new high and lowater.
This takes O(n) time on input.
Every element of input must be read, because any of them could go from their start location to the end and change the result. So this is O-optimal.
This merges intervals into the largest contiguous one you can make; you want to save all of the "edges" or "seams" in the original intervals. To solve your spec, simply keep track of seams (in order) and break the generated intervals at those seams. "Lowater" seams will always come with increasing values; highwater seams may not. So an ordered set of seams should work. This is O(nlgn) sadly due to the set.
// half open
struct interval {
int lowater = 0;
int highwater = 0;
bool empty() const {
return lowater == highwater;
}
friend std::ostream& operator<<( std::ostream& os, interval i ) {
return os << "[" << i.lowater << "," << i.highwater << "]";
}
};
template<class Range, class Out>
void build_intervals( Range&& in, Out out ) {
std::optional<interval> current;
std::set<int> seams;
auto dump_interval = [&](interval i){
if (i.empty()) return;
*out = i;
};
auto dump_current = [&]{
if (!current) return;
// std::cout << "Dumping " << *current << " with seams: {";
for (int seam:seams) {
// std::cout << seam << ",";
dump_interval({ current->lowater, seam });
current->lowater = seam;
}
// std::cout << "}\n";
dump_interval( *current );
current = std::nullopt;
seams.clear();
};
for (auto&& e : in) {
if (current && e.lowater <= current->highwater) {
seams.insert(e.lowater);
seams.insert(e.highwater);
// std::cout << "No gap between " << *current << " and " << e << "\n";
current->highwater = (std::max)(e.highwater, current->highwater);
// std::cout << "Combined: " << *current << "\n";
continue;
}
if (!current) {
// std::cout << "New current " << e << "\n";
} else {
// std::cout << "Gap between " << *current << " and " << e << "\n";
dump_current();
}
current = e;
seams.insert(e.lowater);
seams.insert(e.highwater);
}
dump_current();
}
live example.
I came up with something like this, by adding just couple of if it is done in O(n) time. I'm just not sure about last elements, my output:
[1 : 2], [2 : 3], [3 : 5], [7 : 8], [8 : 9], [9 : 12], [12 : 15], [15 : 19]
Maybe its something that would help:
std::vector<std::pair<int, int>> noOverlaps(std::vector<std::pair<int, int>>& input) {
if (input.size() <= 1) {
return input;
}
std::vector<std::pair<int, int>> result;
result.push_back(input[0]);
for (int i = 1; i < input.size(); ++i) {
//If overlap
if (input[i].first < result.back().second) {
auto lastOne = result.back();
result.pop_back();
result.push_back(std::make_pair(lastOne.first, input[i].first));
if (lastOne.second > input[i].second) {
result.push_back(std::make_pair(input[i].first, input[i].second));
result.push_back(std::make_pair(input[i].second, lastOne.second));
} else {
result.push_back(std::make_pair(input[i].first, lastOne.second));
result.push_back(std::make_pair(lastOne.second, input[i].second));
}
} else {
result.push_back(input[i]);
}
}
return result;
}
Update 1
As pointed out in the comment above will not work with multiple overlapping intervals, so the above solution can be improved by swallowing intervals that are containing each other and run the same algorithm:
std::vector<std::pair<int, int>> noOverlaps(std::vector<std::pair<int, int>>& origInput) {
if (origInput.size() <= 1) {
return origInput;
}
std::vector<std::pair<int, int>> result;
std::vector<std::pair<int, int>> input;
input.push_back(origInput[0]);
for (int i = 1; i < origInput.size(); ++i) {
if (input[i-1].first <= origInput[i].first && input[i-1].second >= origInput[i].second) {
continue;
}
input.push_back(origInput[i]);
}
result.push_back(input[0]);
for (int i = 1; i < input.size(); ++i) {
//If overlap
if (input[i].first < result.back().second) {
auto lastOne = result.back();
result.pop_back();
result.push_back(std::make_pair(lastOne.first, input[i].first));
if (lastOne.second > input[i].second) {
result.push_back(std::make_pair(input[i].first, input[i].second));
result.push_back(std::make_pair(input[i].second, lastOne.second));
} else {
result.push_back(std::make_pair(input[i].first, lastOne.second));
result.push_back(std::make_pair(lastOne.second, input[i].second));
}
} else {
result.push_back(input[i]);
}
}
return result;
}
But this requires 2xO(n) space complexity and code is not nice.
So I just wonder would that not be enough:
std::vector<std::pair<int, int>> noOverlaps2(std::vector<std::pair<int, int>>& origInput) {
if (origInput.size() <= 1) {
return origInput;
}
int low = origInput[0].first, high = origInput[0].second;
std::vector<std::pair<int, int>> result;
for (int i = 1; i < origInput.size(); ++i) {
if (high < origInput[i].first) {
result.emplace_back(low, high);
low = origInput[i].first;
high = origInput[i].second;
} else {
high = std::max(origInput[i].second, high);
}
}
result.emplace_back(low, high);
return result;
}
For your data it gives output:[1 : 5], [7 : 19] but it get rid of overlaps.
I'm trying to make a program that works with a simple algorithm.
But for some reason, I get a strange bug (below is the simplified version of the program).
#include "stdafx.h"
#include <iostream>
#include <string>
using std::cout;
using std::string;
void find(int arr[], string name)
{
int t = 8;
int i = 0;
int v = 0;
// t should become equal to the smallest int of the array after this.
while (arr[i])
{
if (arr[i] < t)
{
t = arr[i];
}
++i;
}
/* When this statement below gets executed t gets what looks like a
random value for some reason */
cout << arr[t] << '\n';
for (int b = 0; b < 2; ++b)
{
if (t == arr[b])
{
v = b;
}
}
/* Again, arr[v] gets what looks like a random number */
cout << "The cheapest " << name << " is number " << arr[v] << ".";
}
int main()
{
/* [0] = "Cost for Steve"
[1] = "Cost for Mark"
[2] = "Cost for Andrew" */
int cleaning[] = { 5, 4, 7 };
int cooking[] = { 3, 6, 4 };
int babysitting[] = { 7, 6, 3 };
cout << "Number 1: Steve, Number 2: Mark, Number 3: Andrew.\n";
find(cleaning, "cleaner");
find(cooking, "cook");
find(babysitting, "babysitter");
/* This is to prevent the console application from quitting */
while (true)
{
}
}
I'm sure there is something wrong in the for and the while loop, but what?
If you're reading my code and some text or variable name seems foreign to you, chances are that I forgot to translate it (this is originally written in Italian).
Thanks for taking your time to read this.
EDIT: Thanks to #Tar I fixed the first part of the program, but the part which says The (name) that costs less is n. (arr[v]).still gives me a random number, I compiled and ran the program, the output is:
Number 1: Steve, Number 2: Mark, Number 3: Andrew.
4
The cheapest cleaner is number 4.
3
The cheapest cook is number 3.
3
The cheapest babysitter is number 7.
That is obviously wrong as it should say that the cheapest cleaner is number 2, the cheapest cook is number 1 and the cheapest babysitter is number 3.
PS: As soon as everything is fixed I will take the part which prints the cheapest price out.
The problem is within your first while loop in find:
while (arr[i]) // while the element at arr[i] is NOT 0
{
if (arr[i] < t)
{
t = arr[i];
}
i++;
}
Here you continuously evaluate elements in arr for whether they are not 0. This is not correct. You've declared your arrays as:
int cleaning[3] = { 5, 4, 7 };
int cooking[3] = { 3, 6, 4 };
int babysitting[3] = { 7, 6, 3 };
None of these contain 0, so your while loop will run indefinitely and you'll be reading past the memory for each array which is not good news.
Consider using std::vector instead, and see how much clearer and safer your code becomes:
#include <vector>
#include <iostream>
void find(const std::vector<int>& numbers)
{
auto t = 8;
// Look through each element in the container:
for(auto number : numbers)
{
if (number < t)
{
t = number;
}
}
std::cout << t << std::endl;
}
int main()
{
std::vector<int> cleaning = {5, 4, 7};
find(cleaning);
}
Above all, I want to make a statement: I am not an English-speaker, so if I said wrong words, please excuse me.
I think this question is not very difficult. I fixed your algorithm and output format. Actually, I almost rewrote it.
In my view, your code seems kind of naive. If you only learnt C++ syntax, there is a long way to study algorithm.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int find(const vector<int>& numbers) {
int minVul = numbers[0];
int rank = 0;
for (int i = 1; i < numbers.size(); i++)
{
if (minVul > numbers[i])
{
minVul = numbers[i];
rank = i;
}
}
return rank;
}
int main() {
vector<string> name = { "steve","mark","andrew" };
/* [0] = "Cost for steve"
[1] = "Cost for mark"
[2] = "Cost for andrew" */
vector<int> cleaning = { 5, 4, 7 };
vector<int> cooking = { 3, 6, 4 };
vector<int> babysitting = { 7, 6, 3 };
int cleaner = find(cleaning);
cout << "Cleaning:" << name[cleaner] << " costs least in " << cleaning[cleaner] << endl;
int cooker = find(cooking);
cout << "Cooking:" << name[cooker] << " costs least in " << cooking[cooker] << endl;
int babysitter = find(babysitting);
cout << "Babysitter:" << name[babysitter] << " costs least in " << babysitting[babysitter] << endl;
system("pause"); //This is a common skill to prevent the console application from quitting.
return 0;
}
Outputs:
Cleaning:mark costs least in 4
Cooking:steve costs least in 3
Babysitter:andrew costs least in 3
So if I am given an array such as
a = {1, 2, 3}
We know that the given subarrays (non contiguous included) are (this represents the power set)
{1} {2} {3} {1,2,3} {1,2} {1,3} {2,3}
I also know that these subsets can be represented by counting in binary from
000 -> 111 (0 to 7), where each 1 bit means we 'use' this value from the array
e.g. 001 corresponds to the subset {3}
I know that this method can somehow be used to generate all subsets, but im not really sure how this can be implemented in c++
So basically what I am asking is how can (if it can) binary counting be used to generate power sets?
Any other methods for generating a power set are also much appreciated!
For your example with 3 set elements you can just do this:
for (s = 1; s <= 7; ++s)
{
// ...
}
Here's a demo program:
#include <iostream>
int main()
{
const int num_elems = 3; // number of set elements
const int elems[num_elems] = { 1, 2, 3 }; // mapping of set element positions to values
for (int s = 1; s < (1 << num_elems); ++s) // iterate through all non-null sets
{
// print the set
std::cout << "{";
for (int e = 0; e < num_elems; ++e) // for each set element
{
if (s & (1 << e)) // test for membership of set
{
std::cout << " " << elems[e];
}
}
std::cout << " }" << std::endl;
}
return 0;
}
Compile and test:
$ g++ -Wall sets.cpp && ./a.out
{ 1 }
{ 2 }
{ 1 2 }
{ 3 }
{ 1 3 }
{ 2 3 }
{ 1 2 3 }
Note that it's a common convention to make the least significant bit correspond to the first set element.
Note also that we are omitting the null set, s = 0, as you don't seem to want to include this.
If you need to work with sets larger than 64 elements (i.e. uint64_t) then you'll need a better approach - you can either expand the above method to use multiple integer elements, or use std::bitset or std::vector<bool>, or use something like #Yochai's answer (using std::next_permutation).
Actually creating the sets is pretty easy - just use bitwise operations >>= and & to test a bit at a time. Assuming input vector/array a[] known to have 3 elements and therefore produce a 7 vector output:
std::vector<std::vector<T>> v(7);
for (int n = 1; n <= 7; ++n) // each output set...
for (int i = 0, j = n; j; j >>= 1, ++i) // i moves through a[i],
// j helps extract bits in n
if (j & 1)
v[n-1].push_back(a[i]);
For compile time size, you may use bitset, something like:
template <std::size_t N>
bool increase(std::bitset<N>& bs)
{
for (std::size_t i = 0; i != bs.size(); ++i) {
if (bs.flip(i).test(i) == true) {
return true;
}
}
return false; // overflow
}
template <typename T, std::size_t N>
void display(const std::array<T, N>& a, const std::bitset<N>& bs)
{
std::cout << '{';
const char* sep = "";
for (std::size_t i = 0; i != bs.size(); ++i) {
if (bs.test(i)) {
std::cout << sep << a[i];
sep = ", ";
}
}
std::cout << '}' << std::endl;
}
template <typename T, std::size_t N>
void display_all_subsets(const std::array<T, N>& a)
{
std::bitset<N> bs;
do {
display(a, bs);
} while (increase(bs));
}
Live example