从昨晚开始我就一直在想这个谜题。我已经想出了一个解决办法,但效率不高,所以我想看看是否有更好的办法。
难题是:
给定正整数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 = 2T = 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 asexpect和a asseq的实例时,遵循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)
所以我很快意识到解决办法其实是:
前缀a是一些有效的{0, 1}组合,其正长度为l,其中包含o1s
l + 1位置,我开始添加一个或多个-1s并使用b来跟踪数字。最大值将是B + o或取决于保留在A中的槽数,以最小的为数。
递归调用SOLVE'(N, T, B)
在前面的N = 2, T = 4示例中,在其中一个搜索案例中,我将执行以下操作:
让A的前缀为[1],我们就得到了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)])

最佳答案

一个观察:假设n至少是1,你所陈述问题的每一个解决方案都以[1, 0, ..., 0]的形式结束:即一个1后跟零个或多个0s。在这之前的部分解决方案是完全位于[0, n-1]中、从0开始、在n-1结束并采取少于t步骤的步行。
因此,您可以将原来的问题简化为一个稍微简单的问题,即确定t中从[0, n]开始到0结束的n步数(其中每个步可以是0+1-1,与之前一样)。
下面的代码解决了更简单的问题它使用lru_cachedecorator来缓存中间结果;这在python 3的标准库中,或者有一个recipe可以为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

关于algorithm - 用于计算数字和的组合数得出固定结果的算法难题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34709311/

10-12 19:34