对列表中的每对元素进行操作

通过使用 Python,我希望比较列表中每一个可能的对。

假设我有

my_list = [1,2,3,4]

I'd like to do an operation (let's call it foo) on every combination of 2 elements from the list.

最终结果应该与

foo(1,1)
foo(1,2)
...
foo(4,3)
foo(4,4)

我的第一个想法是手动遍历列表两次,但这看起来并不十分简单。

101439 次浏览

查看 itertools模块中的 product(),它完全符合您的描述。

import itertools


my_list = [1,2,3,4]
for pair in itertools.product(my_list, repeat=2):
foo(*pair)

这相当于:

my_list = [1,2,3,4]
for x in my_list:
for y in my_list:
foo(x, y)

编辑: 还有两个非常相似的函数,permutations()combinations():

product()生成所有可能的元素对,包括所有重复的元素:

1,1  1,2  1,3  1,4
2,1  2,2  2,3  2,4
3,1  3,2  3,3  3,4
4,1  4,2  4,3  4,4

permutations()生成每个独特元素对的所有独特排序,消除 x,x的重复:

 .   1,2  1,3  1,4
2,1   .   2,3  2,4
3,1  3,2   .   3,4
4,1  4,2  4,3   .

最后,combinations()只生成每个独特的元素对,按字典顺序排列:

 .   1,2  1,3  1,4
.    .   2,3  2,4
.    .    .   3,4
.    .    .    .

All three of these functions were introduced in Python 2.6.

如果你只是调用一个函数,你不可能比:

for i in my_list:
for j in my_list:
foo(i, j)

如果要收集调用该函数的结果列表,可以执行以下操作:

[foo(i, j) for i in my_list for j in my_list]

它将返回一个列表,列出对每个可能的对 (i, j)应用 foo(i, j)的结果。

我也遇到过类似的问题,并且找到了解决方案 给你。它不需要导入任何模块就可以工作。

Supposing a list like:

people = ["Lisa","Pam","Phil","John"]

A simplified one-line solution would look like this.

所有可能的对 ,包括重复的:

result = [foo(p1, p2) for p1 in people for p2 in people]

所有可能的对,不包括重复的 :

result = [foo(p1, p2) for p1 in people for p2 in people if p1 != p2]

唯一对 ,无关顺序:

result = [foo(people[p1], people[p2]) for p1 in range(len(people)) for p2 in range(p1+1,len(people))]

如果您不想操作,只是想获取这些对,那么删除函数 foo并仅使用一个元组就足够了。

所有可能的对 ,包括重复的:

list_of_pairs = [(p1, p2) for p1 in people for p2 in people]

Result:

('Lisa', 'Lisa')
('Lisa', 'Pam')
('Lisa', 'Phil')
('Lisa', 'John')
('Pam', 'Lisa')
('Pam', 'Pam')
('Pam', 'Phil')
('Pam', 'John')
('Phil', 'Lisa')
('Phil', 'Pam')
('Phil', 'Phil')
('Phil', 'John')
('John', 'Lisa')
('John', 'Pam')
('John', 'Phil')
('John', 'John')

所有可能的对,不包括重复的 :

list_of_pairs = [(p1, p2) for p1 in people for p2 in people if p1 != p2]

结果:

('Lisa', 'Pam')
('Lisa', 'Phil')
('Lisa', 'John')
('Pam', 'Lisa')
('Pam', 'Phil')
('Pam', 'John')
('Phil', 'Lisa')
('Phil', 'Pam')
('Phil', 'John')
('John', 'Lisa')
('John', 'Pam')
('John', 'Phil')

唯一对 ,无关顺序:

list_of_pairs = [(people[p1], people[p2]) for p1 in range(len(people)) for p2 in range(p1+1,len(people))]

结果:

('Lisa', 'Pam')
('Lisa', 'Phil')
('Lisa', 'John')
('Pam', 'Phil')
('Pam', 'John')
('Phil', 'John')

编辑: 为了简化这个解决方案进行了重复工作之后,我意识到它与亚当 · 罗森菲尔德的方法是相同的。我希望更大的解释能帮助一些人更好地理解它。

my_list = [1,2,3,4]


pairs=[[x,y] for x in my_list for y in my_list]
print (pairs)

Ben Bank's answer works well if you want the combinations to be ordered lexicographically. However, if you want the combinations to be randomly ordered, here's a solution:

import random
from math import comb




def cgen(i,n,k):
"""
returns the i-th combination of k numbers chosen from 0,1,...,n-1
    

forked from: https://math.stackexchange.com/a/1227692
changed from 1-indexed to 0-indexed.
"""
# 1-index
i += 1
    

c = []
r = i+0
j = 0
for s in range(1,k+1):
cs = j+1
while r-comb(n-cs,k-s)>0:
r -= comb(n-cs,k-s)
cs += 1
c.append(cs-1)
j = cs
return c




def generate_random_combinations(n, k, shuffle=random.shuffle):
"""
Generate combinations in random order of k numbers chosen from 0,1,...,n-1.
    

:param shuffle: Function to in-place shuffle the indices of the combinations. Use for seeding.
"""
total_combinations = comb(n, k)
combination_indices = list(range(total_combinations))
shuffle(combination_indices)
    

for i in combination_indices:
yield cgen(i, n, k)

示例用法

对于 N=100k=4:

gen_combos = generate_random_combinations(100, 4)


for i in range(3):
print(next(gen_combos))

results in:

[4, 9, 55, 79]
[11, 49, 58, 64]
[75, 82, 83, 91]

用例

对于我的用例,我正在实现一个算法,它搜索一个(或几个)组合,并在找到有效的组合时停止。平均而言,它遍历所有可能组合的一个非常小的子集,因此没有必要预先构建所有可能的组合,然后洗牌(总之,总体规模太大,无法适应内存中的所有组合)。

随机性对于快速找到解决方案至关重要,因为按字典顺序排序会导致总体中的一个值被包含在所有组合中,直到用尽。例如,如果我们有 n=100k=4,那么结果将是这样的:

索引 密码
0 (0,1,2,3)
1 (0,1,2,4)
2 (0, 1, 2, 5)
...
156848 (0, 97, 98, 99)
156849 (1, 2, 3, 4)

如果 0不是有效解决方案的一部分,那么我们将无缘无故地搜索156849个组合。随机化订单有助于缓解这个问题(参见上面的示例输出)。