Solving this problem by a general convert-to-iterative is a bad idea. But, that is what you asked.
None of these are good ways to solve fib: there are closed form solutions for fib, and/or iterative solutions that are cleaner, and/or recursive memoized solutions. Rather, I'm showing relatively mechanical techniques for taking a recursive function (that isn't tail-recursive or otherwise simple to solve), and solving it without using the automatic storage stack (recursion).
I have had code that does too deep a recursive nesting and blows the stack in medium-high complexity cases; when refactored to iterative, the problem went away. These are the kinds of solutions required when what you have is a recursive solution you half understand, and you need it to be iterative.
The general means to convert a recursive to an iterative solution is to manage the stack manually.
In this case, I'll also memoize return values.
We cache the return values in retvals.
If we cannot immediately solve a problem, we state what problems we first need to solve in order to solve our problem (in particular, the n-1 and n-2 cases). Then we queue up solving our problem again (by which point, we will have what we need ready go).
int fib( int n ) {
std::map< int, int > retvals {
{1,3},
{2,2}
};
std::vector<int> arg;
arg.push_back(n);
while( !arg.empty() ) {
int n = arg.back();
arg.pop_back();
// have we solved this already? If so, stop.
if (retvals.count(n)>0)
continue;
// are we done? If so, calculate the result:
if (retvals.count(n-1)>0 && retvals.count(n-2)>0) {
retvals[n] = retvals[n-1] + retvals[n-2];
continue;
}
// to calculate n, first calculate n-1 and n-2:
arg.push_back(n); arg.push_back(n-1); arg.push_back(n-2);
}
return retvals[n];
}
No recursion, just a loop.
A "dumber" way to do this is to take the function and make it a pseudo-coroutine.
First, rewrite your recursive code to do one thing per line:
int fib(int n) {
if(n == 1)
return 3
if (n == 2)
return 2
int a = fib(n-2);
int b = fib(n-1);
return a+b;
}
Next, create a struct with all of the functions' state:
struct fib_data {
int n, a, b, r;
};
and add labels at each point where we make a recursive call, and an enum with similar names:
enum Calls {
e1, e2
};
int fib(int n) {
fib_data d;
d.n = n;
if(d.n == 1)
return 3
if (d.n == 2)
return 2
d.a = fib(n-2);
CALL1:
d.b = fib(n-1);
CALL2:
d.r = d.a+d.b;
return d.r;
}
add CALLS to your fib_data.
Next create a stack of fib_data:
enum Calls {
e0, e1, e2
};
struct fib_data {
Calls loc = Calls::e0;
int n, a, b, r;
};
int fib(int n) {
std::vector<fib_data> stack;
stack.push_back({n});
if(stack.back().n == 1)
return 3
if (stack.back().n == 2)
return 2
stack.back().a = fib(stack.back().n-2);
CALL1:
stack.back().b = fib(stack.back().n-1);
CALL2:
stack.back().r = stack.back().a + stack.back().b;
return stack.back().r;
}
now create a loop. Instead of recursively calling, set the return location in your fib_data, push a fib_data onto the stack with an n and an e0 location, then continue the loop. At the top of the loop, switch on the top of the stack's location.
To return: Create a function local variable r to store return values. To return, set r, pop the stack, and continue the loop.
If the stack is empty at the start of the loop, return r from the function.
enum Calls {
e0, e1, e2
};
struct fib_data {
int n, a, b, r;
Calls loc = Calls::e0;
};
int fib(int n) {
std::vector<fib_data> stack;
stack.push_back({n});
int r;
while (!stack.empty()) {
switch(stack.back().loc) {
case e0: break;
case e1: goto CALL1;
case e2: goto CALL2;
};
if(stack.back().n == 1) {
r = 3;
stack.pop_back();
continue;
}
if (stack.back().n == 2){
r = 2;
stack.pop_back();
continue;
}
stack.back().loc = e1;
stack.push_back({stack.back().n-2});
continue;
CALL1:
stack.back().a = r;
stack.back().loc = e2;
stack.push_back({stack.back().n-1});
continue;
CALL2:
stack.back().b = r;
stack.back().r = stack.back().a + stack.back().b;
r = stack.back().r;
stack.pop_back();
continue;
}
}
Then note that b and r do not have to be in the stack -- remove it, and make it local.
This "dumb" transformation emulates what the C++ compiler does when you recurse, but the stack is stored in the free store instead of automatic storage, and can reallocate.
If pointers to the local variables need to persist, using a std::vector for the stack won't work. Replace the pointers with offsets into the standard vector, and it will work.
if (n == 1) return 3? Why do you put3rather than1in the case wheren == 1?