tags:

views:

231

answers:

6

I can iterate over the subsets of size 1

for( int a = 0; a < size; a++ ) {

or subsets of size 2

for( int a1 = 0; a1 < size; a1++ ) {
    for( int a2 = a1+1; a2 < size; a2++ ) {

or 3

for( int a1 = 0; a1 < size; a1++ ) {
for( int a2 = a1+1; a2 < size; a2++ ) {
   for( int a3 = a2+1; a3 < size; a3++ ) {

But how to do this for subsets of size n?

This does the job, based on an answer by Adam Rosenfield

void iterate(int *a, int i, int size, int n)
{
 int start = 0;
 if( i > 0 ) start = a[i-1]+1;
 for(a[i] = start; a[i] < n; a[i]++) {
  if(i == n-1) {
      // a is the array of indices of size n
      for( int k = 0; k < size; k++ ) {
          printf("%d ",a[k]);
      }
      printf("\n");
  }
        else
            iterate(a, i+1, size, n);
    }
}
+2  A: 

You likely could do this with some recursion.

Daniel A. White
this is a valid answer to EVERY question. You can do ANYTHING with recursion!
Brian Postow
+6  A: 

You can use recursion:

void iterate(int *a, int i, int size, int n)
{
    for(a[i] = 0; a[i] < size; a[i]++)
    {
        if(i == n-1)
            DoStuff(a, n);  // a is the array of indices of size n
        else
            iterate(a, i+1, size, n);
    }
}
...
// Equivalent to 4 nested for loops
int a[4];
iterate(a, 0, size, 4);
Adam Rosenfield
This is interesting. However a[i] > a[j] where i>j is not enforced
ravenspoint
But if I add this: int start = 0; if( i > 0 ) start = a[i-1]; for(a[i] = start; a[i] < size; a[i]++)
ravenspoint
Oops! Should be int start = 0; if( i > 0 ) start = a[i-1]+1; for(a[i] = start; a[i] < size; a[i]++)
ravenspoint
One more change needed. The for loop maximum should be n, rather than size. With these changes, the algorithm works in production code and passes unit tests! Thanks.
ravenspoint
A: 

Here is something I used for a similar problem. It does not use recursion; rather, it uses a vector of indexes.

#include <vector>

template<class T>
class MultiForVar {
std::vector<T> _begin, _end, _vars;
inline int dim(){return _vars.size();}
public:
MultiForVar(std::vector<T> begin, std::vector<T> end) : _begin(begin), _end(end), _vars(_begin)
{
  assert(begin.size() == end.size() and "Starting and ending vector<T> not the same size!" );
}
MultiForVar& operator ++()
{
  ++_vars[dim()-1];
  for(int d = dim()-1; d > 0; --d)
  {
    if( _vars[d] >= _end[d] )
    {
      _vars[d] = _begin[d];
      ++_vars[d-1];
    }
  }
  return *this;
}
bool done()
{
  /*for(int d = 0; d < dim(); ++d)
    if( _vars[d] < _end[d] )
      return false;
  return true;*/
  return (_vars[0] >= _end[0]);
}
T operator[](int d)
{
  return _vars.at(d);
}
int numDimensions(){
  return dim();
}
std::vector<T>& getRaw(){
  return _vars;
}

};

Adrian Park
I love it when people are so afraid of recursion that instead they create code that is 5x as long, and much harder to write! B-)
Brian Postow
Forcing recursion for a non-recursive problem is just as silly. My code is 5x times as long because it is written to be reusable, not because I am "afraid of recursion".
Adrian Park
A: 

Many many ways are described in more detail than anyone should require in Donald Knuth's recent addition to The Art of Computer Programming.

PeterAllenWebb
A: 

If I understand what you're asking correctly, another way to do it is to use bit-wise operators:

for(int i = 0; i < 1<<size; i++) {
    for(int j = 0; j < size; j++) {
       if(i & 1<<j) printf("%d ", a[j]);
    }
    printf("\n");
}
Soufiane Hassou
A: 

You need something the constructs the powerset of the original set. It's been a while since I've written that, but the psuedocode looks like

Powerset(a, size)
{
   if(size == 0) return emptyset

   subseta = Powerset(a, size-1) // Powerset of everything except last element
   subsetb = appendToAll(a[size-1], subseta) // appends the last element to every set in subseta
   return union(subseta, subsetb)
}
Brian Postow