我设法以递归方式编写算法:
int fib(int n) {
if(n == 1)
return 3
elseif (n == 2)
return 2
else
return fib(n – 2) + fib(n – 1)
}
目前我正试图将其转换为迭代方法而没有成功:
int fib(int n) {
int i = 0, j = 1, k, t;
for (k = 1; k <= n; ++k)
{
if(n == 1) {
j = 3;
}
else if(n == 2) {
j = 2;
}
else {
t = i + j;
i = j;
j = t;
}
}
return j;
}
那么如何纠正我的代码以实现我的目标呢?
答案 0 :(得分:6)
通过一般的转换迭代来解决这个问题是一个坏主意。但是,这就是你的要求。
这些都不是解决fib
的好方法:fib
有封闭式解决方案,和/或是更清晰,和/或递归的备忘解决方案的迭代解决方案。相反,我展示了相对机械技术来获取递归函数(不是尾递归或其他简单解决),并在不使用自动存储堆栈的情况下解决它(递归)。
我的代码在递归嵌套方面做得太深,并且在中高复杂度情况下会使堆栈崩溃;当重构为迭代时,问题就消失了。当你拥有的是一半理解的递归解决方案时,需要这些解决方案,并且你需要它是迭代的。
将递归转换为迭代解决方案的一般方法是手动管理堆栈。
在这种情况下,我还会记住返回值。
我们将返回值缓存在retvals
。
如果我们不能立即解决问题,我们说明为了解决问题我们首先需要解决的问题(特别是n-1和n-2情况)。然后我们排队再次解决我们的问题(到那时,我们将准备好我们需要的东西)。
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];
}
没有递归,只是一个循环。
A&#34; dumber&#34;这样做的方法是获取函数并使其成为伪协程。
首先,重写你的递归代码,每行做一件事:
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;
}
接下来,创建一个包含所有功能的结构&#39;状态:
struct fib_data {
int n, a, b, r;
};
并在我们进行递归调用的每个点添加标签,并使用类似名称的枚举:
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;
}
将CALLS
添加到您的fib_data
。
接下来创建一个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;
}
现在创建一个循环。而不是递归调用,在您的 fib_data
中设置返回位置,将fib_data
推送到具有n
和e0
位置的堆栈,然后继续循环。在循环的顶部,打开堆栈顶部的位置。
要返回:创建一个函数局部变量r来存储返回值。要返回,请设置r
,弹出堆栈,然后继续循环。
如果在循环开始时堆栈为空,则从函数返回r
。
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;
}
}
然后请注意,b
和r
不必在堆栈中 - 将其删除,并将其设置为本地。
这个&#34;哑巴&#34;转换模拟了递归时C ++编译器的作用,但堆栈存储在免费存储中而不是自动存储中,并且可以重新分配。
如果需要保留指向局部变量的指针,则使用std::vector
堆栈将无法正常工作。将具有偏移量的指针替换为标准向量,它将起作用。
答案 1 :(得分:3)
这应该是fib(0)= 0,fib(1)= 1,fib(2)= 1,fib(3)= 2,fib(4)= 3,fib(5)= 5,fib( 6)= 8,......
fib(n)
{
int f0, f1, t;
if(n < 2)
return n;
n -= 2;
f0 = 1;
f1 = 1;
while(n--){
t = f1+f0;
f0 = f1;
f1 = t;
}
return f1;
}
或者你可以稍微展开循环,并摆脱temp变量:
int fib(int n)
{
int f0, f1;
if(n < 2)
return n;
f0 = 1-(n&1);
f1 = 1;
while(0 < (n -= 2)){
f0 += f1;
f1 += f0;
}
return f1;
}
答案 2 :(得分:2)
这是一个经典问题。你不能简单地摆脱递归,如果给你n并且你想要计算下来。
解决方案是动态编程。基本上你想创建一个大小为n的数组,然后从索引0开始填充它直到你达到索引n-1;
类似的东西:
int fib(int n)
{
int buffer[n+1];
buffer[0]=3;
buffer[1]=2;
for(int i=2;i<=n; ++i)
{
buffer[i] = buffer[i-1] + buffer[i-2];
}
return buffer[n];
}
或者为了节省内存而不使用大数组,你可以使用:
int fib(int n)
{
int buffer [2];
buffer[0] = 3;
buffer[1] = 2;
for(int i=3; i<=n; i++)
{
int tmp = buffer[0] + buffer[1];
buffer[0] = buffer[1];
buffer[1] = temp;
}
return buffer[1];
}
答案 3 :(得分:1)
为了完整起见,这里是具有O(1)空间复杂度的迭代解决方案:
int fib(n)
{
int i;
int a0 = 3;
int a1 = 2;
int tmp;
if (n == 1)
return a0;
for (i = 3; i <=n; i++ )
{
tmp = a0 + a1;
a0 = a1;
a1 = tmp;
}
return a1;
}