繁体   English   中英

使用记忆和递归时,如何改进计算帕斯卡三角形第N行的代码?

[英]How can I improve my code for calculating the Nth row of Pascals Triangle while using Memoization and Recursion?

我一直在修改递归,并决定使用它来计算Pascals Triangle的行。 我已经成功创建了一个生成Pascals Triangle的函数,该函数适用于n <= 7,但是效率非常低。 我知道生成Pascals Triangle的身份,但我对此并不真正感兴趣。 我想要一些改进下面代码的指导。

在大约n = 7之后,需要很长时间才能计算出来,这让我觉得我实现了我的记忆错误。

count = 0 
def Pascal(n):
    global count
    count += 1 
    pasc_list = [] 
    i = 0 
    j = i+1
    dictionary = {0:[1],1:[1,1]}

    if n in dictionary:
        return dictionary[n]
    else:
        pasc_list.append(1)
        while j < len(Pascal(n-1)):
            pasc_list.append(Pascal(n-1)[i] + Pascal(n-1)[j])
            i += 1 
            j = i + 1 
        pasc_list.append(1)
        dictionary[n] = pasc_list
    return pasc_list
a = Pascal(5)
print(a)
print(count)

对于n = 5,范围数已经是4694,而当n = 6时,范围数是75105,这是一个巨大的增长。 因此,如果有人可以帮助我降低示波器的制作速度,我将不胜感激!

要在Python中正确使用memo ,请使用可变的默认参数,通常命名为memo

count = 0 

def Pascal(n, memo={0:[1],1:[1,1]}):
    global count
    count += 1 

    pasc_list = [] 
    i = 0 
    j = i+1

    if n in memo:
        return memo[n]

    else:
        pasc_list.append(1)
        while j < len(Pascal(n-1)):
            pasc_list.append(Pascal(n-1)[i] + Pascal(n-1)[j])
            i += 1 
            j = i+1 

        pasc_list.append(1)
        memo[n] = pasc_list

    return pasc_list

a = Pascal(7)
print(a)
print(count)

输出:

c:\srv\tmp> python pascal.py
[1, 7, 21, 35, 35, 21, 7, 1]
70

您还应该将备忘录返回作为函数要做的第一件事:

def Pascal(n, memo={0:[1],1:[1,1]}):
    if n in memo:
        return memo[n]

    global count
    count += 1 

    pasc_list = [] 
    i = 0 
    j = i+1

    pasc_list.append(1)
    while j < len(Pascal(n-1)):
        pasc_list.append(Pascal(n-1)[i] + Pascal(n-1)[j])
        i += 1 
        j = i+1 

    pasc_list.append(1)
    memo[n] = pasc_list

    return pasc_list

输出:

c:\srv\tmp> python pascal.py
[1, 7, 21, 35, 35, 21, 7, 1]
6

您每次迭代都要调用Pascal函数3次(而且,每个函数都越来越多地调用它……)。 因此,您的最终复杂度为O(n!) 只需存储每个Pascal结果的计算:

count = 0 
def Pascal(n):
    global count
    count += 1 

    pasc_list = [] 
    i = 0 
    j = i+1
    dictionary = {0:[1],1:[1,1]}

    if n in dictionary:
        return dictionary[n]

    else:
        pasc_list.append(1)
        p = Pascal(n-1)  # <-------------------- HERE!!
        while j < len(p):
            pasc_list.append(p[i] + p[j])
            i += 1 
            j = i+1 

        pasc_list.append(1)
        dictionary[n] = pasc_list

    return pasc_list

a = Pascal(7)
print(a)
print(count)

将打印:

[1, 7, 21, 35, 35, 21, 7, 1]
7

递归非常准确,并且永远不要在循环语句中调用笨重的函数(像这样: while j < len(Pascal(n-1)): <-- it is VERY bad idea to write code this way! )。 为什么这么慢?

假设我们正在计算Pascal(3):

在P3中,我们在while称呼P2。 因此,我们为j [0] -index调用P2,在其中调用2次,然后在下一次迭代(j [1] -index)中调用3次。 在每个P2迭代中,我们将P1迭代称为3次(对于手动P1迭代,则称为4次)。 而且我们对P4重复了整个过程4次,对P5重复了16次,并且越来越多……这是您的代码是如此缓慢的时候。

备注无法弥补不良的算法。 在这种情况下,它并不能弥补任何问题。 考虑删除了记忆逻辑的代码的清理版本:

count = 0

def Pascal(n):
    global count

    count += 1

    pasc_list = [1]

    if n > 0:

        i = 0
        j = i + 1

        previous = Pascal(n - 1)

        while j < len(previous):
            pasc_list.append(previous[i] + previous[j])
            i += 1
            j = i + 1

        pasc_list.append(1)

    return pasc_list

a = Pascal(10)

print(a)
print(count)

(此解决方案,@ thebjorn或@vurmux都无法使用默认的Python堆栈分配达到Pascal(1000) 。)如果我们对@vurmux的可接受答案进行计时,并在上面进行挖掘,则将每行从0循环到900:

for n in range(900):
    print(Pascal(n))

结果是相同的:

> time python3 no-memoization.py > /dev/null
52.169u 0.120s 0:52.36 99.8%    0+0k 0+0io 0pf+0w
> time python3 memoization.py > /dev/null
52.031u 0.125s 0:52.23 99.8%    0+0k 0+0io 0pf+0w
>

如果我们采用上面的非记忆解决方案,只需添加Python自己的lru-cache装饰器:

from functools import lru_cache

count = 0

@lru_cache()
def Pascal(n):
# ...

我们获得了显着的(100倍)加速:

> time python3 lru_cache.py > /dev/null
0.556u 0.024s 0:00.59 96.6% 0+0k 0+0io 0pf+0w
>

就像我们使用@thebjorn(+1)解决方案一样,该解决方案可以正确地重用字典缓存,而不是在每次调用时重新分配它。

重定向到文件时,所有输出都是相同的。 (很多时候,我将functools:lru-cache添加到一个函数中只是为了发现它实际上放慢了速度-即不是此优化的理想选择。)

专注于编写一个好的算法,并尽可能地对其进行优化。 然后,研究诸如记忆化之类的技术。 但是要做些计时,看看它是否真的是一场胜利。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM