如何获得所有可能的约束组合?

时间:2019-01-15 19:06:01

标签: python

我需要从具有以下约束的列表中获取所有可能的5种组合:

  • 组合中必须包含重复项。
  • 所有数字的总和等于1。

到目前为止,这是我的代码:

    number = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
    comb = [c for c in itertools.product(number, repeat=5) if  c[0] + c[1] + c[2] + c[3] + c[4] == 1]
    for cb in comp:
        print(cb)

似乎我没有得到所有可能的组合。例如,输出的第一行将是

(0.1, 0.1, 0.1, 0.1, 0.6)

但不包括以下任何内容

(0.6, 0.1, 0.1, 0.1, 0.1)
(0.1, 0.6, 0.1, 0.1, 0.1)
(0.1, 0.1, 0.6, 0.1, 0.1)
(0.1, 0.1, 0.1, 0.6, 0.1)

,依此类推。我还尝试了

的其他方法
itertools.combinations_with_replacement
itertools.permutations

3 个答案:

答案 0 :(得分:4)

itertools.product函数会输出所需的内容,问题在于检查浮点数的相等性,为此您可以使用math.isclose

city, state, zip, and house

答案 1 :(得分:2)

这似乎是一个有趣的情况,导致浮点算术错误导致总和不正确。可以在python documentation.

中找到更多信息。

这是发生的事的一个例子:

>>> 0.1 + 0.6 + 0.1 + 0.1 + 0.1 == 1
False

一种解决方案是检查总和是否在1的误差范围之内。

comb = [c for c in itertools.product(number, repeat=5) if abs(sum(c)  - 1) <= .01]

或者,您也可以四舍五入并进行比较以解决问题,如下所示:

comb = [c for c in itertools.product(number, repeat=5) if round(sum(c), 2) == 1]

答案 2 :(得分:1)

正如其他答案正确指出的那样,问题在于浮点比较。十分之一没有有限的二进制表示形式,除非其中有五个。

由于您对一组非常特殊的数字感兴趣,因此可以将它们映射为整数,而不用使用roundiscloseabs比较。这样,您可以使比较精确,然后将结果映射回所需的数字。

在这种情况下,您可以将所有内容乘以10得到整数。对于更复杂的情况,您可能需要其他因素,完全不同的转换函数,甚至可能需要查找表。

number = range(1, 10)
comb = (tuple(x / 10 for x in c) for c in itertools.product(number, repeat=5) if sum(c) == 10)
for cb in comp:
    print(cb)

其他一些小改进:

  • 使用sum将元素加在一起,而不是手动将其写出。它将使添加更多元素变得更加容易。
  • 除非需要多次访问它,否则请保留comb作为生成器,而不是将其变成占用内存的列表。