Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ : dynamic number of nested for loops (without recursion)

I'm writing a code segment that iterates through every permutation of n digits. So for example, if n = 3, I would want to iterate through each of the following elements:

0, 0, 0

...

0, 1, 0

...

1, 0, 0

...

2, 3, 4

...

9, 9, 9

This is very easy to code using nested for loops:

for(digit1 0 to 9)
    for(digit2 0 to 9)
        for(digit3 0 to 9)

But I want to generalize this for n digits. If for example n = 10, I now need 10 nested for loops.

I've thought about this and realized that the problem can be solved using recursion (depth first search through a tree, with each node having 10 children, 0 to 10, and stopping at depth n). But I'm aiming for high performance so I don't want to use recursion due to the overhead. What other alternatives do I have?

like image 898
user1299784 Avatar asked Sep 11 '13 04:09

user1299784


3 Answers

If you want to simulate nested loops with a single one without using recursion, you can do so by maintaining a set of states (or slots) for each looping variable, which can be easily done with an array. Looping then turns into a simple matter of "adding 1" to that array, performing the carry operations as needed. If your nesting depth is n, and your maximum boundary for each loop is b, then the runtime of this is O(b^n), because the carry operations will only cost you at most O(b^n) (I'll skip the algebra here).

Here is the working C++ code (updated to integrate Drew's comment):

void IterativeNestedLoop(int depth, int max)
{
    // Initialize the slots to hold the current iteration value for each depth
    int* slots = (int*)alloca(sizeof(int) * depth);
    for (int i = 0; i < depth; i++)
    {
        slots[i] = 0;
    }

    int index = 0;
    while (true)
    {
        // TODO: Your inner loop code goes here. You can inspect the values in slots

        // Increment
        slots[0]++;

        // Carry
        while (slots[index] == max)
        {
            // Overflow, we're done
            if (index == depth - 1)
            {
                return;
            }

            slots[index++] = 0;
            slots[index]++;
        }

        index = 0;
    }
}
like image 59
David Airapetyan Avatar answered Sep 22 '22 23:09

David Airapetyan


If you want the permutation for all the digits for a specific length;as you have shown example of 3 digits. Instead of running 3 nested loops, run a single loop of 10^3 which will give you all the permutations.

Split the number obtained into digits in each iteration if you want to use it for indexing.

Thus you will be needing just one loop rather than nested loops.

like image 3
Saksham Avatar answered Sep 20 '22 23:09

Saksham


In genreral case if you like to replace recursion to flat code you should use the stack (LIFO). So if you have recursive algorithm:

void print(std::string str, int depth)
{
  if (depth == n) {
    std::cout << str << std::endl;
    return;
  }

  for (int i = 0; i < 10; ++i) {
    char val[2] = { i + '0', 0 };
    print(str + val + ", ", depth+1);
  }
}

You can transform it to LIFO-based with saving local variables (str and i in this case):

struct StackItem {
  StackItem(const std::string& ss, unsigned ii)
    : str(ss), i(ii)
    {}
  std::string str;
  int i;
};

void print_norec()
{
  std::list< StackItem > stack;
  stack.push_back(StackItem("", 0));
  while (!stack.empty()) {
    StackItem& current = stack.back();
    if (stack.size() == n+1) {
      std::cout << current.str << std::endl;
      stack.pop_back(); // return from "recursive" function
      continue;
    }
    if (current.i < 10) {
      char val[2] = { current.i + '0', 0 };
      // call "recursive" function
      stack.push_back(StackItem(current.str + val + ", ", 0)); 
      current.i++;
    } else {          
      stack.pop_back(); // return from "recursive" function
    }
  }
}
like image 3
Dmitry Poroh Avatar answered Sep 23 '22 23:09

Dmitry Poroh