【组合数学】指数型母函数(多重集排列问题)

1.指数型母函数的定义

对于一个序列 a 0 , a 1 , a 2 ⋅ ⋅ ⋅ ⋅ a n a^0,a^1,a^2\cdot\cdot\cdot\cdot a^n a0,a1,a2an,我们称 a 0 0 ! + a 1 1 ! ⋅ x 1 + a 2 2 ! ⋅ x 2 + a 3 3 ! ⋅ x 3 + ⋅ ⋅ ⋅ + a n n ! ⋅ x n \frac{a_0}{0!}+\frac{a_1}{1!}\cdot x^1+\frac{a_2}{2!}\cdot x^2 +\frac{a_3}{3!}\cdot x^3+\cdot\cdot\cdot+\frac{a_n}{n!}\cdot x^n 0!a0+1!a1x1+2!a2x2+3!a3x3++n!anxn为该序列的指数型母函数

指数型母函数算是普通型母函数的延伸,不太懂母函数的可以先看这个,普通型母函数

2.多重集排列问题

有n种物体,给出每种物体的数量,现从中选出m个进行排列,问你排列方案有多少种。

3.指数型母函数求解多重集排列问题

证明过程点这里
过程很详细。

如何构造指数型母函数:

以三种物体,分别有2,2,3个为例,

则可构造如下母函数:

( 1 + x 1 1 ! + x 2 2 ! ) ⋅ ( 1 + x 1 1 ! + x 2 2 ! ) ⋅ ( 1 + x 1 1 ! + x 2 2 ! + x 3 3 ! ) (1+\frac{x^1}{1!}+\frac{x^2}{2!})\cdot(1+\frac{x^1}{1!}+\frac{x^2}{2!})\cdot(1+\frac{x^1}{1!}+\frac{x^2}{2!}+\frac{x^3}{3!}) (1+1!x1+2!x2)(1+1!x1+2!x2)(1+1!x1+2!x2+3!x3)

解释:

一个括号内的内容为一个多项式,第一个多项式代表第一种物品的选取情况,每一项的指数代表选取个数,如 x 2 x^2 x2代表第一种物品选两次,那么第一个多项式就涵盖了第一种物品的所有选取情况(0,1,2)。

第二个多项式同理,第三个多项式多了一项,因为第三种物体有3个,多一种选择的情况。

与普通型母函数对比:

①每一个多项式的指数都是连续的(因为指数代表的意义不同了,普通型母函数用指数代表权值,这里用指数代表选取个数)
②分母多了个阶乘,原因可见上面的链接,内有证明过程

求解:

构造完之后,和普通型母函数一样暴力展开就能得到答案了

继续上面的例子,将展开式展开即得:

1 + 3 x + 9 2 x 2 + 25 6 3 + 31 12 x 4 + 13 12 x 5 + 7 24 x 6 + 1 24 x 7 1+3x+\frac{9}{2}x^2+\frac{25}{6}^3+\frac{31}{12}x^4+\frac{13}{12}x^5+\frac{7}{24}x^6+\frac{1}{24}x^7 1+3x+29x2+6253+1231x4+1213x5+247x6+241x7

如果我们要求从上述物品中选取r个进行排列,只要找到指数为r的项,再将其系数乘以r的阶乘,原因可见上面证明过程的链接

对于指数型母函数来说,它的每一项的系数都是 a n n ! \frac{a_n}{n!} n!an,而其中真正有用的是 a n a_n an,这与用普通型母函数解决多重集组合问题单纯取系数不同。

4.模板

代码都有注释,需要注意的是结尾的输出要用%.0f,用%.0lf会输出0或-0我也不知道,用(int)强制转换也是不行的,因为会截断,而答案需要四舍五入(应该是精度问题造成的,不然答案本来就应该是整数)

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;

int  fact[15];

void fact_calculate(){//阶乘计算
    fact[0]=fact[1]=1;
    for(int i=2;i<=10;i++)
         fact[i]=i*fact[i-1];
}

int main()
{
    int n,m;
    double c[15],temp[15]; //c数组存放系数
    int num[15];      //存放第i种物品有几个
    fact_calculate();
    while(~scanf("%d%d",&n,&m)){
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
           scanf("%d",&num[i]);

        for(int i=0;i<=num[1];i++)   //c数组初始化第一个多项式的系数
            c[i]=1.0/fact[i];


        for(int i=2;i<=n;i++){    //i表示运算到第几个多项式
              memset(temp,0,sizeof(temp));
             for(int j=0;j<=m;j++)     //循环到m项就可以,因为求的是m排列,后面的项计算了也没有意义
                for(int k=0;k<=num[i]&&k+j<=m;k++)
                      temp[k+j]+=(1.0*c[j]/fact[k]);

             for(int j=0;j<=m;j++)
                  c[j]=temp[j];
        }

         printf("%.0f\n",c[m]*fact[m]);
    }

    return 0;

}

5.泰勒展开式的应用

e x e^x ex的泰勒展开:

e x = 1 + x + x 2 2 ! + x 3 3 ! + ⋅ ⋅ ⋅ e^x=1+x+\frac{x^2}{2!}+\frac{x^3}{3!}+\cdot\cdot\cdot ex=1+x+2!x2+3!x3+

可见其与我们构造的指数型母函数是恰好契合的

扩展:

e − x = 1 − x + x 2 2 ! − x 3 3 ! + ⋅ ⋅ ⋅ e^{-x}=1-x+\frac{x^2}{2!}-\frac{x^3}{3!}+\cdot\cdot\cdot ex=1x+2!x23!x3+

则:

e x + e − x 2 = 1 + x 2 2 ! + x 4 4 ! + ⋅ ⋅ ⋅ \frac{e^x+e^{-x}}{2}=1+\frac{x^2}{2!}+\frac{x^4}{4!}+\cdot\cdot\cdot 2ex+ex=1+2!x2+4!x4+

e x − e − x 2 = 1 + x + x 3 3 ! + ⋅ ⋅ ⋅ \frac{e^x-e^{-x}}{2}=1+x+\frac{x^3}{3!}+\cdot\cdot\cdot 2exex=1+x+3!x3+

具体应用:

可看红色病毒 这题

题解:点击跳转

6.练手题目

  1. 排列组合
    这应该是入门的板子题,我刚开始做的时候用了dfs的做法,虽然都过了,但相比之下母函数的做法要快得多,点此处查看题解
  2. 红色病毒
    运用到了高数里的泰勒展开,点此处查看题解
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 题目描述:给定一个包含n个元素的多重集合s,找出其中重数最大的元素,即众数。 解题思路:可以使用哈希表来统计每个元素出现的次数,然后找出出现次数最多的元素即可。 具体实现:首先定义一个哈希表,遍历多重集合s,将每个元素出现的次数记录在哈希表中。然后遍历哈希表,找出出现次数最多的元素,即为众数。 代码实现: ``` def find_mode(s): # 定义一个哈希表 count = {} # 遍历多重集合s,统计每个元素出现的次数 for x in s: if x in count: count[x] += 1 else: count[x] = 1 # 找出出现次数最多的元素 mode = max(count, key=count.get) return mode ``` 测试样例: ``` s = [1, 2, 2, 2, 3, 5] print(find_mode(s)) # 输出2 ``` ### 回答2: 众数问题是一个经典的算法问题,其解法通常可以用于数据挖掘、统计分析等领域。在本问题中,我们需要设计算法来寻找多重集合中的众数,即出现次数最多的元素。 一种简单的解法是使用哈希表来记录元素出现的次数,然后遍历哈希表,找到出现次数最多的元素。具体步骤如下: 1. 创建一个哈希表,用于记录每个元素出现的次数。 2. 遍历多重集合s,对每个元素进行如下操作: a. 如果元素在哈希表中不存在,则将其添加到哈希表中,并将出现次数设置为1。 b. 如果元素在哈希表中已经存在,则将其出现次数加1。 3. 遍历哈希表,找到出现次数最多的元素,即为众数。 该算法的时间复杂度为O(n),其中n为多重集合中元素的个数。由于哈希表的查找和添加操作的时间复杂度都为O(1),因此该算法在实际应用中具有较高的效率。 除了使用哈希表,还有一种更为简洁的解法。我们可以利用抵消的思想来寻找众数。具体步骤如下: 1. 初始化候选众数为s中的第一个元素,计数器初始化为1。 2. 遍历多重集合s的剩余部分,对每个元素进行如下操作: a. 如果该元素等于候选众数,则将计数器加1。 b. 如果该元素不等于候选众数,则将计数器减1。 如果计数器减为0,则将候选众数更换为当前元素,并将计数器重新设置为1。 3. 最终剩下的元素为候选众数。我们可以遍历整个多重集合s来确认该元素是否为真正的众数。 该算法的时间复杂度为O(n),由于只需要遍历一遍多重集合s,因此该算法在实际应用中具有更高的效率。同时,由于这种算法只需要常数级别的额外空间,因此在空间受限的情况下也具有较高的可行性。 ### 回答3: 众数问题是算法设计中的一类经典问题,其主要目的是求出给定的多重集合中的众数以及其重数。众数是指在多重集合中出现次数最多的那个元素,其重数是指该元素在多重集合中出现的次数。 为了求解众数问题,可以采用多种不同的算法。其中最简单直接的算法是暴力枚举,即枚举多重集合中的每一个元素并统计其出现次数,最后找出出现次数最多的那个元素。虽然暴力枚举算法非常简单,但是其时间复杂度为O(n^2),对于较大规模的多重集合来说效率比较低。 为了提高效率,可以采用分治、排序、哈希等算法进行优化。其中较为常用的算法是排序。具体来说,可以使用快速排序或者归并排序的算法对多重集合中的元素进行排序,然后再遍历一遍排好序的元素,统计每个元素出现的次数,最终找出出现次数最多的那个元素。排序算法的时间复杂度为O(nlogn),比暴力枚举算法要快得多。 除了排序算法,还可以使用哈希算法进行优化。具体来说,可以将多重集合中的每个元素都转换为一个哈希值,并使用哈希表数据结构来存储每个元素的出现次数。遍历一遍哈希表,找出出现次数最多的那个元素,即为众数。哈希算法的时间复杂度为O(n),相对于排序算法有着更快的运行速度。 总之,众数问题在算法设计中属于比较基础的问题,广泛应用于各个领域。在实际应用中可以根据数据规模、性能要求和实际场景等多个因素选择不同的算法来进行优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值