使用 itertools 在 Python 中进行组合和排列

(以下信息和更多信息可以从我即将出版的、即将出版的关于递归算法的书中找到。我将在 2022 年该书出版时更新这篇文章。)

如果您需要从中生成组合和排列的列表、字典或其他可迭代的值对象,Python 具有内置的 itertools 模块作为其标准库的一部分。 可迭代对象的排列是所有值的每一种可能的排序,而组合是一些、没有或所有值的每一种可能选择。 例如,集合的排列组合 {'A', 'B', 'C'} 是:

排列组合ABC、ACB、BAC、BCA、CAB(无)、A、B、C、AB、AC、BC、ABC

您还可以多次重复使用这些值,这称为重复排列和重复组合(也称为替换):

重复排列组合重复AAA, AAB, AAC, ABA, ABB, ABC, ACA, ACB, ACC, BAA, BAB, BAC, BBA, BBB, BBC, BCA, BCB, BCC, CAA, CAB, CAC, CBA, CBB, CBC, CCA, CCB, CCC(无), A, B, C, AA, AB, AC, BB, BC, CC, AAA, AAB, AAC, ABB, ABC, ACC, BBB, BBC, BCC, CCC

(请注意,重复排列与尝试每种可能的字符组合以暴力破解密码的密码破解器实际上是一样的。)

当向可迭代对象添加更多值时,排列和组合的数量会迅速增加。 排列组合总数如下:

n个值的排列组合n个值无重复n!2^n有重复n^n“2n选n”,即(2n)! / (n!)^2

但是要让 Python 生成排列,您可以使用 itertools.permutations():

>>> import itertools
>>> for v in itertools.permutations(['A', 'B', 'C']):
...   print(v)
...
('A', 'B', 'C')
('A', 'C', 'B')
('B', 'A', 'C')
('B', 'C', 'A')
('C', 'A', 'B')
('C', 'B', 'A')
>>>
>>> list(itertools.permutations(['A', 'B', 'C']))
[('A', 'B', 'C'), ('A', 'C', 'B'), ('B', 'A', 'C'), ('B', 'C', 'A'), ('C', 'A', 'B'), ('C', 'B', 'A')]


>>> list(itertools.permutations(['A', 'B', 'C'], 2))
[('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
>>> list(itertools.permutations(['A', 'B', 'C'], 1))
[('A',), ('B',), ('C',)]

要让 Python 生成组合,您可以使用 itertools.combinations():

>>> import itertools
>>> for v in itertools.combinations(['A', 'B', 'C'], 2):
...   print(v)
...
('A', 'B')
('A', 'C')
('B', 'C')


>>> list(itertools.combinations(['A', 'B', 'C'], 1))
[('A',), ('B',), ('C',)]
>>> list(itertools.combinations(['A', 'B', 'C'], 2))
[('A', 'B'), ('A', 'C'), ('B', 'C')]
>>> list(itertools.combinations(['A', 'B', 'C'], 3))
[('A', 'B', 'C')]

请注意, combinations() 函数采用第二个参数来表示要选择的值的数量。 要获得所有组合(也称为幂集),您需要多次调用 combinations():

>>> powerSet = []
>>> import itertools
>>> for k in range(4):
...   powerSet.extend(itertools.combinations(['A', 'B', 'C'], k))
...
>>> powerSet
[(), ('A',), ('B',), ('C',), ('A', 'B'), ('A', 'C'), ('B', 'C'), ('A', 'B', 'C')]

要获得重复/替换的排列,请致电 itertools.product() 并为其传递可迭代对象的大小 repeat 争论:

>>> import itertools
>>> for v in itertools.product(['A', 'B', 'C'], repeat=3):
...   print(v)
...
('A', 'A', 'A')
('A', 'A', 'B')
('A', 'A', 'C')
('A', 'B', 'A')
('A', 'B', 'B')
('A', 'B', 'C')
('A', 'C', 'A')
('A', 'C', 'B')
('A', 'C', 'C')
('B', 'A', 'A')
('B', 'A', 'B')
('B', 'A', 'C')
('B', 'B', 'A')
('B', 'B', 'B')
('B', 'B', 'C')
('B', 'C', 'A')
('B', 'C', 'B')
('B', 'C', 'C')
('C', 'A', 'A')
('C', 'A', 'B')
('C', 'A', 'C')
('C', 'B', 'A')
('C', 'B', 'B')
('C', 'B', 'C')
('C', 'C', 'A')
('C', 'C', 'B')
('C', 'C', 'C')
>>> list(itertools.product(['A', 'B', 'C'], repeat=3))
[('A', 'A', 'A'), ('A', 'A', 'B'), ('A', 'A', 'C'), ('A', 'B', 'A'), ('A', 'B', 'B'), ('A', 'B', 'C'), ('A', 'C', 'A'), ('A', 'C', 'B'), ('A', 'C', 'C'), ('B', 'A', 'A'), ('B', 'A', 'B'), ('B', 'A', 'C'), ('B', 'B', 'A'), ('B', 'B', 'B'), ('B', 'B', 'C'), ('B', 'C', 'A'), ('B', 'C', 'B'), ('B', 'C', 'C'), ('C', 'A', 'A'), ('C', 'A', 'B'), ('C', 'A', 'C'), ('C', 'B', 'A'), ('C', 'B', 'B'), ('C', 'B', 'C'), ('C', 'C', 'A'), ('C', 'C', 'B'), ('C', 'C', 'C')]

要获得重复/替换的组合,请致电 itertools.combinations_with_replacement():

>>> import itertools
>>> for v in itertools.combinations_with_replacement(['A', 'B', 'C'], 3):
...   print(v)
...
('A', 'A', 'A')
('A', 'A', 'B')
('A', 'A', 'C')
('A', 'B', 'B')
('A', 'B', 'C')
('A', 'C', 'C')
('B', 'B', 'B')
('B', 'B', 'C')
('B', 'C', 'C')
('C', 'C', 'C')
>>> list(itertools.combinations_with_replacement(['A', 'B', 'C'], 3))
[('A', 'A', 'A'), ('A', 'A', 'B'), ('A', 'A', 'C'), ('A', 'B', 'B'), ('A', 'B', 'C'), ('A', 'C', 'C'), ('B', 'B', 'B'), ('B', 'B', 'C'), ('B', 'C', 'C'), ('C', 'C', 'C')]

如果您像我一样难以记住排列和组合之间的差异,有无重复,以及哪些 Python 函数实现了它们,请将此页面添加为书签以便将来轻松访问。

阅读更多

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注