排列组合:排序、排位、排
0 问题
1 基本计数法则
1 分类加法计数原则
定义:若具有性质A的事件有m个,具有性质B的事件有n个,则具有性质A或性质B的事件有m+n个。
例子:事件A:a点到b点乘火车有3条线路。
事件B:a点到b点乘汽车有2条线路。
则a点到b点共有(2+3)=5条不同线路
解释说明:完成一件事,有n类方法。在第1类办法中有m1种不同的方法,在第2类方法中有m2种不同的方法,……,在第n类方法中有mn 种不同的方法,则完成这件事的不同方法共有:N=m1+m2+...+mn
注意点:各类方法相互独立的。
2 分步乘法计数原则
定义:若具有性质A的事件有m个,具有性质B的事件有n个,则具有性质A与性质B的事件有m*n个。
例子:事件A:a点到b点有3条不同的路径。
事件B:b点到c点有2条不同的路径。
则a点到b点共有3*2=6条不同的路径。
解释说明:完成一件事,需要分成n个步骤。做第1步有m1种不同的方法,做第2步有m2种不同的方法,……,做第n步与mn种不同的方法,则完成这件事的不同方法共有:N=m1*m2*mn
注意点:各个步骤相互依存。
3 分类计数原理与分布计数原理相同点与不同点
- 相同点: 分类计数原理与分布计数原理都是涉及完成一件事的不同方法的种数问题。
- 不同点: 分类计数原理与“分类”有关,各种方法相互独立,用其中任何一种方法都可以完成这件事。 分布计数原理与“分布”有关,各个步骤相互依存,只有各个步骤都完成了,这件事才算完成。
2 排列
1 排列描述
排列的定义
设 A={a1,a2,...,an}是n个不同元素的集合,m满足0≤m≤n。任取A中m个元素按顺序排成一列,称为从A中取m个的一个无重排列。
例子:
排列符号定义
从n个不同元素中,任取m个元素的所有排列的个数叫做从n个元素的排列数,用符号表示。
排列计算公式
选排列:当m<n时的排列称为选排列。选排列数公式:
全排列:当m=n时的排列称为全排列。全排列公式:
2 排列的优缺点
优点
可以辅助计算排列的概率问题。
缺点
此公式只适用m个元素是不同的情况,如果当m个元素有重复的时候就不能用此方法,使用受到条件限制。
排列应用
可以计算彩票的中奖概率,分析彩票的购买的排列情况,如七位数等。
3 圆周排列
1 圆周排列描述
圆周排列定义
从n个不同的数中不重复的取出m个沿一圆圈排列,称为一个圆周排列,表示为 。
圆周排列符号定义
从n个不同的数中不重复的取出m个沿一圆圈排列,得到的排列数,符号表示为 。
例子
圆周排列计算公式
2 圆周排列优缺点
优点
可以快速计算圆周排列的排列数。
缺点
不区分圆周排列是逆时针还是顺时针排列。
圆周排列应用
在圆形的马场比赛跑道,可以计算赛马的中奖概率。
4 组合
1 组合描述
组合的定义
从n个不同元素中不重复的选取m个元素,组成一组(不管其顺序),称为从n个不同元素中选取m个元素的无重组合。
组合符号定义
从n个不同元素中不重复的选取m个元素,组成一组(不管其顺序),得到的排列数用符号 表示。
例子:
组合计算公式
组合数的两个性质: ,
2 组合的优缺点
优点
生活中经常计算概率的时候经常需要用到组合辅助计算。
缺点
输入的n个元素必须互不相同。
组合应用
可以计算彩票的中奖概率,分析彩票的购买的组合情况,如组合3。
5 排列组合算法
1 排列算法
递归法、字典序法、递增进位数制法、递减近位数制法、邻位交换法、n进制法……
递归算法
输入:{1,2,3}。输出:123,132,213,231,312,321
算法思想:第一位取1,对2,3全排列。第一位取2,对1,3全排列。第一位取3,对1,2全排列。
字典序法
算法思想:设p是集合{1、2、3、4……n}的一个全排列:p=p1p2.....pn
1、从排列的右端往左,找出第一个比右边数字小的数字序号j,也就是第一个顺序两位数。
2、从右端开始到j.找出第一个比pj大的数字pk。
3、交换pj,pk
4、对交换后的集合中p(j+1)......pn顺序逆转,也就是顺序排列。
2 排列算法描述
排列算法输入数据、输出结果
* @param object 输入可选元素
* @param m 选取m个数排列
* @param out 输出排列后的元素集
* @return 输出排列总数
类和方法描述
类名:Permutation.java
方法:
getCountByLine(Object[] object, long m)获取无重排列数目
getCountByCircle(Object[] object, int m)获取无重圆周排列数目
getAllPermutation(Object[] in, List<Object[]> out, int index)得到所有排列
java代码实现
类名:Permutation.java
方法:
getCountByLine(Object[] object, long m)获取无重排列数目
getCountByCircle(Object[] object, int m)获取无重圆周排列数目
getAllPermutation(Object[] in, List<Object[]> out, int index)得到所有排列
java代码实现
/**
* 获取无重线排列总数目
* 描述:从n个元数中选取m个元数进行全排列,得出一共有多少种排法
* 公式:A(m,n)=m!/(n-m)!
* 优缺点:输入数据必须互不相同。求阶乘时,使用了for循环,避免了递归方法导致内存溢出的风险。
* (object数组中元素总数为n,从object数组中选出的m个数排列)
* @param object 输入可选元素
* @param m 选取m个数排列
* @return 输出排列总数
*/
public static BigInteger getCountByLine(Object[] object, long m) {
long n = object.length;
BigInteger result = MathUtils.factorial(n-m+1, n);//计算n-m+1到n的阶乘
return result;
}
/**
* 获取无重圆周排列数目
* 描述:从n个数中选取m个数进行圆周排列,获取一共有多少种排法
* 公式:Q(m,n)=A(m,n)/m=m!/(m*(n-m)!)
* 优缺点:输入数据必须互不相同。m定义了int类型,限制了计算范围。
* (object数组中元素总数为n,从object数组中选出的m个数圆周排列)
* @param object 输入可选元素
* @param m 选取m个数排列
* @return 输出排列总数
*/
public static BigInteger getCountByCircle(Object[] object, int m) {
int n = object.length;
BigInteger result = MathUtils.factorial(n-m+1, n).divide(BigInteger.valueOf(m));//计算n-m+1到n的阶乘除以m
return result;
}
/**
* 全排列(递归)
* 例:输入{1,2,3}输出{1,2,3}{1,3,2}{2,1,3}{2,3,1}{3,1,2}{3,2,1}
* 描述:输入一个数组,对数组中的所有元素进行全排列,把每种排列放入out集合中。
* 优缺点:输入数据必须互不相同。使用了递归的算法,当输入数据比较大时,会存在内存溢出的危险。
* @param in 输入数据
* @param out 输出数据
* @param index 默认为0
*/
public static void getAllPermutation(Object[] in, List<Object[]> out, int index) {
if(index == in.length-1) {
Object[] outTemp = in.clone();
out.add(outTemp);
}
for(int i=index; i<in.length; i++) {
swap(in,index,i);
getAllPermutation(in,out,index+1);//递归
swap(in,index,i);
}
}
/**
* 获取所有排列
* 例:输入{1,2,3}输出{1}{2}{3}{1,2}{2,1}{1,3}{3,1}{2,3}{3,2}{1,2,3}……
* 描述:输入一个数组,先从数组中选取一个数全排列,再选取两个两个数全排列,再选取三个数全排列,把所有的得到的排列放入out集合中。
* 优缺点:输入元素必须互不相同。
* @param in 输入数据
* @param out 输出数据
*/
public static void getSelectPermutaion(Object[] in, List<Object[]> out) {
List<Object[]> outTemp = new ArrayList<Object[]>();
for(int i=0; i<in.length; i++) {
Combination.getCombination(in, outTemp, i+1);//获取所有组合
}
for(int i=0; i<outTemp.size(); i++) {
getAllPermutation(outTemp.get(i), out, 0);//对每种组合进行全排列
}
}
/**
* 交换下标为i,j两个元素
* 描述:对object数组的i,j位置的元素进行交换
* @param object
* @param i
* @param j
*/
public static void swap(Object[] object, int i, int j) {
Object temp = object[i];
object[i] = object[j];
object[j] = temp;
}
排列算法异常和误差
输入数据时检查数据类型,输出的数据被转换成Object类型了。
适用或不适用场景
输入的元素是互不相同的,并且有序排列。可以据此分析彩票的中奖率,比如七位数。
3 组合算法描述
二进制法
算法思想:首先初始化,将数组前n个元素置1,表示第一个组合为前n个数。然后从左到右扫描数组元素值的“10”组合,找到第一个“10”组合后将其变为 “01”组合,同时将其左边的所有“1”全部移动到数组的最左端。当第一个“1”移动到数组的m-n的位置,即n个“1”全部移动到最右端时,就得到了最后一个组合。
组合算法输入数据、输出结果
* @param in 输入可选元素
* @param m 选取m个数排列
* @param out 输出组合后的元素集
* @return 返回组合后的数目
类和方法描述
类名:Combination.java
方法:
getCount(Object[] in, long m)获取组合数
getCombination(Object[] in, List<Object[]> out, int m)获取组合后的所有情况
java代码实现
/**
* 获取无重组合数目
* 描述:从n个数中选取m个元素组成一组,得到一共有多少种不同的组合
* 公式:C(m,n)=n!/(m!*(n-m)!)
* @param in 输入可选元素
* @param m 每次选m个
* @return 返回组合后的总数目
*/
public static BigInteger getCount(Object[] in, long m) {
long n = in.length;
BigInteger result = BigInteger.valueOf(1);
if(m<2/n) {//增加计算效率
result = MathUtils.factorial(n-m+1, n).divide(MathUtils.factorial(1, m));//计算公式
} else {
result = MathUtils.factorial(m+1, n).divide(MathUtils.factorial(1, n-m));//计算公式
}
return result;
}
/**
* 组合(二进制法)
* 例:输入{1,2,3}从中选2个的数组合。输出:{1,2}{1,3}{2,3}
* 描述:从输入的元素中,选m个元素组合,把所有的组合存在out中
* @param in 输入数组
* @param out 输出组合后的数组集
* @param m 取出m个数
*/
public static void getCombination(Object[] in, List<Object[]> out, int m) {
int n = in.length;
int[] array = new int[n];
for(int i=0; i<n; i++) {//初始化
if(i<m)
array[i] = 1;
else
array[i] = 0;
}
List<int[]> list = new ArrayList<int[]>();
boolean flag = true;
while(flag) {
int[] intTemp = array.clone();
list.add(intTemp);
for(int i=0; i<n-1; i++) {
if(array[i]==1 && array[i+1]==0) {
array[i]=0;//交换i,i+1的值
array[i+1]=1;
if(array[0]==0) {//把i位置左边的1都移到最左边
for(int k=0,j=0;k<i;k++) {
if(array[k]==1) {
array[j]=1;
array[k]=0;
j++;
}
}
}
break;
}
if(i==n-2) flag = false;
}
}
for(int i=0; i<list.size(); i++) {//把二进制中的1转换成对应的数组
Object[] objTemp = new Object[m];
for(int j=0,k=0;j<n;j++) {
if(list.get(i)[j]==1) {
objTemp[k] = in[j];
k++;
}
}
out.add(objTemp);
}
}
组合算法异常和误差
输入数据时检查数据类型,输入数据的可选范围是受限的。输出的数据为Object类型了,可以通过类型转换变为与输入数据一致的类型。
适用或不适用场景
输入的元素必须是互不相同的,并且属于抽取不放回的情况,抽出是无序的。可以据此分析彩票的中奖率,比如组合3。
6 本章总结
排列与组合的区别
7 开源共享
PPT:http://yunpan.cn/cFwhKFiKraHWr 访问密码 5662
开源代码:http://yunpan.cn/cFwhjdaqB9iFs 访问密码 624d
http://www.cnblogs.com/baiboy