前言
在
上一篇文章中,我们在求取抽卡片的所有次数时,用到了全排列,当时提到由于总卡片数是35,这个数字产生的全排列过于巨大,计算起来耗时久,因此我们只用10和12两个数字进行示例说明。
事实上,在求全排列的个数时,
上一篇文章中编写的程序用的是最容易被同学们理解但却是最暴力的方法,尽管用暴力方法可以将所有方案列写出来,但用该程序来求全排列的个数问题时并不是最优的。
这一篇文章中,我们来研究一下含重复数字的全排列个数求解问题。
不重复数字的全排列
对于求不重复数字的全排列,其实很容易计算,只要学习过高中数学的同学都会,那就是求这个全排列数目的阶乘即可。在
Python
中,求阶乘有现成的函数:
或者,用
scipy
中的
perm
来求一个数的全排列数:
如果自定义求,也是容易的:
计算含重复数字全排列数思路一
如果给出的数字中有重复的,比如给出
1、1、1、2、2、3
这6个数,要求由它组成的不同6位数个数,应该如何求取呢?
当6个数字不重复时,我们可以直接求6的阶乘,对于其中重复的数字,需要进行剔除,比如其中的
1
有三个,
2
有两个,这就需要将这些情况剔除,数学表示为:
用程序计算如下:
在得到这个函数之后,我们再来看看
上篇文章中关于对35张卡片的抽取总方法数有多少种:
我们来测试一下利用这种方式计算35张卡片的总抽取次数所花时间:
可以看出,只需要1毫秒多,而如果利用上一篇文章中的方法,计算35张卡片的总抽取次数也许需要几个小时都有可能。
可以用这个函数来验证一下上一篇文章中提到的当卡片为10和12时的总次数是否一致:
从结果输出来看,是与上一篇文章中的数字相符合的。
计算含重复数字全排列数思路二
另一种计算含重复数字的全排列数算法是分步法,同样以
1、1、1、2、2、3
这6个数为例来说明。
为了组成六位数,可考虑逐步从这六个数字中选取,首先我们将三个
1
选取出来,这三个
1
在六位数中可能出现位置的情况是求在6个数中选3个的组合(
同学们思考下为什么不是排列?),其次再将两个
2
选取出来,在6位数中余下的三个位置上安排这两个
2
,即求在3个位置中选取2个位置的组合(
理由同上),最后来安排一个
3
,这样计算的结果可由下式表示:
计算可得:
60。
这与第一种方法计算结果一致,采用这个计算公式,我们来看35张卡片用程序计算的代码演示是怎样的:
来测试一下这个方法的计算时间:
从运行结果来看,与方法一在时间上相差无几。
小结
本文着重对于含重复数字的全排列个数进行了分析,对于这类问题的较方便的解法是先整体后去重或者分步组合法,这两种计算方法相对于将全部排列都列写出来再统计而言,是一个质的飞跃,充分利用数学知识,确实对生活中的许多问题都会有极大的帮助。