python - 将分而治之递归算法转换为迭代版本

标签 python algorithm recursion dynamic-programming divide-and-conquer

我想将数组上的递归算法转换为迭代函数。它不是尾递归算法,有两个递归调用,后跟一些操作。 该算法是一种分而治之的算法,其中在每个步骤中将数组分成两个子数组,并且将某个函数 f 应用于前两个结果。实际上 f 很复杂,因此迭代算法应该使用函数 f,对于最小的工作示例,我使用了一个简单的加法。

下面是 python 中递归程序的最小工作示例。

import numpy as np

def f(left,right):
    #In practice some complicated function of left and right
    value=left+right
    return value

def recursive(w,i,j):
    if i==j:
        #termination condition when the subarray has size 1
        return w[i]
    else:
        k=(j-i)//2+i
        #split the array into two subarrays between indices i,k and k+1,j
        left=recursive(w,i,k)
        right=recursive(w,k+1,j)

        return f(left,right)

a=np.random.rand(10)
print(recursive(a,0,a.shape[0]-1))

现在如果我想迭代地写这个,我意识到我需要一个堆栈来存储中间结果,并且在每一步我都需要将 f 应用于堆栈顶部的两个元素。我只是不确定如何在不递归的情况下构建将元素放入堆栈的顺序。这是对解决方案的尝试,这当然不是最佳解决方案,因为似乎应该有一种方法可以删除第一个循环并仅使用一个堆栈:

def iterative(w):
    stack=[]
    stack2=[]
    stack3=[]
    i=0
    j=w.shape[0]-1
    stack.append((i,j))
    while (i,j)!=(w.shape[0]-1,w.shape[0]-1):
        (i,j)=stack.pop()
        stack2.append((i,j))
        if i==j:
            pass
        else:
            k=int(np.floor((j-i)/2)+i)
            stack.append((k+1,j))
            stack.append((i,k))
    while len(stack2)>0:
        (i,j)=stack2.pop()
        if i==j:
            stack3.append(w[i])
        else:
            right=stack3.pop()
            left=stack3.pop()
            stack3.append(f(left,right))
    return stack3.pop()

编辑:我感兴趣的真正问题是将不同大小的张量数组作为输入,运算 f 求解涉及这些张量的线性规划并输出一个新的张量。我不能简单地遍历初始数组,因为在这种情况下 f 的输出大小呈指数增长。这就是为什么我使用这种分而治之的方法来减小这个大小。递归程序工作正常,但对于大尺寸会显着减慢,这可能是由于 python 打开并跟踪的帧。

最佳答案

下面我将程序转换为使用延续 (then) 和蹦床 (run/recur)。它演化出一个线性迭代过程,不会溢出堆栈。如果您没有遇到堆栈溢出问题,这对解决您的特定问题没有多大帮助,但它可以教您如何展平分支计算。

将普通函数转换为连续传递样式的过程可能是机械的。如果您稍微眯起眼睛,就会发现该程序与您的程序有大部分相同的元素。内联注释并排显示代码 -

import numpy as np

def identity (x):
  return x

def recur (*values):
  return (recur, values)

def run (f):
  acc = f ()
  while type (acc) is tuple and acc [0] is recur:
    acc = f (*acc [1])
  return acc

def myfunc (a):
  # def recursive(w,i,j)
  def loop (w = a, i = 0, j = len(a)-1, then = identity):
    if i == j:                # same
      return then (w[i])      # wrap in `then`
    else:                     # same
      k = (j - i) // 2 + i    # same
      return recur \          # left=recursive(w,i,k)
        ( w
        , i
        , k
        , lambda left:
          recur               # right=recursive(w,k+1,j)
            ( w
            , k + 1
            , j
            , lambda right:
                then          # wrap in `then`
                  (f (left, right)) # same
            )
        )
  return run (loop)

def f (a, b):
    return a + b              # same

a = np.random.rand(10)        # same
print(a, myfunc(a))           # recursive(a, 0, a.shape[0]-1)

# [0.5732646  0.88264091 0.37519826 0.3530782  0.83281033 0.50063843 0.59621896 0.50165139 0.05551734 0.53719382]

# 5.208212213881435

关于python - 将分而治之递归算法转换为迭代版本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54255306/

相关文章:

python - 查找共同元素 (Amazon SDE-1)

Python:将字典的项目除以取决于第一个键的值

python - 如何在 Qlistview 中获取选中的项目?

python - 从标签中识别单词 'method' 并提取文本

c# - 使用广度优先遍历时如何保持显示树的间距?

algorithm - 快速船体最坏情况解释

python - ffmpeg 将多张图片放入不同的帧中

java - 通用日志解析器算法

java - 为什么我在使用最大整数值时会得到荒谬的值?

java - 在 Java 中创建一个 Hosoya 三角形