简体   繁体   English

如何将递归转换为迭代解决方案

[英]How to convert recursive to iterative solution

I've managed to write my algorithm in recursive way: 我设法以递归的方式编写算法:

int fib(int n) {
    if(n == 1)
        return 3
    elseif (n == 2)
        return 2
    else
        return fib(n – 2) + fib(n – 1)
}

Currently I'm trying to convert it to iterative approach without success: 目前,我正在尝试将其转换为迭代方法,但未成功:

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;
}

So how can I rectify my code to reach my goal? 那么,如何纠正我的代码以实现自己的目标?

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. 这些都不是解决fib好方法:没有针对fib封闭式解决方案,和/或更简洁的迭代解决方案,和/或递归的固定解决方案。 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 . 我们将返回值缓存在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). 如果我们不能立即解决问题,则说明要解决问题首先需要解决的问题(特别是n-1和n-2情况)。 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 . CALLS添加到您的fib_data

Next create a stack of 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;
}

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. 取而代之的递归调用,在设定的返回地点fib_data ,推fib_data到堆栈与ne0的位置,然后继续循环。 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. 要返回:创建一个函数局部变量r来存储返回值。 To return, set r , pop the stack, and continue the loop. 要返回,请设置r ,弹出堆栈,然后继续循环。

If the stack is empty at the start of the loop, return r from the function. 如果循环开始时堆栈为空,则从函数返回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;
  }
}

Then note that b and r do not have to be in the stack -- remove it, and make it local. 然后注意br不必在堆栈中-将其删除并放在本地。

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. 这种“笨拙”的转换模拟了递归时C ++编译器的工作,但是堆栈存储在免费存储中,而不是自动存储中,并且可以重新分配。

If pointers to the local variables need to persist, using a std::vector for the stack won't work. 如果需要保留指向局部变量的指针,则对堆栈使用std::vector无效。 Replace the pointers with offsets into the standard vector, and it will work. 将指针替换为标准向量中的偏移量,它将起作用。

This should be fib(0) = 0, fib(1) = 1, fib(2) = 1, fib(3) = 2, fib(4) = 3, fib(5) = 5, fib(6) = 8, ... . 应为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;        
}

or you can unfold the loop a bit, and get rid of the temp variable: 或者您可以稍微展开循环,然后删除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;
}

This is a classic problem. 这是一个经典的问题。 you can not simply get rid of the recursion if you are given n and you want to calculate down. 如果给定n并想要向下计算,则不能简单地摆脱递归。

the solution is Dynamic programming. 解决方案是动态编程。 basically you want to create an array of size n, then starting from index 0 fill it up until you reach index n-1; 基本上,您想创建一个大小为n的数组,然后从索引0开始填充它直到达到索引n-1;

something like this: 像这样的东西:

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];
}

alternatively to save memory and not use a big array you can use: 或者,为了节省内存而不使用大数组,可以使用:

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];

} }

For a sake of completeness here is the iterative solution with O(1) space complexity: 为了完整起见,这里是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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM