排列组合在编程中很经常会用到,出于好奇,翻里一下python函数库中的排列和组合的函数,觉得写得挺巧妙得,对比自己写的,还是有点值得学习的地方。
itertools.permutations
1 | def permutations(iterable, r=None): |
代码执行逻辑
- 循环: 按照n!/(n-r)! 循环,从indices队尾部开始循环,indices[i]相当于倒数第n个数
- 移动:每次循环,把第i个数与i后面的数交换位置
i与j互换的j位置等价于: (i, n-i+1) (i, n-i+2) (i, n-i+3) (i, .....) (i, n-1)
全部位置都缓过一次后,cycles[i] == 0,把第i个数放到最后面,这个时候indices的顺序和第一次换位前一致,这个算法巧妙之处
- 结束:当cycles所有数减到成0时候结束循环
执行过程描述
1 | # 3选2排列输出 |
数组循环示意图
itertools.combinations
1 | def combinations(iterable, r): |
代码执行逻辑
- 循环:按照选择个数r的倒序索引循环
- 移动:每次循环把 索引i中的位置+1,以及把i+往后的位置也移动一格
- 结束:当索引数组indices保存的位置全部刚好为倒数第r个时候停止
执行过程描述
1 | 6选3的输出 |
数组移动示意图
自己写的排列组合
自己写的感觉容易理解些,排列的性能对比函数库的性能差别不是很大,组合的性能相差较大,主要在数组复制和切割操作多了。
排列 1
2
3
4
5
6
7
8
9
10
11
12
13def permutations2(nums, n = None, index = 0):
if not n:
n = len(nums)
if index == n:
yield tuple(nums[:n])
return
for i in range(index, len(nums)):
nums[index],nums[i] = nums[i],nums[index]
for x in permutations2(nums, n, index+1):
yield x
nums[i],nums[index] = nums[index],nums[i]
组合 1
2
3
4
5
6
7
8
9
10
11
12
13def combinations2(nums, n, result = None):
if not result:
result = []
if len(result) == n:
yield tuple(result)
return
for i in range(len(nums)):
result1 = result[:]
result1.append(nums[i])
for x in combinations2(nums[i+1:], n, result1):
yield x
性能对比
性能优化主要更具leetcode上面的排列组合的练习题测试,获得一个大概相对的效率(在几十ms的效率时候,分别提交多次代码检测出的性能变化也可能较大),图片中的性能上面的性能为库函数,下面的的是自己写的。
排列
组合
else:
从python源代码中还看到了一种之前很少用到的语法,
for … in :
…
else:
…
在for in循环中判断条件然后break执行逻辑然后全部循环完了再else跳出,有趣!