【问题标题】:Find all combination that sum to N with multiple lists使用多个列表查找总和为 N 的所有组合
【发布时间】:2016-01-24 00:38:41
【问题描述】:

给定

  • m 列表数量(m 可能会有所不同)。
  • 每个列表都包含arange() 个数字。

想要

  • 找到 sum()N 的 m 元组(每个列表一个数字)。

我有什么

  • 我可以在静态数量的列表中找到所有组合。

    import numpy as np
    for a in np.arange(0,1,0.01):
        for b in np.arange(0,1,0.01):
            for c in np.arange(0,1,0.01):
                for d in np.arange(0,1,0.01):
                    if (a+b+c+d) == 1.0: 
                        print a,b,c,d
    

我也想找到一种最佳方法来计算它。

【问题讨论】:

  • 一种天真的方法可以使用docs.python.org/2/library/itertools.html#itertools.product 为您提供任意数量列表的嵌套循环行为。
  • 您确定要添加浮点数并测试完全相等吗?
  • 只是为了确定:您知道例如0.1 + 0.2 == 0.3错误,对吗?
  • 这是错误的,因为浮点数不准确,您可能会很不走运。 0.1 + 0.2 == 0.3 示例是他们“失败”的示例。所以你还确定要这样做吗?
  • 如果你使用整数,我想我有一个很好的解决方案。

标签: python numpy


【解决方案1】:

正如 cmets 中所讨论的,范围都是相同的,我们应该使用整数。那么这是一种很好的方式。

不是生成四个数字并测试它们加起来是否为 10,而是生成 三个 数字,将区间 [0, 10] 划分为四个区间。例如,当我们在 (3, 4, 8) 处有切割,并且我们添加了端点 0 和 10,那么我们就有了边界 (0, 3, 4, 8, 10)。相邻边界之间的差异为 (3-0, 4-3, 8-4, 10-8) = (3, 1, 4, 2)。那是四个数字加起来等于 10。下面是执行此操作的代码:

n = 10
import itertools, operator
for cuts in itertools.combinations_with_replacement(range(n+1), 3):
    combi = list(map(operator.sub, cuts + (n,), (0,) + cuts))
    if max(combi) < n:
        print(combi)

打印出来的:

[0, 0, 1, 9]
[0, 0, 2, 8]
[0, 0, 3, 7]
[0, 0, 4, 6]
[0, 0, 5, 5]
[0, 0, 6, 4]
[0, 0, 7, 3]
[0, 0, 8, 2]
[0, 0, 9, 1]
[0, 1, 0, 9]
[0, 1, 1, 8]
[0, 1, 2, 7]
...
...
[7, 2, 0, 1]
[7, 2, 1, 0]
[7, 3, 0, 0]
[8, 0, 0, 2]
[8, 0, 1, 1]
[8, 0, 2, 0]
[8, 1, 0, 1]
[8, 1, 1, 0]
[8, 2, 0, 0]
[9, 0, 0, 1]
[9, 0, 1, 0]
[9, 1, 0, 0]

它非常有效,因为它可以非常直接地生成组合。 if max(combi) &lt; n 只过滤掉[0, 0, 0, 10][0, 0, 10, 0][0, 10, 0, 0][10, 0, 0, 0]


这是您的原始设备、我的设备和 @Mijamo 之间的速度比较,范围为 100 个数字,例如您的示例:

  drum: 21.027 seconds
Stefan:  0.708 seconds
Mijamo: 62.864 seconds

该测试的完整代码:

import itertools, operator
from timeit import timeit

def drum(n):
    out = []
    for a in range(n):
        for b in range(n):
            for c in range(n):
                for d in range(n):
                    if a + b + c + d == n:
                        out.append((a, b, c, d))
    return out

def Stefan(n):
    combinations = (map(operator.sub, cuts + (n,), (0,) + cuts)
                    for cuts in itertools.combinations_with_replacement(range(n+1), 3))
    return [c for c in combinations if max(c) < n]

def Mijamo(n):
    combinations = itertools.product(range(n), repeat=4)
    return [tuple for tuple in combinations if sum(tuple) == n]

for func in drum, Stefan, Mijamo:
    print '%6s: %6.3f seconds' % (func.__name__, timeit(lambda: func(100), number=1))

【讨论】:

  • 其实有[10, 0, 0, 0]也没什么不好。这是一个有效的结果。
  • 您能解释一下(map(operator.sub, cuts + (n,), (0,) + cuts) 的作用吗?这有点令人困惑。
  • 嗯,你原来想要的总和为 1,而你的 a/b/c/d 来自 [0, ..., 0.99] 范围。所以当我求和 10 时,我的数字应该只来自 [0, ..., 9],不是吗?
  • 哇哦。你一个权利。这实际上是我的代码中的一个错误,因为我完全忘记了范围是 [..)
  • 那个map(operator.sub, ...):例如当我们有切割(3,7,8),我们添加端点0和10,那么我们有边界(0,3,7, 8、10)。相邻边界之间的差异为 (3-0, 7-3, 8-7, 10-8) = (3, 4, 1, 2)。够清楚吗?
【解决方案2】:

所有组合都可以通过这种方式检索:

combinations = itertools.product(np.arange(0,1,0.01), repeat = m)

https://docs.python.org/3.5/library/itertools.html#itertools.product

由于它是一个生成器,因此您可以创建一个新的生成器,以这种方式返回总和为 n 的元组

results = (tuple for tuple in combinations if sum(tuple) == N)

【讨论】:

    【解决方案3】:

    如何使用 itertools 中的“产品”来获取所有可能的 m 长度元组。然后您只需按元组总和 == N

    的条件进行过滤

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多