无法弄清楚如何记忆子集功能

时间:2017-03-26 07:10:02

标签: python arrays dictionary recursion tuples

我的函数应该查找数字列表的值是否以任何形式或形式与我的目标相加。我的代码是:

def memoizedSubset(target, numberList, memo):
    ''' Returns True if there exists a subset of numberList that adds
        up to target and returns False otherwise.'''
    if target == 0:
        return True;
    elif numberList == ():
        return False;
    elif (target, numberList) in memo:
        return memo[(target, numberList)];
    elif numberList[0] > target:
        solution = memoizedSubset(target, numberList[1:], memo);
        memo[(target, numberList)] = solution;
        return solution;
    else:
        useIt = memoizedSubset(target - numberList[0], numberList, memo);
        loseIt = memoizedSubset(target, numberList[1:], memo);
        solution = useIt or loseIt;
        memo[(target, numberList)] = solution;
        return solution;

numberTuple = tuple(range(2, 100, 2));
print(memoizedSubset(1234567, numberTuple, {}));

逻辑似乎完美无缺,但是当我尝试运行该函数时,我得到最大递归深度达到错误。我使用字典来加速进程,因为使用给定的值,在没有字典的情况下完成它需要一段时间。我不能为我的生活找出问题所在。

更新:代码适用于较小的值,但不适用于较大的值,如上面的1234567。是不应该通过记忆来克服递归限制驼峰?

2 个答案:

答案 0 :(得分:1)

删除记忆逻辑并将print(target, numberList[:5])添加到函数顶部会给出:

(1234567, (2, 4, 6, 8, 10))
(1234565, (2, 4, 6, 8, 10))
(1234563, (2, 4, 6, 8, 10))
(1234561, (2, 4, 6, 8, 10))
(1234559, (2, 4, 6, 8, 10))
(1234557, (2, 4, 6, 8, 10))
(1234555, (2, 4, 6, 8, 10))
(1234553, (2, 4, 6, 8, 10))
...

Traceback (most recent call last):
  File "/Users/raymond/Documents/tmp3.py", line 22, in <module>
    print(memoizedSubset(1234, numberTuple, {}));
  File "/Users/raymond/Documents/tmp3.py", line 16, in memoizedSubset
    useIt = memoizedSubset(target - numberList[0], numberList, memo);

这表明递归过程太慢,无法继续工作。

即使添加sys.setrecursionlimit(10000)也无法解决问题。

调试代码:

import sys
sys.setrecursionlimit(10000)

def memoizedSubset(target, numberList, memo):
    ''' Returns True if there exists a subset of numberList that adds
        up to target and returns False otherwise.'''
    print(target, numberList[:5])
    if target == 0:
        return 0;
    elif numberList == ():
        return False;
    elif numberList[0] > target:
        solution = memoizedSubset(target, numberList[1:], memo);
        return solution;
    else:
        useIt = memoizedSubset(target - numberList[0], numberList, memo);
        loseIt = memoizedSubset(target, numberList[1:], memo);
        solution = useIt or loseIt;
        return solution;

numberTuple = tuple(range(2, 100, 2));
print(memoizedSubset(1234567, numberTuple, {}));

答案 1 :(得分:0)

备忘录本身没有问题。但是如果您使用的是Python 3.4+(不需要自己传递备忘录),则可以使用functools.lru_cache

  • if target == 0: return 0应为if target == 0: return True。否则函数总是返回False(0 == False);在我评论之后在问题中编辑。
  • useIt = memoizedSubset(target - numberList[0], numberList, memo)应为useIt = memoizedSubset(target - numberList[0], numberList[1:], memo)
    • numberList永远不会减少。
  • 假设numberList已排序,如果False,您可以立即返回numberList[0] > target
  • 您可以将以下几行合并为一个以获得短路的好处(即使useIt为True,否则loseIt部分将被执行)

    useIt = memoizedSubset(target - numberList[0], numberList, memo);
    loseIt = memoizedSubset(target, numberList[1:], memo);
    solution = useIt or loseIt;
    
from functools import lru_cache

@lru_cache(None)
def solve(target, numbers):
    # assuming numbers is a sorted tuple of `int`s.
    if target == 0:
        return True
    elif (not numbers) or numbers[0] > target:
        return False
    else:
        return solve(target - numbers[0], numbers[1:]) or \
               solve(target, numbers[1:])