这是一个使用动态编程(记忆)的递归解决方案,以便立即处理非常大的列表:
from functools import lru_cache
def boundCumSum(A,loBound,hiBound):
sRange = range(loBound+1,hiBound)
@lru_cache()
def count(s,nPos,nNeg):
if nPos==0 and nNeg==0: return int(s == 0)
if nPos<0 or nNeg<0 or not s in sRange: return 0
return count(s-1,nPos-1,nNeg)+count(s+1,nPos,nNeg-1)
nPos = sum(a>0 for a in A)
nNeg = sum(a<0 for a in A)
return count(sum(A),nPos,nNeg)
它的工作方式是从值的总和(始终是累积总和中的最后一个值)开始,然后通过可用的 +1 和 -1 向后工作。递归部分只需要知道有多少正数和负数,并且目标是在它们用尽时达到零,而不会超出界限。这允许使用 lru_cache 装饰器(来自 functools)将以前的结果保存在内存中并避免重新计算它们。这种记忆是获得良好性能的关键,因为递归过程通常具有相同的值模式来计算。
示例输出:
boundCumSum([1,1,1,1,1,-1,-1,-1],0,4)
# 8
boundCumSum([1,1,1,1,1,-1,-1,-1],-2,3)
# 21
boundCumSum([1]*15+[-1]*12,0,4)
# 4096
boundCumSum([1]*150+[-1]*120,0,40)
# 1772031749108746260736075351494970301882401049674141497351113308699264759272661
为了验证这一点,可以使用生成器函数来显示实际解决方案及其累积和:
def genCumSum(A,loBound,hiBound,combo=[]):
if not A: yield combo; return
for v,i in {v:i for i,v in enumerate(A)}.items():
if v<=loBound or v >=hiBound: continue
yield from genCumSum(A[:i]+A[i+1:],loBound-v,hiBound-v,combo+[v])
...
from itertools import accumulate
for i,solution in enumerate(genCumSum([1,1,1,1,1,-1,-1,-1],0,4),1):
print(i,solution,list(accumulate(solution)))
1 [1, 1, 1, -1, 1, -1, 1, -1] [1, 2, 3, 2, 3, 2, 3, 2]
2 [1, 1, 1, -1, 1, -1, -1, 1] [1, 2, 3, 2, 3, 2, 1, 2]
3 [1, 1, 1, -1, -1, 1, 1, -1] [1, 2, 3, 2, 1, 2, 3, 2]
4 [1, 1, 1, -1, -1, 1, -1, 1] [1, 2, 3, 2, 1, 2, 1, 2]
5 [1, 1, -1, 1, 1, -1, 1, -1] [1, 2, 1, 2, 3, 2, 3, 2]
6 [1, 1, -1, 1, 1, -1, -1, 1] [1, 2, 1, 2, 3, 2, 1, 2]
7 [1, 1, -1, 1, -1, 1, 1, -1] [1, 2, 1, 2, 1, 2, 3, 2]
8 [1, 1, -1, 1, -1, 1, -1, 1] [1, 2, 1, 2, 1, 2, 1, 2]
...
for i,solution in enumerate(genCumSum([1,1,1,1,1,-1,-1,-1],-2,3),1):
print(i,solution,list(accumulate(solution)))
1 [1, 1, -1, 1, -1, 1, -1, 1] [1, 2, 1, 2, 1, 2, 1, 2]
2 [1, 1, -1, 1, -1, -1, 1, 1] [1, 2, 1, 2, 1, 0, 1, 2]
3 [1, 1, -1, -1, 1, 1, -1, 1] [1, 2, 1, 0, 1, 2, 1, 2]
4 [1, 1, -1, -1, 1, -1, 1, 1] [1, 2, 1, 0, 1, 0, 1, 2]
5 [1, 1, -1, -1, -1, 1, 1, 1] [1, 2, 1, 0, -1, 0, 1, 2]
6 [1, -1, 1, 1, -1, 1, -1, 1] [1, 0, 1, 2, 1, 2, 1, 2]
7 [1, -1, 1, 1, -1, -1, 1, 1] [1, 0, 1, 2, 1, 0, 1, 2]
8 [1, -1, 1, -1, 1, 1, -1, 1] [1, 0, 1, 0, 1, 2, 1, 2]
9 [1, -1, 1, -1, 1, -1, 1, 1] [1, 0, 1, 0, 1, 0, 1, 2]
10 [1, -1, 1, -1, -1, 1, 1, 1] [1, 0, 1, 0, -1, 0, 1, 2]
11 [1, -1, -1, 1, 1, 1, -1, 1] [1, 0, -1, 0, 1, 2, 1, 2]
12 [1, -1, -1, 1, 1, -1, 1, 1] [1, 0, -1, 0, 1, 0, 1, 2]
13 [1, -1, -1, 1, -1, 1, 1, 1] [1, 0, -1, 0, -1, 0, 1, 2]
14 [-1, 1, 1, 1, -1, 1, -1, 1] [-1, 0, 1, 2, 1, 2, 1, 2]
15 [-1, 1, 1, 1, -1, -1, 1, 1] [-1, 0, 1, 2, 1, 0, 1, 2]
16 [-1, 1, 1, -1, 1, 1, -1, 1] [-1, 0, 1, 0, 1, 2, 1, 2]
17 [-1, 1, 1, -1, 1, -1, 1, 1] [-1, 0, 1, 0, 1, 0, 1, 2]
18 [-1, 1, 1, -1, -1, 1, 1, 1] [-1, 0, 1, 0, -1, 0, 1, 2]
19 [-1, 1, -1, 1, 1, 1, -1, 1] [-1, 0, -1, 0, 1, 2, 1, 2]
20 [-1, 1, -1, 1, 1, -1, 1, 1] [-1, 0, -1, 0, 1, 0, 1, 2]
21 [-1, 1, -1, 1, -1, 1, 1, 1] [-1, 0, -1, 0, -1, 0, 1, 2]
...
print(sum(1 for _ in genCumSum([1]*15+[-1]*12,0,4)))
# 4096
# The last example (with 270 items in the list) would take forever to check