【问题标题】:How to find number of ways that the integers 1,2,3 can add up to n?如何找到整数 1、2、3 加起来为 n 的方式数?
【发布时间】:2017-08-16 18:02:09
【问题描述】:

给定一组整数 1,2 和 3,找出它们加起来为 n 的方式数。 (顺序很重要,即说 n 是 5。1+2+1+1 和 2+1+1+1 是两个不同的解决方案)

我的解决方案是将 n 拆分为 1 的列表,因此如果 n = 5,则 A = [1,1,1,1,1]。我将通过添加相邻的数字从每个列表中递归生成更多子列表。所以 A 会再生成 4 个列表:[2,1,1,1], [1,2,1,1], [1,1,2,1],[1,1,1,2],每个这些列表中的一些将生成更多的子列表,直到它到达像 [3,2] 或 [2,3] 这样的终止案例

这是我提出的解决方案(在 Python 中)

ways = []
def check_terminating(A,n):
    # check for terminating case
    for i in range(len(A)-1):
        if A[i] + A[i+1] <= 3:
            return False # means still can compute
    return True

def count_ways(n,A=[]):
    if A in ways:
       # check if alr computed if yes then don't compute
       return True
    if A not in ways: # check for duplicates
        ways.append(A) # global ways
    if check_terminating(A,n):
        return True # end of the tree
    for i in range(len(A)-1):
        # for each index i,
        # combine with the next element and form a new list
        total = A[i] + A[i+1]
        print(total)
        if total <= 3:
            # form new list and compute
            newA = A[:i] + [total] + A[i+2:]
            count_ways(A,newA)
            # recursive call

# main            
n = 5
A = [1 for _ in range(n)]

count_ways(5,A)
print("No. of ways for n = {} is {}".format(n,len(ways)))

我想知道我是否走在正确的轨道上,如果是,有什么方法可以让这段代码更有效率吗?

请注意,这不是硬币找零的问题。在硬币变化中,发生的顺序并不重要。在我的问题中,1+2+1+1 与 1+1+1+2 不同,但在硬币变化中,两者都是相同的。请不要为此答案发布硬币找零解决方案。

编辑:我的代码正在运行,但我想知道是否有更好的解决方案。感谢您的所有帮助:)

【问题讨论】:

  • 您是否通过编译器运行过这个?你有错误吗?如果没有,那么在提出关于 SO 的问题之前先尝试一下。如果你想让别人告诉你你的代码有多,试试 Code Review。
  • 顺便说一句,您要解决的问题称为“分区”。
  • 这组数字一定是1、2、3吗?
  • @Prune:我认为这个问题不适合这个门户。这是一个正常的算法问题,值得关注。为什么要关闭它?
  • FWIW,这种组合的正式名称是composition;如果顺序很重要,那么它就是一个分区。

标签: python algorithm recursion numbers


【解决方案1】:

递归关系为 F(n+3)=F(n+2)+F(n+1)+F(n) 其中 F(0)=1, F(-1)=F(-2 )=0。这些是 tribonacci 数(斐波那契数的一种变体):

可以编写一个简单的 O(n) 解决方案:

def count_ways(n):
    a, b, c = 1, 0, 0
    for _ in xrange(n):
        a, b, c = a+b+c, a, b
    return a

它更难,但可以用相对较少的算术运算来计算结果:

def count_ways(n):
    A = 3**(n+3)
    P = A**3-A**2-A-1
    return pow(A, n+3, P) % A

for i in xrange(20):
    print i, count_ways(i)

【讨论】:

  • 不错的回答保罗。它确实告诉了O(n) 时间内可能的正确方式数量。我在想我们是否也可以打印这些解决方案,现在我们知道实际可能的解决方案总数是多少。
  • 你能解释一下这个惊人的第二个公式是从哪里来的吗?我没有在 OEIS A000073 上看到它,我有兴趣了解这种方法如何推广到其他递归关系。
  • @PeterdeRivaz 如果您考虑多项式商环:Z[X]/(X^3-X^2-X-1),计算该环中的 X^n。然后请注意,只要“A”足够大以至于系数不会溢出,您就可以在 Z/(A^3-A^2-A-1) 中近似计算。 3**(n+3) 是这里的 A - 只是一个比 tribonacci 数字增长得更快的数字。这里有一些有用的(斐波那契而不是三角波)背景:fare.tunes.org/files/fun/fibonacci.lisp
  • 这是一个引人入胜的链接!我的理解是,通过将 3 个相关数字打包成一个数字(并利用矩阵的特殊结构进一步加速每个矩阵乘法),它的效率大致相当于快速矩阵求幂。
  • @PeterdeRivaz 是的,没错。它适用于任何斐波那契变体——例如这里的六边形数字:stackoverflow.com/questions/45657365/…
【解决方案2】:

您描述的想法听起来不错。编写一个产生正确答案的递归函数很容易......慢慢地。

然后,您可以通过记住答案来加快速度。只需保留您已经计算过的答案字典即可。在你的递归函数中,看看你是否有预先计算好的答案。如果是这样,请将其退回。如果没有,计算它,将答案保存在字典中,然后返回答案。

那个版本应该运行得很快。

【讨论】:

  • 有关记忆化对分区算法影响的详细示例,请参见例如这个答案:stackoverflow.com/questions/32907406/…
  • 使用您告诉的方式进行记忆不会跟踪出现的顺序。如果顺序在这里不重要,这种方法将是理想的。我不使用这种方式进行记忆可以在这里提供帮助。
  • @CodeHunter 只要递归函数是正确的,记忆函数也是如此。在递归函数中跟踪出现的顺序很容易——实际上不跟踪出现的顺序更难。
【解决方案3】:

O(n) 方法是可能的:

def countways(n):
    A=[1,1,2]
    while len(A)<=n:
        A.append(A[-1]+A[-2]+A[-3])
    return A[n]

这个想法是,我们可以通过考虑最后一个分区大小的每个选择 (1,2,3) 来计算出有多少种方法可以用 n 创建一个序列。

例如要计算 (1,1,1,1) 的选择,请考虑:

  1. 选择 (1,1,1) 后跟 1
  2. 选择 (1,1) 后跟 2
  3. 选择 (1) 后跟 3

如果您需要结果(而不仅仅是计数),您可以按如下方式调整此方法:

cache = {}
def countwaysb(n):
    if n < 0:
        return []
    if n == 0:
        return [[]]
    if n in cache:
        return cache[n]
    A = []
    for last in range(1,4):
        for B in countwaysb(n-last):
            A.append(B+[last])
    cache[n] = A   
    return A

【讨论】:

    猜你喜欢
    • 2022-11-13
    • 2012-07-30
    • 1970-01-01
    • 1970-01-01
    • 2023-04-11
    • 1970-01-01
    • 1970-01-01
    • 2016-04-11
    • 2012-09-26
    相关资源
    最近更新 更多