排列与排列数、组合与组合数


排列与排列数、组合与组合数


排列与排列数

  1. \(n\)个不同元素中,任取\(m(m \le n)\)个元素,按照一定的顺序排成一列,叫做从\(n\)个不同元素中取出\(m\)个元素的一种排列。

    • 注意:排列的元素完全相同,顺序也完全相同
  2. 公式:\(P^m_n=\frac{n!}{(n-m)!}\)

    • 如何理解这个公式:

      1. 例1:三名同学,成绩前两名有多少种可能性?

        1. 第一名:3种可能性
        2. 第二名:(3-1)种可能性
        3. 根据乘法原理,共有\(3*(3-1)\)种可能性
      2. 例2:七名同学,成绩前五名有多少种可能性?

        • 由例一,易知有\(7*(7-1)*(7-2)*...*(7-5+1)\)
      3. 由例1例2进行进一步推导,用\(m,n\)代替具体的数字,可得

        • \(n(n-1)(n-2)(n-3)...(n-m+1)\)
      4. 整理可得,

        \(n(n-1)(n-2)(n-3)...(n-m+1)=\frac{1*2*3*...(n-2)(n-1)n}{1*2*3*...(n-m-1)(n-m)}\)

        (即乘上\(\frac{1*2*3*...(n-m-1)(n-m)}{1*2*3*...(n-m-1)(n-m)}\),目的是得到完整的、从1开始的可以化为阶乘形式的数)

      5. 继续整理,得到\(P^m_n=\frac{n!}{(n-m)!}\)

      6. 但是通过第3步得到的式子,其实是不需要阶乘的

        从大的数字开始往小乘,乘小的数字那么多个


组合与组合数

  1. \(n\)个不同元素中,任取\(m(m \le n)\)个元素并成一组,叫做从\(n\)个不同元素中取出\(m\)个元素的一个组合。

  2. 记号:\(C(n,k)\)\(\binom{n}{k}\)

  3. 公式:\(\binom{n}{k}=\frac{P(n,k)}{P(k,k)}\)

  4. 推导公式:\(\binom{n}{k}=\frac{\frac{k!}{(k-n)!}}{\frac{n!}{(n-n)!}}=\frac{k!}{n!(k-n)!}\)

  5. 用更常用的\(m,n\)来表示组合数公式,就得到了\(\binom{m}{n}=\frac{n!}{m!(n-m)!}\)

  6. 理解公式:

    1. 观察可知,其实组合数就是在排列数公式上除以一个\(m!\)\(P(k,k)\)其实就等于\(k!\)

    2. 思考一下:组合数与排列数的区别是组合数不要求顺序相同,所以如果按照排列数去求组合数,会出现一定数目的重复

    3. 打表找规律():

      1. 四选三(即m=3,n=4):

        1. 排列数:共\(P^3_4=\frac{4!}{(4-3)!}=\frac{4*3*2*1}{1}=24\)

          ABC,ABD,ACB,ACD,ADB,ADC,
          BAC,BAD,BCA,BCD,BDA,BDC,
          CAB,CAD,CBA,CBD,CDA,CDB,
          DAB,DAC,DBA,DBC,DCA,DCB;

        2. 观察,剔除重复项

          ABC,ABD,ACD,BCD \(4\)

        3. 找规律:保留了\(\frac{24}{6}\)种,即保留了\(\frac{4*3*2*1}{3*2*1}\)种,即\(C^m_n=\frac{P(m,n)}{m!}\)

        4. 嗯现在就假装推完了吧()

      从大的数字开始往小乘,乘“小的数字那么多”个,再除以“小的数字开始往小乘,乘小的数字那么多个”


拓展公式

  1. \(C^m_n=C^{n-m}_n\)

    其实就是反选,从五个里面取出四个的组合数就相当于从五个里面取一个(取哪四个等同于不取哪一个)

  2. \(C^{m-1}_n+C^m_n=C^m_{n+1}\)

    这个似乎在\(m\)\(n\)比较大的时候可以有效避免爆空间

  3. \(C^0_n+C^1_n+......+C^n_n=2^n\)

  4. \(C^r_r+C^r_{r+1}+......+C^r_n=C^{r+1}_{n+1}\)

  5. \(\displaystyle\sum_{i=0}^kC^i_nC^{k-i}_{m}=C^k_{m+n}\)

这三个公式还没悟等悟了在补充

代码实现

  1. 定义式

    int C(int m,int n){
        //这里用longlong是因为第6行乘完之后极有可能暂时超过了int能表示的范围
    	long long result = 1;
    	
    	for(int i = 0;i<n;i++){
    		result *= (m-i);
    	}
    	for(int i = 2;i<=n;i++){
    		result /= i;
    	}
    	return (int)result;
    }
    

    非常容易想到

  2. 递归版组合数求法

    int C(int m,int n){
    	if(m == n) return 1;
    	if(n == 1) return m;
    	return C(m-1,n)+C(m-1,n-1);
    } 
    

    根据拓展公式(2)写的,优点是非常好写(手和脑子只能懒一个),缺点是慢......

    这张图是以C(7,3)为例画的图(不得不说!这网站!实在是太强了)

    一眼就能看见相当多的重复运算

  3. 递推版组合数求法

    int C(int m,int n){
    	int c[n+1][m+1];
    	for(int i = 1;i<=m;i++){
    		c[1][i] = i;
    	}
    	for(int i = 2;i<=n;i++){
    		c[i][i] = 1;
    	}
    	for(int i = 2;i<n;i++){
    		for(int j = i+1;j<m;j++){
    			c[i][j] = c[i-1][j-1] + c[i][j-1];
    		}
    	}
    	for(int i = n+1;i<=m;i++){
    		c[n][i] = c[n-1][i-1] + c[n][i-1];
    	}
    	return c[n][m];
    }
    

    既然\(C(m,n)\)\(m,n\)都有关系,那么不妨开一个n行m列的二维数组(m行n列也行,自己调整一下)

    看上图的所有的叶子节点,都是\(m=n\)或者\(n=1\)

    \(m=n\)\(C(m,n)=1\)

    \(n=1\)\(C(m,n)=m\)

    因此,代码的第3~8行开出了如下表的一个二维数组

    1 2 3 4 5 6 7
    1 1 2 3 4 5 6 7
    2 1
    3 1

    接着,根据拓展公式二,我们可以知道这个二维数组的每一项等于其左侧值和左上值的和

    并且第m列(即上表中的第7列)除了最后一行都没有必要填上(填上也行)

    所以代码的第9~13行将二维数组填充到了如下表所示的程度

    1 2 3 4 5 6 7
    1 1 2 3 4 5 6 7
    2 1 3 6 10 15
    3 1

    然后第14~17行填完了最后一行

    最后返回值即可


    这是以C(36,24)为例的耗时(再大递推还是秒出,但递归我可能真的等不了了)


如何通俗的解释排列公式和组合公式的含义? - 浣熊数学的回答 - 知乎


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM