问题: 十本不同的书放在书架上。现重新摆放,使每本书都不在原来放的位置。有几种摆法?


 


这个问题推广一下,就是错排问题,是组合数学中的问题之一。考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n)。 研究一个排列错排个数的问题,叫做错排问题或称为更列问题。


 


错排问题最早被尼古拉·伯努利和欧拉研究,因此历史上也称为伯努利-欧拉的装错信封的问题。这个问题有许多具体的版本,如在写信时将n封信装到n个不同的信封里,有多少种全部装错信封的情况?又比如四人各写一张贺年卡互相赠送,有多少种赠送方法?自己写的贺年卡不能送给自己,所以也是典型的错排问题。


——摘自百度百科


递推公式


当n个编号元素放在n个编号位置,元素编号与位置编号各不对应的方法数用D(n)表示,那么D(n-1)就表示n-1个编号元素放在n-1个编号位置,各不对应的方法数,其它类推.


 


第一步,把第n个元素放在一个位置,比如位置k,一共有n-1种方法;


第二步,放编号为k的元素,这时有两种情况:


⑴把它放到位置n,那么,对于剩下的n-1个元素,由于第k个元素放到了位置n,剩下n-2个元素就有D(n-2)种方法;


⑵第k个元素不把它放到位置n,这时,对于这n-1个元素,有D(n-1)种方法;


综上得到


 


D(n) = (n-1) [D(n-2) + D(n-1)]


特殊地,D(1) = 0, D(2) = 1.


下面通过这个递推关系推导通项公式:


 


为方便起见,设D(k) = k! N(k), k = 1, 2, …, n,


则N(1) = 0, N(2) = 1/2.


n ≥ 3时,n! N(n) = (n-1) (n-1)! N(n-1) + (n-1)! N(n-2)


即 nN(n) = (n-1) N(n-1) + N(n-2)


于是有N(n) - N(n-1) = - [N(n-1) - N(n-2)] / n = (-1/n) [-1/(n-1)] [-1/(n-2)]…(-1/3) [N(2) - N(1)] = (-1)^n / n!.


因此


N(n-1) - N(n-2) = (-1)^(n-1) / (n-1)!,


N(2) - N(1) = (-1)^2 / 2!.


相加,可得


N(n) = (-1)^2/2! + … + (-1)^(n-1) / (n-1)! + (-1)^n/n!


因此


D(n) = n! [(-1)^2/2! + … + (-1)^(n-1)/(n-1)! + (-1)^n/n!].


此即错排公式


 


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


在这里我还看到了一种对递推公式其他的理解:


 


一个人写了n封不同的信及相应的n个不同的信封,他把这n封信都装错了信封,问都装错信封的装法有多少种?


分析:

假设有信封A,B,C,D...;信件1,2,3,4...;全部装错有f(n)种情况。



这里分两种情况考虑:①前面n-1个信封全部装错;②前面n-1个信封有一个没有装错其余全部装错。

①前面n-1个信封全部装错:因为前面n-1个已经全部装错了,所以第n封只需要与前面任一一个位置交换即可,总共有f(n-1)*(n-1)种情况。


②前面n-1个信封有一个没有装错其余全部装错:为什么考虑这种情况,因为n-1个信封中如果有一个没装错,那么我们把那个没装错的与n交换,即可得到一个全错位排列情况。

得到这种情况的种数也很简单,即是忽略掉那个没装错的情况去排列其他的信封的全错排种数f(n-2)*(n-1)。


 


综上,f(n)=f(n-1)*(n-1)+f(n-2)*(n-1)=(n-1)*(f(n-1)+f(n-2));


 


 


 


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


例题:



国庆期间,省城HZ刚刚举行了一场盛大的集体婚礼,为了使婚礼进行的丰富一些,司仪临时想出了有一个有意思的节目,叫做"考新郎",具体的操作是这样的:

  首先,给每位新娘打扮得几乎一模一样,并盖上大大的红盖头随机坐成一排; 然后,让各位新郎寻找自己的新娘.每人只准找一个,并且不允许多人找一个. 最后,揭开盖头,如果找错了对象就要当众跪搓衣板...

看来做新郎也不是容易的事情...

假设一共有N对新婚夫妇,其中有M个新郎找错了新娘,求发生这种情况一共有多少种可能.


Input


输入数据的第一行是一个整数C,表示测试实例的个数,然后是C行数据,每行包含两个整数N和M(1<M<=N<=20)。

Output


对于每个测试实例,请输出一共有多少种发生这种情况的可能,每个实例的输出占一行。

Sample Input

2
2 2
3 2

Sample Output

1
3


错排:有n对新婚夫妇,对应新娘在(1-n)n个位置上,让各位新郎寻找自己的新娘,全部找错共有多少种找法,记n个元素的错排总数为f(n)假设有n个新郎,第一个新郎可找  2-n   的任一个位置,共n-1种找法

设第一个新郎找到了第 k 个位置,若此时第 k 个新郎正好找到了第 1 个位置,则只要将剩下的 n-2 错排,即f (n-2),就能实现全错排,若第k个新郎没有找到第1个位置,则将 n-1 个位置错排,即为f(n-1)

由递推可得,f(n)=(n-1)*(f(n-1)+f(n-2))

 N个新郎找出M个新郎找错新娘,我们需要用到排列组合在 N 个新郎中选出是哪 M 个新娘,就是C(n,m)=n!/m!/(n-m)!

用该组合数再去乘错位排列数就是该结果

 



1 #include<cstdio>
2 #include<algorithm>
3 #define ll long long int
4 using namespace std;
5 ll f[30];
6 ll c[30];
7 int main()
8 {
9 ll n,m,t,i,ans;
10 f[1]=0;
11 f[2]=1;
12 for(i=3;i<=30;i++)///错排
13 {
14 f[i]=(i-1)*(f[i-1]+f[i-2]);
15 }
16 c[0]=1;
17 c[1]=1;
18 for(i=2;i<=30;i++)///求前i项阶乘之和
19 {
20 c[i]=c[i-1]*i;
21 }
22 scanf("%lld",&t);
23 while(t--)
24 {
25 scanf("%lld%lld",&n,&m);
26 ans=c[n]/(c[m]*c[n-m]);
27 printf("%lld\n",ans*f[m]);
28 }
29 }


 

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Problem Description


HDU 2006'10 ACM contest的颁奖晚会隆重开始了!

为了活跃气氛,组织者举行了一个别开生面、奖品丰厚的抽奖活动,这个活动的具体要求是这样的:


首先,所有参加晚会的人员都将一张写有自己名字的字条放入抽奖箱中;

然后,待所有字条加入完毕,每人从箱中取一个字条;

最后,如果取得的字条上写的就是自己的名字,那么“恭喜你,中奖了!”


大家可以想象一下当时的气氛之热烈,毕竟中奖者的奖品是大家梦寐以求的Twins签名照呀!不过,正如所有试图设计的喜剧往往以悲剧结尾,这次抽奖活动最后竟然没有一个人中奖!

我的神、上帝以及老天爷呀,怎么会这样呢?

不过,先不要激动,现在问题来了,你能计算一下发生这种情况的概率吗?

不会算?难道你也想以悲剧结尾?!


 


Input


输入数据的第一行是一个整数C,表示测试实例的个数,然后是C 行数据,每行包含一个整数n(1<n<=20),表示参加抽奖的人数。


Output


对于每个测试实例,请输出发生这种情况的百分比,每个实例的输出占一行, 结果保留两位小数(四舍五入),具体格式请参照sample output。


Sample Input



12


Sample Output



50.00%


 


 


没有人能够获奖说明每个人都拿到的不是带有自己名字的字条,与信封问题一样是错位排列,求最后的概率等于错位排序的排列个数/所有可能的排列个数。




1 #include<stdio.h>
2 #include <stdlib.h>
3 int main()
4 {
5 long long f[21]= {0};//定义不超过21的长整型数组用来存放错误的抽取
6 f[1]=0;
7 f[2]=1;
8 for(int i=3; i<21; i++)
9 f[i]=(i-1)*(f[i-1]+f[i-2]);//递推求值
10 int C;
11 scanf("%d",&C);
12 while(C--)
13 {
14 int n;
15 scanf("%d",&n);
16 long long sum=1;
17 for(int j=2; j<=n; j++)
18 sum*=j;//求阶乘,表示的是所有的抽取方法
19 double b=100.0*f[n]/sum;
20 printf("%.2f%%\n",b);
21 }
22 return 0;
23 }


 


作者:王陸