【问题标题】:From recursive algorithm to bottom up dynamic programming approach从递归算法到自下而上的动态规划方法
【发布时间】:2019-08-28 03:58:21
【问题描述】:

我有一个递归算法,可以在其中计算一些概率值。输入是一个整数列表和一个整数值,表示一个常量值。

例如,p([12,19,13], 2) 进行了三个递归调用,分别是

p([12,19],0)p([13], 2)

p([12,19],1)p([13], 1)

p([12,19],2)p([13], 0)

因为 2 可以分解为 0+2、1+1 或 2+0。然后每个调用都遵循类似的方法并进行其他几个递归调用。

我有的递归算法

limit = 20
def p(listvals, cval):
    # base case
    if len(listvals) == 0:
        return 0

    if len(listvals) == 1:
        if cval == 0:
            return listvals[0]/limit
        elif listvals[0] + cval > limit:
            return 0
        else:
            return 1/limit

    result = 0
    for c in range(0,cval+1):
        c1 = c
        c2 = cval-c
        listvals1 = listvals[:-1]
        listvals2 = [listvals[-1]]
        if listvals[-1] + c2 <= limit:
            r = p(listvals1, c1) * p(listvals2, c2)
            result = result+r

    return result

我一直在尝试将其转换为自下而上的 DP 代码,但无法弄清楚我需要进行迭代的方式。

我写下了最终结果需要计算的所有中间步骤,很明显递归调用的底部有很多重复。

我尝试创建一个预先计算值的字典,如下所示

m[single_value]=[list of calculated values] 

并使用这些值而不是进行第二次递归调用 p(listvals2, c2),但就运行时间而言,它并没有多大帮助。

如何使用适当的自下而上的方法来提高运行时间?

【问题讨论】:

    标签: python algorithm recursion dynamic-programming bottom-up


    【解决方案1】:

    不确定我是否理解您的程序想要计算的内容,所以对此无能为力,也许可以再解释一下?

    关于提高性能,您只缓存递归调用中重复计算的叶节点。更好的方法是将函数 p 的第一个参数作为元组而不是列表,然后使用 p 的两个参数的元组作为字典中的缓存键。

    Python 的标准库 functools 提供了一种简单的方法来完成这个相当常见的部分。

    from functools import wraps
    
    def cached(func):
      cache = {}
      @wraps(func)
      def wrapped(listvals, cval):
        key = (listvals, cval)
        if key not in cache:
            cache[key] = func(key)
        return cache[key]
      return wrapped
    

    使用这个装饰器缓存所有调用函数:

    @cached
    def p(listvals, cval):
    

    现在让您的 p 使用元组而不是列表:

    p((12,19,13), 2) 
    

    【讨论】:

      猜你喜欢
      • 2021-07-06
      • 2014-05-20
      • 1970-01-01
      • 2013-10-05
      • 1970-01-01
      • 1970-01-01
      • 2014-05-22
      • 2023-03-23
      • 2019-07-26
      相关资源
      最近更新 更多