更新: 添加了通用解决方案。
这里提供的解决方案代码稍微有些复杂,但不会产生重复元素,并且可以进行惰性求值:
from itertools import combinations, product, chain
r = 3.14
s = 2.71
n = 1
m = 2
idx = combinations(range(n + m), n)
vs = ((r if j in i else s for j in range(n + m)) for i in idx)
res = chain.from_iterable(product(*((+vij, -vij) for vij in vi)) for vi in vs)
print("\n".join(map(str, res)))
输出:
(3.14, 2.71, 2.71)
(3.14, 2.71, -2.71)
(3.14, -2.71, 2.71)
(3.14, -2.71, -2.71)
(-3.14, 2.71, 2.71)
(-3.14, 2.71, -2.71)
(-3.14, -2.71, 2.71)
(-3.14, -2.71, -2.71)
(2.71, 3.14, 2.71)
(2.71, 3.14, -2.71)
(2.71, -3.14, 2.71)
(2.71, -3.14, -2.71)
(-2.71, 3.14, 2.71)
(-2.71, 3.14, -2.71)
(-2.71, -3.14, 2.71)
(-2.71, -3.14, -2.71)
(2.71, 2.71, 3.14)
(2.71, 2.71, -3.14)
(2.71, -2.71, 3.14)
(2.71, -2.71, -3.14)
(-2.71, 2.71, 3.14)
(-2.71, 2.71, -3.14)
(-2.71, -2.71, 3.14)
(-2.71, -2.71, -3.14)
解释
我们可以将输出视为包含 n
+/- r
元素和 m
+/- s
元素的排列,或者换句话说,是由 n
个 +/- r
元素和其余部分的 +/- s
元素组成的元组。 idx
包含所有可能的 +/- r
元素的位置的元组;例如,对于第一个结果,它是 (0,)
。
然后,对于每个这些元组 i
,我们在 vs
中创建“模板”元组,它们只是具有大小为 n
+ m
的元组,在其中索引为 i
的元素为 r
,其余元素为 s
。因此,对于 idx
中的元组 (0,)
,您将获得 (r, s, s)
。如果 n
+ m
很大,则可以考虑先进行步骤 idx = map(set, idx)
以获得更快的 in
操作,但我不确定在哪个点上这将值得一试。
最后,对于 v
中的每个模板 vi
,我需要考虑使用其每个元素的正值和负值的所有可能性。因此,它是 (+vi[0], -vi[0]), (+vi[1], -vi[1]), ...
的笛卡尔积。最后,您只需将每个这些产品的生成器链接在一起即可获得最终结果。
通用解决方案
为了构建一个针对任意数量不同元素的问题的通用解决方案,您需要考虑索引集合的 分区。例如,对于 n = 3
和 m = 5
,您可以将 {0, 1, 2, 3, 4, 5, 6, 7}
分成大小为 3 和 5 的两部分的所有可能方式。以下是一个实现:
from itertools import chain, repeat, permutations, product
def partitions(*sizes):
if not sizes or all(s <= 0 for s in sizes):
yield ()
for i_size, size in enumerate(sizes):
if size <= 0:
continue
next_sizes = sizes[:i_size] + (sizes[i_size] - 1,) + sizes[i_size + 1:]
for p in partitions(*next_sizes):
yield (i_size,) + p
def signed_permutations(*elems):
values, sizes = zip(*elems)
templates = partitions(*sizes)
return chain.from_iterable(
product(*((+values[ti], -values[ti]) for ti in t)) for t in templates)
r = 3.14
s = 2.71
n = 1
m = 2
res = signed_permutations((r, n), (s, m))
print("\n".join(map(str, res)))
思路相同,您需要构建“模板”(这次它们包含值的索引而不是值本身),然后从它们中构建笛卡尔积。
list(itertools.product(*([[+r, -r]] * 1 + [[+s, -s]] * 1)))
返回[(3.14, 2.71), (3.14, -2.71), (-3.14, 2.71), (-3.14, -2.71)]
--3.14
从未出现在第二个位置。 - Nico Schlömer