如果有两个数字r
和s
,我想获得n
+-r
和m
+-s
所有排列的列表。例如(使用r=3.14
和s=2.71
),
n = 1
m = 1
out = [
(+r, +s), (+r, -s), (-r, +s), (-r, -s),
(+s, +r), (+s, -r), (-s, +r), (-s, -r)
]
n = 1
m = 2
out = [
(+r, +s, +s), (+r, -s, +s), (-r, +s, +s), (-r, -s, +s), ...
(+s, +r, +s), (-s, +r, +s), (+s, -r, +s), (-s, -r, +s), ...
...
]
使用itertools.product([+r, -r], repeat=n)
我可以单独获取r
和s
的列表,而我只需要将它们交织在一起,但我不是确定这是否正确。
效率并不是非常重要,所以我不会介意产生许多重复结果的解决方案,只是为了让它们在之后变得独一无二。
答案 0 :(得分:6)
更新:添加了一般解决方案。
这是一个在代码中稍微复杂但不会产生重复元素的解决方案,可以懒惰地进行评估:
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
+ m
个元素,其中n
为+/- r
,其余为+/- s
。 idx
包含具有+/- r
元素的所有可能位置的元组;例如,对于第一个结果,它是(0,)
。
然后,对于这些元组i
中的每一个,我们在vs
中创建“模板”元组,它们只是大小为n
+ m
的元组,其中i
中的索引1}}为r
,其余为s
。因此,对于(0,)
中的元组idx
,您将获得(r, s, s)
。如果n
+ m
非常大,您可以考虑前一步idx = map(set, idx)
来进行更快in
的操作,但我不确定哪一点值得
最后,对于vi
中的每个模板v
,我需要考虑使用每个元素的正值和负值的所有可能性。所以它是(+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)))
这个想法是一样的,你构建“模板”(这次它们包含值的索引而不是值本身),然后是它们的笛卡尔积。
答案 1 :(得分:5)
您还可以将permutations
r
和s
与product
+1
和-1
以及zip
合并他们俩。这样,整个结构更具可读性恕我直言:
>>> n, m = 1, 2
>>> r, s = 3.14, 2.71
>>> [[x*i for x,i in zip(perm, prod)] for perm in permutations([r]*n + [s]*m)
... for prod in product((+1, -1), repeat=n+m)]
[[3.14, 2.71, 2.71],
[3.14, 2.71, -2.71],
...
[-2.71, -2.71, 3.14],
[-2.71, -2.71, -3.14]]
答案 2 :(得分:4)
首先在每个元素上使用product
,然后使用permutations
。然后连接所有结果并将它们传递给set()
以删除重复项:
arr = set(itertools.chain.from_iterable([
itertools.permutations(x)
for x in itertools.product(*([[+r, -r]] * n + [[+s, -s]] * m))
]))
print(arr)