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

时间:2019-01-18 13:45:38

标签: 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打开并跟踪了框架。

1 个答案:

答案 0 :(得分:0)

下面,我将程序转换为使用延续(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