【问题标题】:Recursion Formula for Integer Partitions整数分区的递归公式
【发布时间】:2015-12-22 17:58:01
【问题描述】:

我编写了以下代码,用于使用涉及五边形数的递推公式评估整数分区:

def part(n):
    p = 0
    if n == 0:
        p += 1
    else:
        k = 1
        while ((n >= (k*(3*k-1)/2)) or (n >= (k*(3*k+1)/2))):
            i = (k * (3*k-1)/2)
            j = (k * (3*k+1)/2)
            if ((n-i) >= 0):
                p -= ((-1)**k) * part(n-i)
            if ((n-j) >= 0):
                p -= ((-1)**k) * part(n-j)
            k += 1
    return p

    n = int(raw_input("Enter a number: "))
    m = part(n)
    print m

代码在n=29 之前运行良好。 n=24 周围有点慢,但我仍然可以在不错的运行时内获得输出。我知道算法是正确的,因为产生的数字与已知值一致。

对于 35 以上的数字,即使等待了很长时间(大约 30 分钟),我也没有得到输出。我的印象是 python 可以处理比这里使用的数字大得多的数字。有人可以帮助我改善运行时间并获得更好的结果吗?另外,如果代码有问题,请告诉我。

【问题讨论】:

    标签: python recursion


    【解决方案1】:

    你可以使用Memoization:

    def memo(f):
        mem = {}
        def wrap(x):
            if x not in mem:
                mem[x] = f(x)
            return mem[x]
        return wrap
    
    @memo
    def part(n):
        p = 0
        if n == 0:
            p += 1
        else:
            k = 1
            while (n >= (k * (3 * k - 1) // 2)) or (n >= (k * (3 * k + 1) // 2)):
                i = (k * (3 * k - 1) // 2)
                j = (k * (3 * k + 1) // 2)
                if (n - i) >= 0:
                    p -= ((-1) ** k) * part(n - i)
                if (n - j) >= 0:
                    p -= ((-1) ** k) * part(n - j)
                k += 1
        return p
    

    演示:

    In [9]: part(10)
    Out[9]: 42
    
    In [10]: part(20)
    Out[10]: 627
    
    In [11]: part(29)
    Out[11]: 4565
    
    In [12]: part(100)
    Out[12]: 190569292
    

    通过 memoization,我们可以记住之前的计算,因此对于重复计算,我们只需在 dict 中进行查找。

    【讨论】:

    • 对于 n=500,我收到一个运行时错误,提示超出了最大递归深度。有没有办法解决这个问题?
    • @Nannu,你可以增加递归限制,但实际上你最好迭代地实现它。 Python 没有针对递归进行优化
    • 我可以达到 n =999,之后的任何事情都会破坏堆栈
    • 我知道递归并不是真正使用 python 的最佳方式,但我想使用递归获得答案;我有另一个不使用递归的公式,所以我想比较一下。你能解释一下如何增加递归限制吗?
    • 是的,我得到了 n=500 直到我得到错误。这是在记住函数之后
    【解决方案2】:

    你可以做很多事情。

    1. 删除重复计算。 - 基本上,每次执行 while 循环时,您都会多次计算“3 * k + 1”。您应该计算一次并将其分配给变量,然后使用该变量。

    2. 将 (-1)**k 替换为更快的操作,例如 -2*(k%2)+1)。因此,计算不是相对于 k 是线性的,而是恒定的。

    3. 缓存昂贵的确定性计算的结果。 “部分”是一个确定性函数。使用相同的参数多次调用它。您可以构建映射到结果的输入的哈希图。

    4. 考虑重构它以使用循环而不是递归。据我了解,Python 不支持尾递归,因此当您使用深度递归时,它不得不维护非常大的堆栈。

    如果您缓存计算,我可以保证它的运行速度会快很多倍。

    【讨论】:

    • 我认为缓存优化从我的回答中非常明显,迭代解决方案可能是真正改进缓存之外的 OP 自己的代码的唯一方法
    • @Adam,我知道递归并不是真正使用 python 的最佳方式,但我想使用递归获得答案;我还有一个不使用递归的公式,所以我想比较一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多