Python笛卡尔积和条件?

Python cartesian product and conditions?

在 Python 中,我使用 itertools.product() 函数为模拟生成输入参数。

我有一个测试函数需要 4 个输入参数 a1、a2、b1 和 b2。我使用以下代码生成参数。示例:

params = itertools.product(range(10,41,2), range(10,41,2), range(0, 2), range(5, 31, 5))

... 这给了我 3072 种组合。不幸的是,某些组合在逻辑上毫无意义。例如如果 a2 大于 a1,则测试结果无用,同样,当 b1 等于 0 时,b2 的值完全不相关——因此测试此类组合没有意义。

除了手动执行和嵌套 for 循环之外,是否有可能限制或过滤笛卡尔积?因为我的实际用例有超过 4 个参数,这就是为什么我喜欢 itertools 的笛卡尔积函数的便利性。

有什么想法或替代方案吗? 感谢任何帮助,谢谢。

您可以结合使用列表推导式和对所选参数的任何限制。我建议在执行此操作之前将您的参数放入一个集合中,以确保没有不必要的代码。我不会在你上面提到的情况下发生,但并不总是使用 range 来生成参数选项。

例如,这里创建了一个元组参数列表,其中只有参数 1 大于参数 2 + 10 时才是有效组合:

acceptableParamCombinations = 
[ (p1,p2) for p1 in set(range(10,41,2)) for p2 in set(range(10,41,2)) if p1 > p2 + 10 ]

一个选择是制作 params 另一个发电机,它本身由 itertools.product 供电。

例如:

params = (prod for prod in itertools.product(...) if prod[2] <= prod[1])

您可以根据条件在 if 之后添加任何内容。例如 prod[2] <= prod[1] and prod[3] != 0 将检查您在问题中陈述的条件,只允许通过您需要的结果并丢弃任何未通过测试的产品。

Python 3

在 Python 3 中,您可以使用 itertools.filterfalse:

过滤掉不需要的组合
# predicate is true when need to skip the combination
predicate = (lambda (a1, a2, b1, b2): a1 <= a2 and (b1 != 0 or b2 == 5), params)
filtered_params = itertools.filterfalse(predicate, params)

Python 2

您可以使用列表理解或 itertools.ifilter:

filtered_params = itertools.ifilter
    (lambda (a1, a2, b1, b2): a1 <= a2 and (b1 != 0 or b2 == 5), params)

请注意,这两个版本都在后台进行循环和过滤。如果你想避免这种情况,你需要构建一个改进的算法来创建没有不受欢迎的元组。

如果您有很多参数,使用像 python-constraint 这样的模块的基于约束的方法可能更容易使用 - 让它努力找出哪些组合是有效的。

这看起来像

from constraint import Problem

prob = Problem()
prob.addVariables(["a1", "a2"], range(10,41,2))
prob.addVariable("b1", [0, 2])
prob.addVariable("b2", range(5, 31, 5))
prob.addConstraint(lambda a1, a2: a2 <= a1, ["a1", "a2"])
prob.addConstraint(lambda b1, b2: b1 != 0 or b2 == 5, ["b1", "b2"])

for params in prob.getSolutionIter():
    run_sim(**params)

在这种情况下,大多数 convenient/intuitive/readable 可以使用 numpy 的矢量运算来表达您的规则。例如:

import numpy as np

arr = np.array(list(params), dtype = [('a1',int),('a2',int),('b1',int),('b2',int)])
arr = arr[ arr['a2'] <= arr['a1'] ]
arr = arr[ arr['b1'] != 0 ]