【问题标题】:Find all triplets i,j,k such that i+j+k=n找到所有三元组 i,j,k 使得 i+j+k=n
【发布时间】:2022-01-24 21:22:47
【问题描述】:

我已经对此进行了编码,但这很长:

for i in range(n + 1):
    for j in range(n + 1):
        for k in range(n + 1):
            if i + j + k == n:

有没有什么聪明的办法让它跑得更快?目前是 O(n^3),这很可悲。

【问题讨论】:

  • 选择k = n - j - i 并删除最内层的循环很容易将其发送给O(n^2)
  • 如果没有进一步的限制,有 O(n^2) 个可能的三元组总和为 n,所以不,对于最坏情况的运行时,您无能为力。
  • 问题:对于 i != j,是否需要同时返回 (i, j, k) 和 (j, i, k) ?
  • 我认为 O(n^2) 已经相当不错了
  • 如果您允许负值,则存在无限数量的三元组(例如,(n, j, -j) 是任何j 值的解决方案),因此您不能希望明确找到它们全部

标签: python math discrete-mathematics


【解决方案1】:

有几种可能的解决方案 - 实际上您不需要在所有这些中循环直到 N,最后一个数字完全免费。

请记住,三元组中的所有数字都必须是正数(否则答案是无限的)。

  1. 如果不包括排列(即(1,2,3)(3,2,1) 是同一个三元组),从小到大:

    def iter_triplets(n):
        # This is the smallest number, can't be more than 1/3 of n
        for i in range(0, n//3 + 1):
            sum_left = n-i
            # This is the second smallest, can't be more than 1/2 of the sum_left or less than the first by definition
            for j in range(i, sum_left//2 + 1):
                yield (i, j, sum_left-j)  # Last number is calculated.
    
    
    >>> list(iter_triplets(6))  
    [(0, 0, 6), (0, 1, 5), (0, 2, 4), (0, 3, 3), (1, 1, 4), (1, 2, 3), (2, 2, 2)]
    >>> list(iter_triplets(10))
    [(0, 0, 10), (0, 1, 9), (0, 2, 8), (0, 3, 7), (0, 4, 6), (0, 5, 5), (1, 1, 8), (1, 2, 7), (1, 3, 6), (1, 4, 5), (2, 2, 6), (2, 3, 5), (2, 4, 4), (3, 3, 4)]
    
  2. 如果不包括排列(即(1,2,3)(3,2,1) 是同一个三元组),从大到小:

    import math
    def iter_triplets(n):
        # This is the biggest number, can't be less than 1/3 of n
        for i in range(n, math.ceil(n/3) - 1, -1):
            sum_left = n-i
            # This is the second biggest number, can't be less than 1/2 of the sum_left and more than first number by definition.
            # ceil to correct rounding errors.
            for j in range(min(sum_left, i), math.ceil((sum_left)/2) - 1, -1):
                yield (i, j, sum_left-j)  # Last number is calculated.
    
    
    >>> list(iter_triplets(10))
    [(10, 0, 0), (9, 1, 0), (8, 2, 0), (8, 1, 1), (7, 3, 0), (7, 2, 1), (6, 4, 0), (6, 3, 1), (6, 2, 2), (5, 5, 0), (5, 4, 1), (5, 3, 2), (4, 4, 2), (4, 3, 3)]
    
  3. 如果包含排列(即(1,2,3)(3,2,1)不同三元组),从小到大:

    def iter_triplet_permutations(n):
        for i in range(0, n+1):
            sum_left = n-i
            for j in range(0, sum_left+1):
                yield (i, j, sum_left-j)
    
    >>> list(iter_triplet_permutations(5)) 
    [(0, 0, 5), (0, 1, 4), (0, 2, 3), (0, 3, 2), (0, 4, 1), (0, 5, 0), (1, 0, 4), (1, 1, 3), (1, 2, 2), (1, 3, 1), (1, 4, 0), (2, 0, 3), (2, 1, 2), (2, 2, 1), (2, 3, 0), (3, 0, 2), (3, 1, 1), (3, 2, 0), (4, 0, 1), (4, 1, 0), (5, 0, 0)]
    

【讨论】:

  • 是的,这是正确的实现。
【解决方案2】:

最里面的循环似乎是多余的,因为一旦有了ijk 就会免费提供:

for i in range(n + 1):
    for j in range(n + 1):
        if i+j <= n:
            print((i, j, n-i-j))

如果我们想要添加到n 的唯一数字的三元组,那么也许这可以工作:

for i in range(n + 1):
    for j in range(i, (n - i)//2 + 1):
        out.append((i, j, n-i-j))

【讨论】:

    【解决方案3】:

    有几种方法可以让代码运行得更快。

    1. 使用列表推导。语法 [ReturnThing ForLoops 条件]

    2. 从前一个循环到达的地方开始每个循环。这样可以避免重复

      def MakeTriplet(n):                                                                          
          return [(i,j,k) for i in range(0,n+1) for j in range(i,n+1) for k in range(j,n+1) if (i+j+k)==n]
      

    【讨论】:

    • 我需要这些副本。就是这样。 (我的问题中没有提到它)
    猜你喜欢
    • 2018-09-16
    • 1970-01-01
    • 1970-01-01
    • 2014-02-15
    • 2020-01-10
    • 2014-11-25
    • 1970-01-01
    • 1970-01-01
    • 2012-10-20
    相关资源
    最近更新 更多