查找序列第N个词的更好方法

时间:2019-11-05 06:15:24

标签: python algorithm recursion

给出:

    I : a positive integer
    n : a positive integer

第n个输入项的序列= I:

    F(I,1) = (I * (I+1)) / 2
    F(I,2) = F(I,1) + F(I-1,1) + F(I-2,1) + .... F(2,1) + F(1,1)
    F(I,3) = F(I,2) + F(I-1,2) + F(I-2,2) + .... F(2,2) + F(2,1)
    ..
    ..
    F(I,n) = F(I,n-1) + F(I-1,n-1) + F(I-2,n-1) + .... F(2,n-1) + F(1,n-1)
    nth term --> F(I,n)

方法1:使用递归查找以上内容:

def recursive_sum(I, n):
    if n == 1:
        return (I * (I + 1)) // 2
    else:
        return sum(recursive_sum(j, n - 1) for j in range(I, 0, -1))

方法2:迭代以将可重用的值存储在字典中。使用此字典获得第n个字词:

def non_recursive_sum_using_data(I, n):
    global data
    if n == 1:
        return (I * (I + 1)) // 2
    else:
        return sum(data[j][n - 1] for j in range(I, 0, -1))

def iterate(I,n):
    global data
    data = {}
    i = 1
    j = 1   
    for i in range(n+1):
        for j in range(I+1):
            if j not in data:
                data[j] = {}
            data[j][i] = recursive_sum(j,i)
    return data[I][n]

由于最大递归深度,递归方法显然无效。而且,下一种方法的时间和空间复杂性将很差。

是否有更好的递归方法?还是不同于递归的方法?
我很好奇我们是否可以找到第n个项的公式。

4 个答案:

答案 0 :(得分:1)

您可以cache递归结果:

from functools import lru_cache

@lru_cache(maxsize=None)
def recursive_sum(I, n):
    if n == 1:
        return (I * (I + 1)) // 2
    return sum(recursive_sum(j, n - 1) for j in range(I, 0, -1))

这样一来,您就可以获得递归方法的可读性和简洁性,而不会出现大多数性能问题,因为该函数对于每个参数组合(I, n)仅调用一次。

答案 1 :(得分:1)

使用常规的binomial(n,k) = n!/(k!*(n-k)!),您可以 F(I,n) = binomial(I+n, n+1)

然后,您可以选择最喜欢的方法来计算二项式系数。

这里有个例子:

def binomial(n, k):
    numerator = denominator = 1 
    t = max(k, n-k) 
    for low,high in enumerate(range(t+1, n+1), 1): 
        numerator *= high 
        denominator *= low 
    return numerator // denominator

def F(I,n): return binomial(I+n, n+1)

答案 2 :(得分:0)

序列的第n项的公式就是您已经提到的公式。 同样正确的是,您已经确定它会导致效率低下的算法和堆栈溢出。

您可以研究动态编程方法,您只需计算一次F(I,N),然后重复使用该值即可。

例如,这是斐波那契序列的计算方式。 [仅作为示例] https://www.geeksforgeeks.org/program-for-nth-fibonacci-number/ 您需要找到相同的模式并缓存值

我在这里用golang编写的这段小代码中有一个示例 https://play.golang.org/p/vRi-QMj7z2v

答案 3 :(得分:0)

标准DP

一个人可以做一点点数学来重写您的函数:

F(i,n) = sum_{k=0}^{i-1} F(i-k, n-1) = sum_{k=1}^{i} F(k, n-1)

现在注意,如果考虑矩阵F_{ixn},要计算F(i,n),我们只需要添加上一列的元素即可。

x----+---
|    + |
|----+ |
|----+-F(i,n)

我们得出的结论是,我们可以构建第一层(也称为专栏)。然后第二个。依此类推,直到我们到达第n层。

最后,我们取最后一层的最后一个元素为F(i,n)

计算时间约为O(I*n)

更多基于数学但更快的

另一种方法是将我们的图层视为向量变量X。 我们可以将递归关系写为

X_n = MX_{n-1} 

其中M是一个三角形矩阵,下部带有1

然后我们要计算X_n的通用项,因此我们要计算M^n

按照Yves Daoust

(我只是从上面的链接中复制)

系数应标为_{n+1}_n,但此处为_1和''表示可读性

此外,矩阵是上三角形,但是我们可以随后进行转置...

a_1 b_1 c_1 d_1   1 1 1 1   a b c d
    a_1 b_1 c_1 = 0 1 1 1 * 0 a b c
        a_1 b_1   0 0 1 1   0 0 a b
            a_1   0 0 0 1   0 0 0 a

从最后一行转到第一行:

  • a = 1
  • 来自b_1 = a+b = 1 + b = nb = n
  • 来自c_1 = a+b+c = 1+n+cc = n(n+1)/2
  • 来自d_1 = a+b+c+d = 1+n+n(n+1)/2 +d, d = n(n+1)(n+2)/6

我还没有证明,但我暗示e_1 = n(n+1)(n+2)(n+3)/24(基本上就是C_n^k) (我认为证明更多地在于F(i,n) = F(i,n-1) + F(i-1,n)

更一般地说,不是采用变量a,b,c ...而是X_n(0)X_n(1) ...

X_n(0) = 1
X_n(i) = n*...*(n+i-1) / i!

并通过应用计算X的方法:

X_n(0) = 1
X_n(i) = X_n(i-1)*(n+i-1)/i

最后,我们将F(i,n)推导出标量积Y_{n-1} * X_1,其中Y_nX_nX_1(n) = n*(n+1)/2的反向向量



from functools import lru_cache
#this is copypasted from schwobaseggl
@lru_cache(maxsize=None)
def recursive_sum(I, n):
    if n == 1:
        return (I * (I + 1)) // 2
    return sum(recursive_sum(j, n - 1) for j in range(I, 0, -1))


def iterative_sum(I,n):
    layer = [ i*(i+1)//2 for i in range(1,I+1)]
    x = 2
    while x <= n:
        next_layer = [layer[0]]
        for i in range(1,I):
            #we don't need to compute all the sum everytime
            #take the previous sum and add it the new number
            next_layer.append( next_layer[i-1] + layer[i] )
        layer = next_layer

        x += 1
    return layer[-1]

def brutus(I,n):
    if n == 1:
        return I*(I+1)//2

    X_1 = [ i*(i+1)//2 for i in range(1, I+1)]
    X_n = [1]
    for i in range(1, I):
        X_n.append(X_n[-1] * (n-1 + i-1) / i )

    X_n.reverse()
    s = 0
    for i in range(0, I):
        s += X_1[i]*X_n[i]
    return s

def do(k,n):
    print('rec', recursive_sum(k,n))
    print('it ', iterative_sum(k,n))
    print('bru', brutus(k,n))
    print('---')

do(1,4)
do(2,1)
do(3,2)
do(4,7)
do(7,4)