Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic nested loops

Tags:

c++

algorithm

I am working on a N dimensional grid.
I would like to generate nested loops depending on any dimension (2D, 3D, 4D, etc...).
How can I do that in an elegant and fast way ? Below a simple illustration of my problem. I am writing in C++ but I think this kind of question can be useful for other languages.
I need to know the indices (i,j,k...) in my do stuff part. Edit : lower_bound and upper_bound represents the indexes in the grid so they are always positive.

#include <vector>

int main()
{
    // Dimension here is 3D
    std::vector<size_t> lower_bound({4,2,1});
    std::vector<size_t> upper_bound({16,47,9});

    for (size_t i = lower_bound[0]; i < upper_bound[0]; i ++)
        for (size_t j = lower_bound[1]; j < upper_bound[1]; j ++)
            for (size_t k = lower_bound[2]; k < upper_bound[2]; k ++)
                // for (size_t l = lower_bound[3]; l < upper_bound[3]; l ++)
                //  ...
                {
                    // Do stuff such as
                    grid({i,j,k}) = 2 * i + 3 *j - 4 * k;
                    // where grid size is the total number of vertices
                }
}
like image 925
coincoin Avatar asked Dec 19 '14 14:12

coincoin


2 Answers

Following may help:

bool increment(
    std::vector<int>& v,
    const std::vector<int>& lower,
    const std::vector<int>& upper)
{
    assert(v.size() == lower.size());
    assert(v.size() == upper.size());

    for (auto i = v.size(); i-- != 0; ) {
        ++v[i];
        if (v[i] != upper[i]) {
            return true;
        }
        v[i] = lower[i];
    }
    return false;
}

And use it that way:

int main() {
    const std::vector<int> lower_bound({4,2,1});
    const std::vector<int> upper_bound({6,7,4});
    std::vector<int> current = lower_bound;

    do {
        std::copy(current.begin(), current.end(), std::ostream_iterator<int>(std::cout, " "));
        std::cout << std::endl;
    } while (increment(current, lower_bound, upper_bound));
}

Live demo

like image 102
Jarod42 Avatar answered Nov 03 '22 01:11

Jarod42


An iterative approach could look like this:

#include <iostream>
#include <vector>

int main()
{
  std::vector<int> lower_bound({-4, -5, -6});
  std::vector<int> upper_bound({ 6,  7,  4});

  auto increase_counters = [&](std::vector<int> &c) {
    for(std::size_t i = 0; i < c.size(); ++i) {
      // This bit could be made to look prettier if the indices are counted the
      // other way around. Not that it really matters.
      int &ctr    = c          .rbegin()[i];
      int  top    = upper_bound.rbegin()[i];
      int  bottom = lower_bound.rbegin()[i];

      // count up the innermost counter
      if(ctr + 1 < top) {
        ++ctr;
        return;
      }

      // if it flows over the upper bound, wrap around and continue with
      // the next.
      ctr = bottom;
    }

    // end condition. If we end up here, loop's over.
    c = upper_bound;
  };

  for(std::vector<int> counters = lower_bound; counters != upper_bound; increase_counters(counters)) {
    for(int i : counters) {
      std::cout << i << ", ";
    }
    std::cout << "\n";
  }
}

...although whether this or a recursive approach is more elegant rather depends on the use case.

like image 36
Wintermute Avatar answered Nov 03 '22 01:11

Wintermute