[英]algorithmic puzzle for calculating the number of combinations of numbers sum to a fixed result
自昨晚以来,这是我想到的一个难题。 我想出了一个解决方案,但是效率不高,所以我想看看是否有更好的主意。
难题是这样的:
给定正整数N和T,您将需要:
for i in [1, T], A[i] from { -1, 0, 1 }, such that SUM(A) == N
additionally, the prefix sum of A shall be [0, N], while when the prefix sum PSUM[A, t] == N, it's necessary to have for i in [t + 1, T], A[i] == 0
here prefix sum PSUM is defined to be: PSUM[A, t] = SUM(A[i] for i in [1, t])
the puzzle asks how many such A's exist given fixed N and T
例如,当N = 2
, T = 4
,遵循A
的工作:
1 1 0 0
1 -1 1 1
0 1 1 0
但以下不要:
-1 1 1 1 # prefix sum -1
1 1 -1 1 # non-0 following a prefix sum == N
1 1 1 -1 # prefix sum > N
当给定N为expect
且给定A实例为seq
时,以下python代码可以验证该规则(某些人可能比阅读文字描述更容易阅读代码):
def verify(expect, seq):
s = 0
for j, i in enumerate(seq):
s += i
if s < 0:
return False
if s == expect:
break
else:
return s == expect
for k in range(j + 1, len(seq)):
if seq[k] != 0:
return False
return True
我已经编写了解决方案的代码,但是太慢了。 以下是我的:
我将问题分解为两部分,一部分不包含-1
(仅{0, 1}
,另一部分带有-1
。
因此,如果SOLVE(N, T)
是正确答案,则定义一个函数SOLVE'(N, T, B)
,其中正B允许我将前缀和扩展为[-B, N]
的区间的[0, N]
所以实际上SOLVE(N, T) == SOLVE'(N, T, 0)
。
所以我很快意识到解决方案实际上是:
{0, 1}
组合,长度为l
,其中o
1
s l + 1
,我开始加1个或多个-1
s并使用B来跟踪数字。 最大值将为B + o
或取决于A
剩余的插槽数,以较小者为准。 SOLVE'(N, T, B)
在前面的N = 2, T = 4
示例中,在一种搜索情况下,我将执行以下操作:
A = [1, -, -, -]
。 -1
。 在这里,我将仅添加一个: A = [1, -1, -, -]
。 SOLVE'
,这里我将调用SOLVE'(2, 2, 0)
解决最后两个问题。 在这里它将仅返回[1, 1]
。 然后组合之一会产生[1, -1, 1, 1]
。 但是这个算法太慢了。
我想知道如何优化它或以其他方式查看可以提高性能的问题(我只需要这个想法,而不是暗示)
一些示例将是:
T N RESOLVE(N, T)
3 2 3
4 2 7
5 2 15
6 2 31
7 2 63
8 2 127
9 2 255
10 2 511
11 2 1023
12 2 2047
13 2 4095
3 3 1
4 3 4
5 3 12
6 3 32
7 3 81
8 3 200
9 3 488
10 3 1184
11 3 2865
12 3 6924
13 3 16724
4 4 1
5 4 5
6 4 18
指数时间解决方案通常如下(在python中):
import itertools
choices = [-1, 0, 1]
print len([l for l in itertools.product(*([choices] * t)) if verify(n, l)])
一个观察结果:假设n
至少为1
,则您所陈述问题的每个解决方案都以[1, 0, ..., 0]
形式结束:即,单个1
后跟0或多个0
s。 解决方案在该点之前的部分是一个完全位于[0, n-1]
,从0
开始,在n-1
结束且步长少于t
步行。
因此,您可以将原始问题简化为一个简单的问题,即确定[0, n]
中有多少t
步走,从0
开始到n
(每个步可以是0
, +1
或-1
,和以前一样)。
以下代码解决了较简单的问题。 它使用lru_cache
装饰器来缓存中间结果。 可以在Python 3的标准库中找到,也可以从Python 2下载食谱 。
from functools import lru_cache
@lru_cache()
def walks(k, n, t):
"""
Return the number of length-t walks in [0, n]
that start at 0 and end at k. Each step
in the walk adds -1, 0 or 1 to the current total.
Inputs should satisfy 0 <= k <= n and 0 <= t.
"""
if t == 0:
# If no steps allowed, we can only get to 0,
# and then only in one way.
return k == 0
else:
# Count the walks ending in 0.
total = walks(k, n, t-1)
if 0 < k:
# ... plus the walks ending in 1.
total += walks(k-1, n, t-1)
if k < n:
# ... plus the walks ending in -1.
total += walks(k+1, n, t-1)
return total
现在我们可以使用此功能解决您的问题。
def solve(n, t):
"""
Find number of solutions to the original problem.
"""
# All solutions stick at n once they get there.
# Therefore it's enough to find all walks
# that lie in [0, n-1] and take us to n-1 in
# fewer than t steps.
return sum(walks(n-1, n-1, i) for i in range(t))
我的机器上的solve(10, 100)
结果和时间:
In [1]: solve(10, 100)
Out[1]: 250639233987229485923025924628548154758061157
In [2]: %timeit solve(10, 100)
1000 loops, best of 3: 964 µs per loop
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.