大家好,欢迎来到IT知识分享网。
简介
全错位排列被著名数学家欧拉(Leonhard Euler,1707-1783)称为“组合数论的一个妙题”的“装错信封问题”的两个特例。
公式证明
n个相异的元素排成一排 a1,a2,…,an a 1 , a 2 , … , a n ,则 ai a i 不在第i位的排列数为:
证明:
设1,2,…,n的全排列 t1,t2,…,tn t 1 , t 2 , … , t n 的集合为I,而使 ti=i t i = i 的全排列的集合记为Ai(1<=i<=n),
则 Dn=|I|−|A1∪A2∪…∪An| D n = | I | − | A 1 ∪ A 2 ∪ … ∪ A n |
所以 Dn=n!−|A1∪A2∪…∪An| D n = n ! − | A 1 ∪ A 2 ∪ … ∪ A n |
注意到 |Ai|=(n−1)! | A i | = ( n − 1 ) ! ,
|Ai∩Aj|=(n−2)! | A i ∩ A j | = ( n − 2 ) ! ,…, |A1∩A2∩…∩An|=0!=1 | A 1 ∩ A 2 ∩ … ∩ A n | = 0 ! = 1
由容斥原理:
Dn=n!−|A1∪A2∪…∪An|=n!−C1n(n−1)!+C2n(n−2)!−C3n(n−3)!!+…+(−1)nCnn∗0!=n!(1−1/1!+1/2!−…+(−1)n/n!) D n = n ! − | A 1 ∪ A 2 ∪ … ∪ A n | = n ! − C n 1 ( n − 1 ) ! + C n 2 ( n − 2 ) ! − C n 3 ( n − 3 ) ! ! + … + ( − 1 ) n C n n ∗ 0 ! = n ! ( 1 − 1 / 1 ! + 1 / 2 ! − … + ( − 1 ) n / n ! )
应用
1、简单排列
2、递推公式
2.1递推公式
用A、B、C……表示写着n位友人名字的信封,a、b、c……表示n份相应的写好的信纸。把错装的总数为记作f(n)。假设把a错装进B里了,包含着这个错误的一切错装法分两类:
(1)b装入A里,这时每种错装的其余部分都与A、B、a、b无关,应有f(n-2)种错装法。
(2)b装入A、B之外的一个信封,这时的装信工作实际是把(除a之外的)(n-1 )份信纸b、c……装入(除B以外的)n-1个信封A、C……,显然这时装错的方法有f(n-1)种。
总之在a装入B的错误之下,共有错装法f(n-2)+f(n-1)种。a装入C,装入D……的n-2种错误之下,同样都有f(n-2)+f(n-1)种错装法,因此:
2.2概率和极限
有n封不同的信,和n个信封印上了相应的地址。将这n封信放入n个信封中。
A)求至少有一封信刚好放进正确信封中的概率pn
B)当n趋向于无穷大时 ,pn的极限是多少?
大家可以想象一下当时的气氛之热烈,毕竟中奖者的奖品是大家梦寐以求的Twins签名照呀!不过,正如所有试图设计的喜剧往往以悲剧结尾,这次抽奖活动最后竟然没有一个人中奖!——即上述信封全都装错
我的神、上帝以及老天爷呀,怎么会这样呢?
不过,先不要激动,现在问题来了,你能计算一下发生这种情况的概率吗?
不会算?难道你也想以悲剧结尾?
Input
输入数据的第一行是一个整数C,表示测试实例的个数,然后是C 行数据,每行包含一个整数n(1 < < <script type=”math/tex” id=”MathJax-Element-2026″><</script>n <= <= <script type=”math/tex” id=”MathJax-Element-2027″><=</script>20),表示参加抽奖的人数。
Output
对于每个测试实例,请输出发生这种情况的百分比,每个实例的输出占一行, 结果保留两位小数(四舍五入),具体格式请参照sample output。
Sample Input
1 2
Sample Output
50%
#include<iostream> using namespace std; int main() { int c,n,i; //定义为double型就不需要使用long long,同时输出不需要转换类型 double b[21]={
0,1,2,6,24,120,720},a[21]={
0,0,1,2}; //计算全错位排列数 for(i=4;i<21;i++) { //递推公式f(0)=0;f(1)=0;f(2)=1;f(3)=2; a[i]=(i-1)*(a[i-1]+a[i-2]); } //计算阶乘 for(i=7;i<21;i++) { b[i]=i*b[i-1]; } cin>>c; while(c--) { cin>>n; //因为要输出的是百分比 printf("%.2lf%%\n",100*a[n]/b[n]); //输出用printf比cout方便得多 } return 0; }
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <cstdlib> using namespace std; int main (){ int m; __int64 C[30][30],wrongqueue[15]={
0,0,1,2};//C[][]为组合,wrongqueue为全错位排列 [0]用不到 for(int i=1;i<=25;i++) { C[i][1]=i; C[i][0]=1; for(int j=2;j<=i;j++) { C[i][j]=C[i][j-1]*(i-j+1)/j; } } //全错位排列 for(int i=3;i<=12;i++) wrongqueue[i]=(i-1)*(wrongqueue[i-1]+wrongqueue[i-2]); scanf("%d",&m); while(m!=0) { __int64 sum=0; if(m%2==0) for(int i=m/2;i<m;i++) sum=sum+C[m][i]*wrongqueue[m-i]; else for(int i=m/2+1;i<m;i++) sum=sum+C[m][i]*wrongqueue[m-i]; printf("%I64d\n",sum+1); scanf("%d",&m); } return 0; }
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <math.h> using namespace std; int a[21]; double PaC(int n, int m) //Permutation and combination. { double s=1,i; //小技巧,doulbe型的定义 for(i=0;i<m;i++) s*=(n-i)/(i+1); return s; } void init() //错排的序列 { int i; a[0]=0;a[1]=0;a[2]=1; for(int i=3;i<=21;i++) a[i]=(a[i-1]+a[i-2])*(i-1); return ; } int main() { int N,i; double sum; init(); while(~scanf("%d",&N),N) { int m; sum=0; if(N%2==0) m=N/2; else m=N/2+1; for(i=m;i<N;i++) sum+=PaC(N, i)*a[N-i]; //Cnm*a[N-m];即从中选出m个正确的,则有n-m个错排的。根据分步计数原理可知。 printf("%lld\n",(long long int)(sum+1));// } return 0; }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/139233.html