如何优化楼梯问题的python代码以使用较大的值?

时间:2019-06-29 10:35:29

标签: python optimization out-of-memory

我必须创建一个解决楼梯问题的程序,其中我必须设计n砖块数的楼梯。复杂性在于每个步骤中的砖块数量必须唯一,并且少于上一步。 例如,使用6块积木,我可以制作阶梯高度为(5,1) , (4,2) and (3,2,1)而不是(3,3) or (1,2,3) or (2,4)或任何其他排列的楼梯。

我已经设计了代码,但是问题在于,直到n为止,它可以正常运行接近100或120,但是如果输入大于这些值,则冻结。我是Python编程和学习概念的初学者。

我尝试了记忆,但无济于事。我需要知道是否还有其他方法可以使我的代码更优化以在n上以200-250的速度运行?

    import cProfile

    def solution(n):

        memory = {0: [], 1: [1], 2: [2]}

        def rec(max_val, i):

            t = []
            r = []

            for j in range(1,i):

                y = i - j

                if y < max_val:

                    if y > j:
                        t = [y, j]
                        r.append(t)

                        if n / 2 >= j >= 3 and j in memory:
                            mem = memory[j]
                            [r.append([y, item]) for item in mem]

                    else:
                        if y >= 3 and n / 2 >= j >= 3 and j in memory:
                            mem = memory[j]
                            for item in mem:
                                if y > item[0]:
                                    r.append([y, item])
                        else:
                            v = rec(y, j)
                            if v:
                                for item in v:
                                    t = [y, item]
                                    r.append(t)

            if r:
                if i in memory:
                    if len(memory[i]) < len(r):
                        memory[i] = r
                else:
                    memory[i] = r
            return r

        def main_func(n):

            stair = []
            max_val = 201
            total = 0
            for i in range (1,n):

                x = n - i

                if x > i:
                    s = [x, i]
                    total += 1

                    if i >= 3:
                        u = rec(max_val, i)
                        total += len(u)

                elif x == i and i >= 3:
                        u = rec(max_val, i)
                        total += len(u)

                elif x < i and i >= 3:
                        u = rec(x, i)
                        total += len(u)

            return total

        stairs = main_func(n)
        return (stairs)

    print(solution(100))

1 个答案:

答案 0 :(得分:0)

您可以从楼梯底部的角度递归解决问题。该策略是针对每个基本大小对下一步级别的模式计数进行累加。

例如,对于6个积木,第一个调用将遍历基本大小n = 5,4,3,2,然后进行递归调用,以了解使用剩余的积木和最大底数为n-1。下一级计数的总和将构成可能的楼梯样式的总数。

在楼梯的顶部,您至少需要3块砖才能添加一个以上的级别,这样,如果剩余的砖块少于3个,则可以停止递归计数,计数为1。这样,就可以将递归调用汇总起来,形成更大的总数,并在原始调用完成后产生正确的答案。

为了优化此过程,您可以使用备忘录,也可以使用每次递归提供的基本大小来缩短计算时间。

对于给定的基数,可以使用的最大块数将是1到该基数的和。可以使用高斯公式base*(base+1)/2计算得出。如果您所用的块数多于该基数的最大块数,则可以停止递归并返回零计数(因为剩余的块数太多,并且无法在先前级别的基数上全部容纳它们)< / p>

另一种优化计算的方法是按降序循环基本大小。这样一来,您可以在下一级别的计数为零时立即停止循环,这意味着该基本大小(或任何较小的基本大小)的剩余砖块过多

这是一个示例(使用lru_cache进行记忆):

from functools import lru_cache
@lru_cache(None)
def stairCount(N,base=None):
    base = min(base or N-1,N)
    if N > base*(base+1)//2: return 0
    if N < 3: return 1
    count = 0
    while True:
        nextLevels = stairCount(N-base,base-1)
        if nextLevels == 0: break
        count += nextLevels
        base   = base - 1
    return count

通过这些优化,该功能将在不到一秒钟的时间内响应600砖(取决于计算机的速度)。

有了Python的列表解析功能,您可以更简洁地编写此函数(尽管它将失去递减的基本顺序优化≈10%):

@lru_cache(None)
def stairCount(N,base=None):
    base = min(base or N-1,N)
    if N > base*(base+1)//2: return 0
    if N < 3: return 1
    return sum(stairCount(N-b,b-1) for b in range(2,base+1))

编辑:这是带有“手动”记忆的版本(即不使用functools):

def stairCount(N,base=None,memo=dict()):
    memoKey = (N,base)
    if memoKey in memo: return memo[memoKey]
    base = min(base or N-1,N)
    if N > base*(base+1)//2: return 0
    if N < 3: return 1
    count = 0
    while True:
        nextLevels = stairCount(N-base,base-1,memo)
        if nextLevels == 0: break
        count += nextLevels
        base   = base - 1
    memo[memoKey] = count
    return count