-1

Could anybody help me turning this algorithm into an iterative one. I know that recursion is just iteration plus a stack but I did not manage to come up with a proper solution so far.

void rGenCombs(int n, int k, vector<int>& chosen, int idx,
               vector<vector<int>>& combs) {
    if (chosen.size() == k) {
        combs.push_back(chosen);
        return;
    }
    for (int i = idx; i <= n; ++i) {
        chosen.push_back(i);
        rGenCombs(n, k, chosen, i + 1, combs);
        chosen.pop_back();
    }
}

vector<vector<int>> genCombsRec(int n, int k) {
    vector<vector<int>> combs;
    vector<int> chosen;
    rGenCombs(n, k, chosen, 1, combs);
    return combs;
}

Update I have this right now. Problem is I cannot figure out which loops to write. Should be doable somehow with a simple while loop I guess.

vector<vector<int>> genCombs(int n, int k) {
    vector<int> numStack, chosen;
    vector<vector<int>> combs;
    numStack.push_back(1);
    while (!numStack.empty()) {
        if (chosen.size() == k) {
            combs.push_back(chosen);
            chosen.pop_back();
            continue;
        }
        chosen.push_back(numStack.back());
        if (numStack.back() <= n) {
            numStack.push_back(numStack.back() + 1);
        } else {
            numStack.pop_back();
        }
    }
    return combs;
}

Solutions

For the different iterative algorithm without the need of a stack I ended up with the following:

int getNextIncIndex(const vector<int>& combs, int n) {
    int k = static_cast<int>(combs.size());
    for (int i = k - 1; i >= 0; --i) {
        int distFromRight = k - i - 1;
        if (combs[i] < n - distFromRight) {
            return i;
        }
    }
    return -1;
}

vector<vector<int>> genCombs(int n, int k) {
    vector<vector<int>> combs;
    vector<int> comb(k, 1);
    iota(comb.begin(), comb.end(), 1);
    while (true) {
        for (int i = comb[k - 1]; i <= n ; ++i) {
            comb[k - 1] = i;
            combs.push_back(comb);
        }
        int incIdx = getNextIncIndex(comb, n);
        if (incIdx == -1) {
            break;
        } else {
            iota(comb.begin() + incIdx, comb.end(), comb[incIdx] + 1);
        }
    }
    return combs;
}
Nils
  • 13,319
  • 19
  • 86
  • 108

2 Answers2

2

I won't give you the answer, but I'll give you the key trick for a way to do it reasonably mechanically.

Part of your mental block is that you have two types of flow of control intertwining. The first is your for loop. The second is recursion. How do you break out of the inside of your for loop to go to the outside loop and recurse, only to come back inside of your for loop? It is easy to get confused.

But instead introduce not one, but two stacks. One stack is to keep track of actions that you need to do. The other is for all the data in your call frames. The key part of your final code can look like this:

while (0 < actions.size()) {
    action thisAction = actions.pop_back();
    switch (thisAction.type) {
        case ENTER_FUNCTION:
            ...
            break;
        case ENTER_LOOP:
            ...
            break;
        case EXIT_LOOP:
            ...
            break;
        case EXIT_FUNCTION:
            ...
            break;
    }
}

And now you are keeping track of both your loop and your function calls in a unified way. No more confusion.

Here is what you do in each section.

  • ENTER_FUNCTION: Check your if, decide if you'll have a loop and if you will then set it to start and append ENTER_LOOP on your action stack. (If you won't loop, then do the if.)
  • ENTER_LOOP: Test the loop condition. If it matches, set up the loop, and append ENTER_LOOP, EXIT_LOOP, EXIT_FUNCTION, ENTER FUNCTION on the action stack. (Note that the last item in the stack will happen first.) Then add the arguments for the function call on the call stack so that they are there when you make the recursive call.
  • EXIT_LOOP: Do the chosen.pop_back() and increment the i that is part of your current call frame. (This is important, call frames need to be kept separate!)
  • EXIT_FUNCTION: Get rid of the top call frame, it is done.

Once you think that you understand this strategy, go learn Forth. :-)

btilly
  • 43,296
  • 3
  • 59
  • 88
  • Thanks for your answer I'll try this approach when I have time. – Nils Oct 02 '18 at 08:00
  • @Nils I made some minor edits, fixed a bug. You may wish to read the revised example. The biggest change is that when you successfully enter the loop you need to put an `ENTER_LOOP` on the action stack so that the next loop iteration happens. – btilly Oct 02 '18 at 21:08
1

If you just need an iterative algorithm, I think you are looking in a wrong direction. There is no need to have a stack really.

If you for whatever reason do want a stack, please disregard the rest.

For illustrative purposes I run your code with n=6, k=3:

1 2 3 
1 2 4 
1 2 5 
1 2 6 
1 3 4 
1 3 5 
1 3 6 
1 4 5 
1 4 6 
1 5 6 
2 3 4 
2 3 5 
2 3 6 
2 4 5 
2 4 6 
2 5 6 
3 4 5 
3 4 6 
3 5 6 
4 5 6 

You can see a simple pattern, which leads to the simple algorithm:

  • Take the last position. Increment it. That will give the next combination.

  • Once you hit a ceiling, that is this position cannot be incremented anymore, increment the next "incrementable" position, and std::iota everything to the right.

  • Start over, and keep going until there is no more an incrementable position.

A very dirty - but working - implementation, with a plenty of room to enhance:

#include <numeric>

int find_incrementable(std::vector<int>& current, int n)
{
    int pos;
    current.push_back(n + 1);   // Dirty hack
    for (pos = current.size() - 2; pos >= 0; --pos) {
        if (current[pos] + 1 < current[pos + 1]) {
            break;
        }
    }
    current.pop_back();
    return pos;
}

std::vector<std::vector<int>> genCombsIter(int n, int k)
{
    std::vector<std::vector<int>> combs;
    std::vector<int> current(k);
    std::iota(current.begin(), current.end(), 1);
    combs.push_back(current);

    int position = k - 1;
    int incrementable;
    while ((incrementable = find_incrementable(current, n)) >= 0) {
        if (incrementable == position) {
            current[position] += 1;
        } else {
            if (incrementable == -1) {
                break;
            }
            std::iota(current.begin() + incrementable, current.end(), current[incrementable] + 1);
            position = k - 1;
        }
        combs.push_back(current);
    }
    return combs;
}
user58697
  • 7,808
  • 1
  • 14
  • 28
  • Thanks for your answer. Lesson learned: Instead of a 1:1 transformation to iterative algorithm ALWAYS LOOK AT AN EXAMPLE and try to understand the pattern exactly could lead to an easier solution. – Nils Oct 02 '18 at 08:02
  • I wounder about the naming what does iota actually stand for? I have to memorize it somehow. – Nils Oct 02 '18 at 08:03
  • @Nils AFAIK, `iota` goes all the way back to the [APL](http://www.thocp.net/software/languages/apl.htm), where this operation was represented literally by the greek letter. How did Iverson came up with it I have no clue. – user58697 Oct 02 '18 at 17:42
  • 1
    There is more about the naming of `iota` at https://stackoverflow.com/questions/9244879/what-does-iota-of-stdiota-stand-for. As for the transformation, it is worth it to learn both approaches. Understanding the example leads to clearer solutions. Understanding how to transform it mechanically helps you get to an understanding what your programming language does internally. It is good to have both tools in your toolbelt. :-) – btilly Oct 02 '18 at 18:29