【问题标题】:how to find one list of numbers within specified range that sum to another number?如何在指定范围内找到一个与另一个数字相加的数字列表?
【发布时间】:2019-07-11 20:02:40
【问题描述】:

使用 Python

以下是一些要求:

我想找到一个数字列表 [ ]:

  1. 加起来是一个数字(比如 30)

  2. 在 (start,end) 的范围内,比如说 (8, 20)

  3. 列表中有 Y(比如 3 个)元素

例如:[8,10,12]

我已经尝试了下面的代码,它可以满足我的需求,但它给了我所有的组合,这对内存非常重要。要选择一个,我只是随机选择了一个,但是我想将其用于更大范围的更大列表,因此效率不高。

list(combinations(list(range(8,20)),3))

【问题讨论】:

    标签: python


    【解决方案1】:

    您发布的代码不检查总和。

    下面的 sn-ps 优化内存使用,而不是运行时间

    如果您使用的是 Python 3,那么 combinations 已经返回了一个生成器。您所要做的就是迭代组合。如果总和正确,则从循环中打印组合和break

    from itertools import combinations
    
    for comb in combinations(range(8, 20), 3):
        if sum(comb) == 30:
            print(comb)
            break
    

    输出

    (8, 9, 13)
    

    或者,您可以使用filter,然后在结果上调用next。这样你就可以得到尽可能多的组合:

    from itertools import combinations
    
    valid_combs = filter(lambda c: sum(c) == 30, combinations(range(8, 20), 3))
    
    print(next(valid_combs))
    print(next(valid_combs))
    print(next(valid_combs))
    

    输出

    (8, 9, 13)
    (8, 10, 12)
    (9, 10, 11)
    

    更高级和动态的解决方案是使用函数和yield from(如果您使用的是 Python >= 3.3):

    from itertools import combinations
    
    
    def get_combs(r, n, s):
        yield from filter(lambda c: sum(c) == s, combinations(r, n))
    
    
    valid_combs = get_combs(range(8, 20), 3, 30)
    
    print(next(valid_combs))
    print(next(valid_combs))
    print(next(valid_combs))
    

    输出

    (8, 9, 13)
    (8, 10, 12)
    (9, 10, 11)
    

    【讨论】:

      【解决方案2】:

      这里有一个递归函数的例子,可以有效地做到这一点。

      def rangeToSum(start,stop,target,count):
          if count == 0: return []
          minSum = sum(range(start,start+count-1))
          stop   = min(stop,target+1-minSum)
          if count == 1 :
              return [target] if target in range(start,stop) else [] 
          for n in reversed(range(start,stop)):
              subSum = rangeToSum(start,n,target-n,count-1)
              if subSum: return subSum+[n]
          return []
      
      print(rangeToSum(8,20,30,3)) # [8,10,12]
      

      它的工作方式是先尝试最大的数字,然后调用自己在剩余范围内查找与剩余值相加的数字。这将跳过无法产生目标总和的整个组合。例如尝试 20 次跳过包含 19、18、16、15、14、13、12 或 11 的组合。

      它还考虑了第一个 count-1 项将产生的最小总和,以进一步降低停止值。例如用 3 个数字从 8 达到 30 将至少使用 17 (8+9) 作为前两个数字,因此范围的停止值可以减少到 14,因为 17 + 13 将达到 30,任何更高的值都将超过 30 .

      对于较大的数字,该函数在大多数情况下会很快找到解决方案,但也可能需要很长时间,具体取决于参数的组合。

       rangeToSum(80000,20000000,3000000,10) # 0.6 second
      
       # [80000, 80002, 80004, 80006, 80008, 80010, 80012, 80014, 80016, 2279928]
      

      如果您需要它更快,您可以尝试记忆化(例如,使用 functools 中的 lru_cache)。

      【讨论】:

        猜你喜欢
        • 2017-08-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多