【发布时间】:2009-06-03 00:21:23
【问题描述】:
使用 Python,我想比较列表中每个可能的对。
假设我有
my_list = [1,2,3,4]
我想对列表中 2 个元素的每个组合进行一次操作(我们称之为 foo)。
最终结果应该是一样的
foo(1,1)
foo(1,2)
...
foo(4,3)
foo(4,4)
我的第一个想法是手动遍历列表两次,但这似乎不是很pythonic。
【问题讨论】:
标签: python
使用 Python,我想比较列表中每个可能的对。
假设我有
my_list = [1,2,3,4]
我想对列表中 2 个元素的每个组合进行一次操作(我们称之为 foo)。
最终结果应该是一样的
foo(1,1)
foo(1,2)
...
foo(4,3)
foo(4,4)
我的第一个想法是手动遍历列表两次,但这似乎不是很pythonic。
【问题讨论】:
标签: python
在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
. . . .
这三个函数都是在 Python 2.6 中引入的。
【讨论】:
permutations() 和combinations() 获得上述结果,请使用r=2 代替repeat=2 示例中用于product() 的product()
我遇到了类似的问题并找到了解决方案here。它无需导入任何模块即可工作。
假设一个列表如下:
people = ["Lisa","Pam","Phil","John"]
简化的单行解决方案如下所示。
所有可能的配对,包括重复:
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]
结果:
('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')
编辑:在简化此解决方案的返工后,我意识到它与 Adam Rosenfield 的方法相同。我希望更大的解释能帮助一些人更好地理解它。
【讨论】:
如果你只是调用一个函数,你真的不能做得更好:
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]
这将返回一个列表,其中列出了将 foo(i, j) 应用于每个可能的对 (i, j) 的结果。
【讨论】:
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 如果您希望按字典顺序排列组合,则效果很好。但是,如果您希望组合随机排序,这里有一个解决方案:
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=100 和k=4:
gen_combos = generate_random_combinations(100, 4)
for i in range(3):
print(next(gen_combos))
结果:
[4, 9, 55, 79]
[11, 49, 58, 64]
[75, 82, 83, 91]
对于我的用例,我正在实现一种算法,该算法正在搜索单个(或几个)组合并在找到有效组合时停止。平均而言,它会遍历所有可能组合的一个非常小的子集,因此无需预先构建所有可能的组合,然后再洗牌(无论如何,人口规模太大而无法容纳内存中的所有组合)。
随机性对于快速找到解决方案至关重要,因为字典顺序会导致总体中的单个值包含在所有组合中,直到用尽为止。例如,如果我们有n=100 和k=4,那么结果会是这样的:
| index | combination |
|---|---|
| 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 个组合。随机化顺序有助于缓解此问题(请参阅上面的示例输出)。
【讨论】: