为什么三元迭代比递归慢2倍?

时间:2015-06-08 11:20:20

标签: python performance recursion

为了解决我的问题,我需要将所有可能的长度为N的数组放在一起,包含-1,0和1,并通过某个函数运行它们以查看它们是否符合某个标准。

我已经实现了2个方法,一个三元数方法和一个递归。它们都返回正确的结果,都检查相同数量的数组,没有红旗除外,在我的机器上,三元方法几乎慢了2倍。这有什么理由可以吗? printIfTargetHit是一个简单的数字函数,没有缓存或其他可以解释这个问题的复杂功能。

def triadicIteraction(signsQty):
    signs = [None for _ in range(signsQty)]
    comb = int(3**signsQty-1)
    while (comb >= 0):
        combCopy = comb
        for n in range(signsQty):
            signs[n] = 1-combCopy%3 # [0,1,2] -> [1,0,-1], correct range for signs
            combCopy = combCopy//3
        printIfTargetHit(signs)
        comb = comb - 1

def recursiveIteration(signsQty):
    def recursiveIterationInner(signs, newSign, currentOrder):
        if (currentOrder >= 0):
            signs[currentOrder] = newSign
        if currentOrder == (len(signs) - 1):
            printIfTargetHit(signs)
        else:
            recursiveIterationInner(signs,-1, currentOrder+1)
            recursiveIterationInner(signs, 0, currentOrder+1)
            recursiveIterationInner(signs, 1, currentOrder+1)
    recursiveIterationInner(signs = [None for _ in range(signsQty)], newSign = None, currentOrder = -1)

完整代码在github上,https://github.com/Yulia5/workspace/blob/master/P2/P3/PlusesMinuses1ToN.py,我没有发布所有内容,因为我相信上面的例子是自给自足的。

性能输出,时间计算为datetime.datetime.now()之间的差异,第一种方法是简单的嵌入式循环。

Method name: <function embeddedLoopsFixed at 0x01D63D30>
Combination quantity : 16560
Combinations evaluated : 14348907
Time elapsed : 0:01:56.440000
Method name: <function recursiveIteration at 0x01D63DB0>
Combination quantity : 16560
Combinations evaluated : 14348907
Time elapsed : 0:02:20.526000
Method name: <function triadicIteraction at 0x01D63D70>
Combination quantity : 16560
Combinations evaluated : 14348907
Time elapsed : 0:04:12.297000

1 个答案:

答案 0 :(得分:2)

问题是您每次都在''进行O(n)操作:

triadicIteraction

要看到这一点,您可以使用标准库中的 for n in range(signsQty): signs[n] = 1-combCopy%3 # [0,1,2] -> [1,0,-1], correct range for signs combCopy = combCopy//3 模块并实现该功能,以便每个计算都在一个单独的函数中发生(此处名为profile):

calSigns

然后:

def triadicIteractionGranular(signsQty):
    signs = [None for _ in range(signsQty)]
    comb = int(3**signsQty-1)

    def calSigns(combCopy):
        for n in xrange(signsQty):
            signs[n] = 1-combCopy%3 # [0,1,2] -> [1,0,-1], correct range for signs
            combCopy = combCopy//3

    while (comb >= 0):
        calSigns(comb)
        printIfTargetHit(signs)
        comb = comb - 1

其他功能的配置文件看起来很相似,没有>> profile.run('runCombinationCheckingMethod(triadicIteractionGranular, [], {"signsQty" : 15})') Method name: <function triadicIteractionGranular at 0x10f466848> Combination quantity : 16560 Combinations evaluated : 14348907 Time elapsed : 0:02:34.560533 43394489 function calls in 154.561 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 154.561 154.561 <ipython-input-1-bf48bf6dbc36>:119(runCombinationCheckingMethod) 264960 0.068 0.000 0.068 0.000 <ipython-input-1-bf48bf6dbc36>:18(onesZerosToChars) 16560 0.355 0.000 0.488 0.000 <ipython-input-1-bf48bf6dbc36>:27(printEqualityAsString) 14348907 66.392 0.000 66.392 0.000 <ipython-input-1-bf48bf6dbc36>:33(evaluate) 14348907 8.279 0.000 75.159 0.000 <ipython-input-1-bf48bf6dbc36>:54(printIfTargetHit) 16561 0.024 0.000 0.024 0.000 <ipython-input-1-bf48bf6dbc36>:7(combinationNumber) 1 10.580 10.580 154.560 154.560 <ipython-input-26-037769fa8fac>:1(triadicIteractionGranular) **** Note this line **** 14348907 68.822 0.000 68.822 0.000 <ipython-input-26-037769fa8fac>:5(calSigns) 1 0.000 0.000 154.561 154.561 <string>:1(<module>) 2 0.000 0.000 0.000 0.000 {built-in method now} 16560 0.005 0.000 0.005 0.000 {len} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 16560 0.017 0.000 0.017 0.000 {method 'join' of 'str' objects} 16561 0.020 0.000 0.020 0.000 {range} 的费用。

解决方案

有一种方法可以实现calSign,而不会每次都产生triadicIteraction费用:

O(n)

这应避免def triadicIteractionFirstPrinciple(signsQty): signs = [-1 for _ in range(signsQty)] comb = int(3**signsQty-1) def addOne(idx=0): if signs[idx] < 1: signs[idx] += 1 else: signs[idx] = -1 addOne(idx+1) while (comb >= 0): printIfTargetHit(signs) if comb > 0: addOne() comb = comb - 1 费用,并为您提供与其他实施相当的结果。

在我的系统上:

O(n)