这是我自昨晚以来所想到的一个难题。我提出了一个解决方案,但效率不高所以我想知道是否有更好的想法。
难题是:
给出正整数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
以下python代码可以验证此类规则,当N为expect
且A的实例为seq
时(有些人可能比阅读文字说明更容易阅读代码):
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]
。 1}}
实际上SOLVE(N, T) == SOLVE'(N, T, 0)
。
所以我很快意识到解决方案实际上是:
{0, 1}
组合,其长度为l
,其中包含o
1
l + 1
位置,我开始添加1个或多个-1
并使用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]
。但是这个算法太慢了。
我想知道如何优化它或以任何不同的方式来看待可以提升性能的这个问题?(我只需要这个想法,而不是impl)
一些样本将是:
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)])
答案 0 :(得分:1)
观察:假设n
至少为1
,您所述问题的每个解决方案都会以[1, 0, ..., 0]
形式结束:即,单个1
跟随零或更多0
s。此点之前的解决方案部分是完全位于[0, n-1]
的步行,从0
开始,以n-1
结束,步数少于t
。< / p>
因此,您可以将原始问题简化为稍微简单的问题,即确定t
中以[0, n]
开头并以{结尾的0
步数的步数。 {1}}(其中每个步骤可以是n
,0
或+1
,与以前一样。)
以下代码解决了更简单的问题。它使用-1
装饰器来缓存中间结果;这是在Python 3的标准库中,或者你可以为Python 2下载recipe。
lru_cache
现在我们可以使用此功能来解决您的问题。
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)