算法学习笔记(16): 组合数学基础

组合数学基础

本文部分运用到了生成函数的知识

如果直接食用本文结论,请忽略下列链接。

生成函数参考博客:普通型生成函数 - Ricky2007 - 博客园

我认为讲的不错

组合数学非常有用!我们先从一点点简单的性质开始

简单原理

  • 加法原理

    这非常简单,我们举一个例子即可:考虑我有 5 个红苹果和 3 个绿苹果,如果你要选一个苹果去吃,那么你一共有 5+3=8 种选择的方法

  • 乘法原理

    同样非常简单:考虑我有 5 个苹果,涵儿有 6 个苹果,我们各自拿出一个苹果,那么一共有 5×6=30 种拿出的方案

  • 减法原理和除法原理

    本质与加法原理和乘法原理相似,这里不做展开

  • 抽屉原理

    (广义)如果 n 个物品一共有 k 种状态,那么至少有 nk 种物品处于一个状态

    推论:一个从有 >k 个元素的集合映射到 k 个元素的集合的函数一定不是一对一函数。

终于正式开始将排列组合了!

排列

定理:具有 n 个元素的集合,选出 r 个排列的可能数(顺序相关

P(n,r)=n(n1)(n2)(nr+1)

证明:由于顺序相关,不能选择同一个数多次,那么第一个位置有 n 种选法,第二个位置有 n1 中选法,以此类推,第 i 个位置有 ni+1 种选法。考虑乘法原理,那么就得出了上述结论。

特殊的:只要 n 是一个非负整数,那么 P(n,0)=1,因为恰好有一种方法来排列 0 个元素

简写公式:一般来说,我们不会写成上述形式,而是

P(n,r)=n!(nr)!

多重排列

考虑这样一个问题:我有 7 个盘子,2 个苹果,3 个橘子和 2 个桃子,分别在一个盘子中放一个水果,一共有多少种放法(我们认为同一种水果是相同的)?

经过计算,一共有 7!2!3!2!=210 种放法。

抽象来说,我们将 k 个元素进行排列,对于第 i 个元素一共有 xi 个。那么总的排列方案数为

(x1+x2++xk)!x1!x2!xk!

组合

其实就是顺序无关的排列

定理:具有 n 个元素的集合,选出 r 个数组成新的集合,本质不同的集合数为

C(n,r)=(nr)=n!r!(nr)!

由于顺序无关,我们考虑通过排列推导。

证明:为了得出所有集合,我们先考虑顺序相关,也就是有 P(n,r) 个排列,而对于每一个排列,如果不考虑顺序,一共重复计算了 P(r,r) 次,所以

C(n,r)=P(n,r)P(r,r)=n!(nr)!r!(rr)!=n!r!(nr)!

性质

接下来我们考虑组合的各种性质


(nm)=(nnm)=nm(n1m1)=(n1m)+(n1m1)

  • 前两个等式,考虑按照定义展开化简即可

  • 考虑最后一个等式,其实就是杨辉三角的递推,我们钦定 n 中的一个元素,分情况讨论

    • 如果不选择这个数,也就是在剩下的数中选择 m 个数,那么一共有 (n1m) 种情况

    • 如果选择这个数,那么只需要在剩下的数种选择 m1 个数即可,那么一共有 (n1m1) 种情况


(nk)(km)=(nm)(nkmk)

证明:展开即可


i=kn(ik)=(n+1k+1)

证明:还是考虑展开

i=kn(ik)=(kk+1)+(kk)+(k+1k)++(nk)

(kk+1) 是不合法的,所以其值为 0,加上去之后不会对结果产生影响

我们通过公式 (nm)=(n1m)+(n1m1) 两两合并即可。

推论:我们将 i 平移,那么得出

i=0m(k+ii)=(k+m+1m+1)


接下来是剩下的一个最重要的二项式系数恒等式:

(rk)=(1)k(kr1k)

这是上指标反转,需要用到广义组合数的知识。

广义组合数

定义下降幂 xn_=x(x1)(xn+1),那么我们可以定义广义组合数:

(rk)=rk_k!

其是一个关于 rk 次多项式,注意 kN

于是考虑 xn_=x(x1)(xn+1)=(1)n(0x)(1x)(n1x)=(1)n(n1x)n_,这就是上指标反转的来源。


我们总结一下这 10 个最最重要的等式:

(nk)=nk_k!(nk)=(nnk)(nk)=(n1k)+(n1k1)(nm)(mk)=(nk)(nkmk)(x+y)r=k(rk)xkyrk(nm)=(1)m(mn1m)k=0n(km)=(n+1m+1)k=0m(n+kk)=(n+m+1m)k(nk)(msk)=(n+ms)(rk)=(1)k(kr1k)


小练习

i=0ni(ni)=n2n1

证明

考虑代数展开,通过 (nm)=(nnm) 变化即可。

i=0ni(ni)=0(n0)+1(n1)++(n1)(nn1)+n(nn)

但是,其实可以通过生成函数推导。推导步骤如下:

我们先展开,得到

0+1(n1)+2(n2)++n(nn)

我们可以由此联想到生成函数求导的公式

<a1,a2,a3,>→<a2,2a3,3a4,>

那么我们考虑求导前的生成函数序列:

<(n0),(n1),(n2),,(nn),0,>

显然,其生成函数展开之前为 F(x)=(1+x)n

那么我们对其求导得到 F(x)=n(1+x)n1

展开之后为

<1(n1),2(n2),3(n3),,n(nn),0,>

我们考虑需要把所有的系数加起来,那么我们令 x=1 即可

所以,得出

i=0ni(ni)=F(1)=n(1+1)n1=n2n1


我们考虑扩展一下上述式子

i=0ni2(ni)=n2n1+(n1)n2n2

考虑还是利用生成函数的思路。

将生成函数 F(x)=n(1+x)n1 向右平移一位并再次求导:

G(x)=xF(x)=nx(1+x)n1G(x)=n(1+x)n1+(n1)nx(1+x)n2

那么我们还是借上面的思路,令 x=1,所以

i=0ni2(ni)=G(1)=n2n1+(n1)nx(1+x)n2=n2n1+(n1)n2n2

二项式定理

定理:令 n 是非负整数,那么有

(x+y)n=i=0n(ni)xniyi=(n0)xny0+(n1)xn1y1++(nn1)x1yn1+(nn)x0yn

考虑展开之后每一项都应该是 n 次的,所以 xi 次一共有 (ni) 种情况


kn(nk)2kn 分别在 k,n 作为变量时的封闭形式。

  • 关于 k 的和式

先将 n<0 的情况特判掉。

很简单的是将 2n 提出来,利用二项式定理合并即可:

2nkn(nk)2k=2n3n

  • 关于 n 的和式

先将 k<0 的情况特判掉。

略微复杂一点的是我们需要用到上指标反转。

n(nnk)2kn=n(1)nk(k1nk)2(nk)=n(k1nk)(12)nk=(112)k1=2k+1

注意这里使用了广义二项式定理

广义二项式定理可以将 n 次扩展到全体实数上,其描述为对于 αR

(x+y)α=k(αk)xkyαk

这是非常好用的东西!


小练习

当整数 m,n>0 时,求出下式的封闭形式:

k1(nlogmk)

这与二项式定理真的有关系吗?天知道!

我们枚举 logmk 的取值:

j0(nj)k[logmk=j]=j0(nj)k[mjk<mj+1]=j0(nj)(mj+1mj)=(m1)j0(nj)mj=(m1)(m+1)n

怎么样,很简单吧。


我们对于阶乘下降幂实际上有一个很类似的结果:

(x+y)n_=k(nk)xk_ynk_

证明可以采用归纳法,这里就不展开了。

范德蒙德卷积

已知 n,m,t 其中 t 是整数:

i=0t(ni)(mti)=(n+mt)

证明:在组合意义上,相当于在 n 中选 i 个,在 m 中选剩下的,也就是在 n+m 中选择 t 个。

而二项式证明这里就不展开了。

小练习

请证明:

i=0n(ni)2=(2nn)

扩展

范德蒙德卷积有着更为变化的形式:

s0k(sk)(tm+k)=(s+ttm)s0k(1)k(sm+k)(t+kn)=(1)s+m(tmns)l,m,n0kl(lkm)(skn)(1)k=(1)l+m(sm1lmn)m,n0,l+q0qkl(lkm)(q+kn)=(l+q+1m+n+1)

这些式子都可以通过种种神秘的方法进行推导,此处不展开。

Lucas定理

定理:

(nm)(npmp)(n%pm%p)(modp)

这个证明相对复杂,请酌情食用

证明

我们考虑通过带余方程改写上述式子:

(sp+tkp+r)(sk)(tr)(modp)

我们通过生成函数 F(x)=(1+x)sp+t 的第 kp+r 次项的系数求。

我们先求一个推导的时候需要的东西:

(1+x)p1+(p1)x+(p2)x2+(p3)x3++(pp1)xp1+xp(modp)1+xp(modp)

那么我们正式开始推导:

(1+x)sp+t(1+x)sp(1+x)t(modp)((1+x)p)s(1+x)t(1+xp)s(1+x)ti=0s(si)xpij=0t(tj)xj

我们取 xkp+r

那么当且仅当 i=k,j=r 时,就可以取出 xkp+r 项的系数。

考虑为什么当且仅当

可知,我们需要 ip+j=kp+r

j[0,t],t[0,p),r[0,p)j=r,i=k

那么,其系数为

(sk)(tr)

所以,可知

(sk)(tr)(sp+tkp+r)(modp)

得证:

(nm)(npmp)(n%pm%p)(modp)

程序实现

这里还是稍微讲一下吧

首先,我们需要求出组合数,那么我们先预处理一下模数以内的阶乘和阶乘逆元:

long long fac[N], ifac[N];
for (int i = fac[0] = 1; i < MOD; ++i) fac[i] = (i * fac[i - 1]) % MOD;
ifac[MOD - 1] = quickPow(fac[MOD - 1], MOD - 2, MOD);
for (int i = MOD - 1; i; --i) ifac[i - 1] = ifac[i] * i % MOD;

考虑一下组合数的特殊情况,如果 n<m 那么 (nm)=0

所以我们求模数以内的组合数方法如下:

inline int C(int i, int j) {
    return (i < j || j < 0) ? 0 : fac[i] * ifac[j] % MOD * ifac[i - j] % MOD;
}

那么Lucas定理呢?我们处理一下 n=0 的特殊情况即可

inline int Lucas(int i, int j) {
    if (j == 0) return 1;
    return Lucas(i / MOD, j / MOD) * C(i % MOD, j % MOD) % MOD;
}

广义容斥与二项式反演

这个部分相对较复杂,我给出反演公式

fn 表示之多拥有 n 个属性的集合个数,gn 表示恰好拥有 n 个属性的集合

那么

fn=i=0n(ni)gign=i=0n(1)ni(ni)fi

反演推导证明

gn=i=0n(1)ni(ni)j=0i(ij)gj:=j=0ngji=jn(ni)(ij)(1)ni=j=0ngji=jn(nj)(njij)(1)ni=j=0ngj(nj)i=jn(njij)(1)ni=j=0ngj(nj)i=0nj(nji)(1)ni=j=0ngj(nj)(1+(1))nj[n=j:(1+(1))nj0]=gj(nj)[n=j]=gn

posted @   jeefy  阅读(319)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示