为什么后向递归比python中的前向递归执行得更快

时间:2014-05-07 18:32:02

标签: python recursion

我在Python中创建了一个算法,用于计算获得不同硬币面额的金额的方式:

@measure
def countChange(n, coin_list):
    maxIndex = len(coin_list)
    def count(n, current_index):        
        if n>0 and maxIndex>current_index:
            c = 0
            current = coin_list[current_index]
            max_coeff = int(n/current)      
            for coeff in range(max_coeff+1):
                c+=count(n-coeff*current, current_index+1)
        elif n==0: return 1
        else: return 0
        return c
    return count(n, 0)

我的算法使用索引来获取硬币面额,正如您所看到的,我的索引在我进入的每个堆栈帧中都在增加。我意识到算法也可以用这种方式编写:

@measure
def countChange2(n, coin_list):
    maxIndex = len(coin_list)
    def count(n, current_index):        
        if n>0 and 0<=current_index:
            c = 0
            current = coin_list[current_index]
            max_coeff = int(n/current)      
            for coeff in range(max_coeff+1):
                c+=count(n-coeff*current, current_index-1)
        elif n==0: return 1
        else: return 0
        return c
    return count(n, maxIndex-1)

这一次,索引正在减少我进入的每个堆栈帧。我比较了函数的执行时间,我得到了一个非常值得注意的区别:

print(countChange(30, range(1, 31)))
print(countChange2(30, range(1, 31)))

>> Call to countChange took 0.9956174254208345 secods.
>> Call to countChange2 took 0.037631815734429974 secods.

如果我甚至没有缓存结果,为什么算法的执行时间会有很大差异?为什么索引的增加顺序会影响执行时间?

2 个答案:

答案 0 :(得分:12)

根据我的理解,这与动态编程没有任何关系。只是扭转指数不应​​该做出“动态”的事情。

发生的事情是该算法输入敏感。尝试以相反的顺序输入输入。例如,

print(countChange(30, list(reversed(range(1, 31)))))
print(countChange2(30, list(reversed(range(1, 31)))))

正如一些排序算法对已经排序的数据非常快,而且反转数据非常慢,你在这里得到了那种算法。

在输入增加的情况下,countChange需要更多迭代才能得出最终答案,因此看起来要慢得多。但是,当输入减少时,性能特征会反转。

答案 1 :(得分:8)

数字组合并不大

原因是前进你必须探索所有可能性,但是当你倒退时你可以消除大块无效的解决方案,而不必实际计算它们

继续你打电话数500k次

向后退你的代码只会拨打30k电话来计算...

您可以通过记忆调用(或更改算法以不重复调用)来更快地完成这两项工作。