尽管递归限制增加,但在背包问题中超出了最大递归深度

时间:2017-09-07 00:29:04

标签: python algorithm recursion

对于在线课程,我尝试分别解决knapsack problem:给定n项和数组vw的值和权重,找到具有最大值的项目组合,但要遵守总权重小于给定阈值W的约束。

我使用A(i, x, v, w)项和总权重约束i定义子问题x的解决方案;因此,问题是计算A(n, W, v, w)。我尝试过以下方法:

import sys
sys.setrecursionlimit(3000)

def memoize(f):
    memo = {}
    def helper(i, x, *args, **kwargs):
        if (i, x) not in memo:            
            memo[(i, x)] = f(i, x, *args, **kwargs)
        return memo[(i, x)]
    return helper

@memoize
def A(i, x, v, w):
    if i == 0:
        return 0
    if x >= w[i-1]:
        return max(A(i-1, x, v, w), A(i-1, x-w[i-1], v, w) + v[i-1])
    else:
        return A(i-1, x, v, w)

如果我在一个简单的测试用例上运行它,它可以工作:

import pytest

@pytest.fixture
def data_simple():
    W = 6
    n = 4
    v = [3, 2, 4, 4]
    w = [4, 3, 2, 3]
    return W, n, v, w

def test_A(data_simple):
    W, n, v, w = data_simple
    assert A(n, W, v, w) == 8

if __name__ == "__main__":
    pytest.main([__file__, "-s"])

在这种情况下,最大值是通过取最后两个项目获得的,每个项目的值为4。

但是,对于实际问题,我们需要使用从knapsack_big.txt获得的更大输入数据集,其中W = 2,000,000且n = 2,000。

我试图按如下方式运行:

def readfile(file):
    with open(file) as f:
        first_line = f.readline().strip()
        W, n = tuple(map(int, first_line.split()))
        data = [tuple(map(int, line.split())) for line in f.read().splitlines()]
        v, w = zip(*data)
        assert len(v) == n
        return W, n, v, w

@pytest.fixture
def data_big():
    return readfile('knapsack_big.txt')

def test_big(data_big):
    W, n, v, w = data_big
    optimal_value = A(n, W, v, w)

然而,当我尝试运行时,我得到RecursionError

    def helper(i, x, *args, **kwargs):
        if (i, x) not in memo:
>           memo[(i, x)] = f(i, x, *args, **kwargs)
E           RecursionError: maximum recursion depth exceeded

我不明白这一点:如果每次调用函数A,它的值{}为ii的最大值为{ {1}},那么3000的递归深度(在开头设置)是否足够?

(我怀疑可能是这样的情况是每次调用2000时都会调用memoize函数包装器,所以每次都会创建一个空的A字典而不是填写字典。我基于http://www.python-course.eu/python3_memoization.php的示例,但也许我应该尝试https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize处的一些实现?)

1 个答案:

答案 0 :(得分:2)

它属于Maximum recursion depth exceeded,因为每次调用函数A helper时,也会调用函数,所以实际上每次递减i时都会有两次递归调用。我尝试通过在函数helper中实现memoization来删除对A的调用,并且它有效:

memo = {}
def A2(i, x, v, w):
    global memo
    if i == 0:
        return 0
    if (i, x) not in memo:
        if x >= w[i - 1]:
            memo[(i, x)] = max(A2(i - 1, x, v, w),
                               A2(i - 1, x - w[i - 1], v, w) + v[i - 1])
        else:
            memo[(i, x)] = A2(i - 1, x, v, w)
    return memo[(i, x)]

输出:

  

4243395