kayos
kayos

Reputation: 53

Dynamic Nested Loops (C++)

Hello I am looking for a way to write this C++ Code in a general way, so that if a want 20 columns I will not have to write 20 for loops:

for(int i=1; i<6; i++) {
    for(int j=i; j<6; j++) {
        for(int k=j; k<6; k++) {
            for(int m=k; m<6; m++) {
                std::cout << i << j << k << m << std::endl;
            }
        }
    }
}

It is important that my numbers follow a >= Order. I am very grateful for any help.

Upvotes: 3

Views: 1277

Answers (5)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275385

The design here is simple. We take a std::vector each containing a dimension count and a std::vector containing a current index at each dimension.

advance advances the current bundle of dimension indexes by amt (default 1).

void advance( std::vector<size_t>& indexes, std::vector<size_t> const& counts, size_t amt=1 ) {
  if (indexes.size() < counts.size())
    indexes.resize(counts.size());
  for (size_t i = 0; i < counts.size(); ++i ) {
    indexes[i]+=amt;
    if (indexes[i] < counts[i])
      return;
    assert(counts[i]!=0);
    amt = indexes[i]/counts[i];
    indexes[i] = indexes[i]%counts[i];
  }
  // past the end, don't advance:
  indexes = counts;
}

which gives us an advance function for generic n dimensional coordinates.

Next, a filter that tests the restriction you want:

bool vector_ascending( std::vector<size_t> const& v ) {
  for (size_t i = 1; (i < v.size()); ++i) {
    if (v[i-1] < v[i]) {
      return false;
    }
  }
  return true;
}

then a for loop that uses the above:

void print_a_lot( std::vector<size_t> counts ) {
  for( std::vector<size_t> v(counts.size()); v < counts; advance(v,counts)) {
    // check validity
    if (!vector_ascending(v))
      continue;
    for (size_t x : v)
      std::cout << (x+1);
    std::cout << std::endl;
  }
}

live example.

No recursion needed.

The downside to the above is that it generates 6^20 elements, and then filters. We don't want to make that many elements.

void advance( std::vector<size_t>& indexes, std::vector<size_t> const& counts, size_t amt=1 ) {
  if (indexes.size() < counts.size())
    indexes.resize(counts.size());
  for (size_t i = 0; i < counts.size(); ++i ) {
    indexes[i]+=amt;
    if (indexes[i] < counts[i])
    {
      size_t min = indexes[i];
      // enforce <= ordering:
      for (size_t j = i+i; j < counts.size(); ++j) {
        if (indexes[j]<min)
          indexes[j]=min;
        else
          break; // other elements already follow <= transitively
      }
      assert(vector_ascending(indexes));
      return;
    }
    assert(counts[i]!=0);
    amt = indexes[i]/counts[i];
    indexes[i] = indexes[i]%counts[i];
  }
  // past the end, don't advance:
  indexes = counts;
}

which should do it without the vector_ascending check in the previous version. (I left the assert in to do testing).

Upvotes: 1

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122331

Well, I am not the fastest in writing answer... when I started there was no other answer. Anyhow, here is my version:

#include <iostream>
#include <vector>
using namespace std;

class Multiindex {
    public:
        typedef std::vector<int> Index;
        Multiindex(int dims,int size) : 
             dims(dims),size(size),index(Index(dims,0)){}
        void next(){
            int j=dims-1;
            while (nextAt(j) && j >= 0){j--;}
        }
        Index index;
        bool hasNext(){return !(index[0]==size-1);}
    private:
        bool nextAt(int j){
            index[j] = index[j]+1;
            bool overflow = (index[j]==size);
            if (!overflow && j < dims-1){std::fill(index.begin() + j + 1,index.end(),index[j]);}
            return overflow;
        }
    int dims;
    int size;
};

int main() {
   Multiindex m(4,6);
   while (m.hasNext()){
       cout << m.index[0] << m.index[1] << m.index[2] << m.index[3] << endl;
       m.next();
   }
   cout << m.index[0] << m.index[1] << m.index[2] << m.index[3] << endl;
   return 0;
}

Upvotes: 0

Slava
Slava

Reputation: 44248

This recursive function should work:

#include <iostream>

bool inc( int *indexes, int limit, int n )
{
    if( ++indexes[n] < limit )
        return true;
    if( n == 0 ) return false;
    if( inc( indexes, limit, n-1 ) ) {
        indexes[n] = indexes[n-1];
        return true;
    }
    return false;

}

int main()
{
    const size_t N=3;
    int indexes[N];
    for( size_t i = 0; i < N; ++i ) indexes[i] = 1;

    do {
        for( size_t i = 0; i < N; ++i ) std::cout << indexes[i] << ' ';
        std::cout << std::endl;
    } while( inc( indexes, 6, N-1 ) );
    return 0;
}

live example

Upvotes: 2

Samuel
Samuel

Reputation: 686

Same with Processing (java) here :

void loopFunction(int targetLevel, int actualLevel, int min, int max, String prefix){
  /*
    targetLevel is the wanted level (20 in your case)
    actualLevel starts from 1
    min starts from 1
    max is the max number displayed (6 in your case)
    prefix starts from blank
    see usage bellow (in setup function)
  */
  for(int m=min; m<max; m++) {
    if(targetLevel==actualLevel)
    {
      println(prefix+ " " + m);
    }
    else
    {
      loopFunction(targetLevel, actualLevel+1,m,max,prefix+ " " + m);
    }
  }
}

void setup(){

  loopFunction(10,1,1,6,"");

}

Upvotes: 0

Corinna
Corinna

Reputation: 165

This function works for me, but do not call it with 20 if you want it to finish.

#include <list>
#include <iostream>

std::list<std::list<int>> fun (std::list<std::list<int>> inputlist, int counter)
{
    if(counter == 0)
    {
        return inputlist;
    }
    else
    {
        std::list<std::list<int>> outputlist;
        for(std::list<int> oldlist : inputlist)
        {
            for(int i = 1; i<6; i++)
            {
                std::list<int> newlist = oldlist;
                newlist.push_back(i);
                outputlist.push_back(newlist);
            }
        }
        return fun(outputlist, counter - 1);
    }
}

int main()
{
    std::list<int> somelist;
    std::list<std::list<int>> listlist;
    listlist.push_back(somelist);
    std::list<std::list<int>> manynumbers = fun (listlist,5);
    for (std::list<int> somenumbers : manynumbers)
    {
        for(int k : somenumbers)
        {
            std::cout<<k;
        }
        std::cout<<std::endl;
    }
    return 0;
}

Upvotes: 0

Related Questions