【问题标题】:Recursive Algorithm into Iterative递归算法到迭代
【发布时间】:2016-06-14 11:11:17
【问题描述】:

如何将下面的递归算法变成迭代算法?

count(integer: n)
  for i = 1...n
    return count(n-i) + count(n-i)
  return 1

该算法基本上计算以下内容:

count(n-1) + count(n-2) + ... + count(1)

【问题讨论】:

  • count (integer: n) return 2^n-1
  • 我应该澄清一下,不使用 2^n-1
  • 这不是尾递归算法,因此将其转换为迭代虽然可能,但并非易事。
  • 1<<n - 12^n - 1 相同并且“不使用2^n -1
  • 该算法没有意义。您从 aloop 的第一次迭代返回。这应该是if n > 1 而不是for i to n

标签: algorithm recursion iteration


【解决方案1】:

这不是尾递归,因此将其转换为迭代并非易事。

但是,可以很容易地使用堆栈和循环来模拟递归,方法是压入堆栈而不是递归。

stack = Stack()
stack.push(n)
count = 0
while (stack.empty() == false):
  current = stack.pop()
  count++
  for i from current-1 to 1 inclusive (and descending):
     stack.push(i)
return count

另一个解决方案是使用Dynamic Programming,因为您不需要多次计算同一件事:

DP = new int[n+1]
DP[0] = 1
for i from 1 to n:
  DP[i] = 0
  for j from 0 to i-1:
    DP[i] += DP[j]
return DP[n]

请注意,您甚至可以通过记住“到目前为止的总和”来优化它以在 O(n) 而不是 O(n^2) 中运行:

sum = 1
current = 1
for i from 1 to n:
  current = sum
  sum = sum + current
return current

最后,这实际上总结为您可以轻松预先计算的内容:count(n) = 2^(n-1), count(0) = 1(您可以通过查看我们拥有的最后一个迭代解决方案来怀疑它......)

base: count(0) 自动产生 1,因为没有到达循环体。
假设:T(k) = 2^(k-1) 代表所有 k < n

证明:

T(n) = T(n-1) + T(n-2) + ... + T(1) + T(0) = (induction hypothesis)
     = 2^(n-2) + 2^(n-3) + ... + 2^0 + 1 = 
     = sum { 2^i | i=0,...,n-2 } + 1 = (sum of geometric series)
     = (1-2^(n-1)/(1-2)) + 1 =  (2^(n-1) - 1) + 1 = 2^(n-1)

【讨论】:

  • 证明中有一些奇怪的地方:你替换 T(0)=1 但 T(n)=2^(n-1) 如果是这样的话,那么 T(0)=2^ (0-1)=1/2。对吗?
  • 我认为这应该是公认的答案。 OP似乎对他想要什么感到困惑!
  • @mtk99 答案明确表示T(0)=1, T(n)=2^(n-1) n>0。作为直觉,具有非整数值是没有意义的。证明正是使用了这个假设,并显示了正确性。
  • @amit 如果您查看 OP 的代码,您会看到 T(1)=2。他后来所说的关于正在计算的内容是不正确的。该证明不适用于他的一段代码。
【解决方案2】:

如果您以以下递归方式定义您的问题:

count(integer : n)
    if n==0 return 1 
    return count(n-1)+count(n-1)

转换为迭代算法是反向归纳的典型应用,您应该保留所有先前的结果:

count(integer : n):
  result[0] = 1

  for i = 1..n
     result[i] = result[i-1] + result[i-1]

  return result[n]

Ir 很清楚,这比应有的复杂得多,因为重点是举例说明反向归纳。我可以累积到一个地方,但我想提供一个更一般的概念,可以扩展到其他情况。在我看来,这样的想法更清晰。

核心思想明确后,可以改进伪代码。事实上,有两个非常简单的改进只适用于这种特定情况:

  • 不需要保留所有以前的值,只需要最后一个值
  • 不需要两个相同的调用,因为没有预期的副作用

超越,可以根据函数的定义来计算,count(n)= 2^n

【讨论】:

    【解决方案3】:

    语句return count(n-i) + count(n-i) 似乎等同于return 2 * count(n-i)。在这种情况下:

    count(integer: n)
      result = 1
      for i = 1...n
        result = 2 * result
      return result
    

    我在这里错过了什么?

    【讨论】:

    • 哇!您的意思是我们进行一次递归调用并返回其返回值的双倍,而不是进行 2 次递归调用并返回它们的返回值的总和?它们如何等效?
    • @KedarMhaswade 除非f() 是有状态的,否则f(x) + f(x)2 * f(x) 在算术上是等价的?请举一个反例,我会很着迷!
    • 你是对的,@pjs。我只是错过了return!我一定是多任务处理了 :( -- 抱歉。
    猜你喜欢
    • 1970-01-01
    • 2011-02-08
    • 1970-01-01
    • 2012-10-05
    • 1970-01-01
    • 2011-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多