排列和组合-蓝桥杯

一、排列函数permutations()

  • xitertools.permutations(iterable, r = None)

  • 功能:连续返回由 iterable序列中的元素生成的长度为r的排列。

  • 如果r未指定或为None,r默认设置为 iterable 的长度,即生成包含所有元素的全排列。

  • permutations()按什么顺序输出序列?

答:按元素的位置顺序输出元素的排列,也就是说输出排列的顺序是位置的字典序。

例如s =['b','a','c']执行permutations(s),输出“bac bca abc acb cba cab”,并不是按字符的字典序输出排列,而是按位置顺序输出。

s =['b','a','c']的3个元素的位置是'b'=1、'a'=2、'c'=3,输出的排列“bac bca abc acb cba cab”,按位置表示就是“123 132 213 231 312 321”,这是按从小到大的顺序输出的。

  • 如果有相同的元素,不同位置的元素被认为不同。

例如s =[a,a,c,执行permutations(s),输出“aac aca aac aca caa caa”。

  • 易错点

初学者容易犯一个错误:把元素的值当成了位置。

例如s =['1','3','2'],执行permutations(s),输出“132 123 312 321 213 231”看起来很乱,实际上是按3个元素的位置'1'=1、'3'=2、'2'=3输出有序的排列的。

  • 如何输出看起来正常的“123 132 213 231 312 321”?

答:先把s =['1','3','2']用sort()排序为[1,2,3]再执行permutations()。

  • 练习

二、组合函数combinations()

  • permutations()输出的是排列,元素的排列是分先后的,“123”和“321”不同。

  • 有时只需要输出组合,不用分先后,此时可以用combinations()函数。

  • 如果列表s中有相等的元素,那么不同位置的相等元素被认为不同。

  • 如果要去重怎么办?用集合,s用集合{}表示。

  • 当用集合来表示的时候,输出的组合结果的顺序是随机的。

  • 如果要去重且输出按字典序,就先用set()去重,再排序,最后组合。

三、手写排列和组合代码

  • 在某些场景下,Python提供的排列函数并不能用,需要手写代码实现排列组合。

  • 在“DFS与排列组合”中给出了基于DFS的手写方法。

  • 此时还没学到“DFS”,因此下面给出几种简单的手写方法。


  • 手写排列代码

从{1,2,3,4}中选3个的排列,有24种。

最简单直接无技巧的手写排如下。

[优缺点] 简单且效果很好,但是非常笨拙。如果写5个以上的数的排列组合,代码冗长无趣。


  • 手写组合代码

排列数需要分先后,组合数不分先后。

把求组合的代码,去掉if,然后从小到大打印即可。

从{1,2,3,4}中选3个的组合,有4种。


  • 二进制法手写组合代码

一个包含n个元素的集合{a0,a1,a2,a3,..., an-1},它的子集有{空},{a0},{a1},{a2},..., {a0,a1,a2},...,{a0,a1,a2,a3,...,an-1},共2的n次方个。

用二进制的概念进行对照,子集正好对应了二进制。 例如n =3的集合{a0,a1,a2},它的子集和二进制数的对应关系是:

每个子集对应了一个二进制数。二进制数中的每个1,对应了子集中的某个元素。

子集中的元素,是不分先后的,这正符合组合的要求。


实例1操作如下:

下面的代码通过处理每个二进制数中的1,打印出了所有的子集


实例2操作如下:

输出n个数中任意m个数的组合。

根据上面子集生成的二进制方法,一个子集对应一个二进制数;一个有m个元素的子集,它对应的二进制数中有m个1。

问题转化为:查找1的个数为m个的二进制数,这些二进制数对应了需要打印的子集。

如何判断二进制数中1的个数为m个?简单的方法是对这个n位的二进制数逐位检查,共需要检查n次。

有一个更快的方法,可以直接定位二进制数中1的位置,跳过中间的0。用到一个神奇操作:k=k&(k-1),功能是消除k的二进制数的最后一个1。连续进行这个操作,每次消除一个1,直到全部消除,操作次数就是1的个数。例如二进制数1011,经过连续3次操作后,所有1都消除了:

利用这个操作,可以计算出二进制数中1的个数。步骤:

1)用k=k &(k-1)清除k的最后一个1;

2)num++用num统计1的个数;

3) 继续上述操作,直到k =0。

输出按字典序输出,从小到大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ranzi.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值