zoukankan      html  css  js  c++  java
  • 斯特林数与斯特林反演

    此文章重构于(2019.2.26)

    斯特林数分为第一类斯特林数和第二类斯特林数,其形式与二项式系数很相似。斯特林数在下降幂与通常幂的变换中也有重要作用。

    第一类斯特林数

    定义

    我们将把(n)个不同元素分成(k)个非空圆排列的方案数记为(egin{bmatrix}n\ kend{bmatrix}),两种方案不同当且仅当存在一个圆排列仅在其中一种方案中出现。所有形如(egin{bmatrix}n\ kend{bmatrix})都叫做第一类斯特林数,在某些地方第一类斯特林数也被记为(s_n^k)

    递推式

    考虑如何对第一类斯特林数进行递推。类似二项式系数的递推,我们对于第(n)个元素是否单独组成一个圆排列进行分类讨论。如果其单独组成一个圆排列,那么这种情况的方案数显然为(egin{bmatrix}n-1\ k-1end{bmatrix}),否则我们将第(n)个元素插入已有的圆排列中,显然一共存在(n-1)个本质不同的位置,于是这种情况的方案数就为((n-1)egin{bmatrix}n-1\ kend{bmatrix})。于是我们可以得到一个形式优美的递推式:

    [egin{bmatrix}n\ kend{bmatrix}=egin{bmatrix}n-1\ k-1end{bmatrix}+(n-1)egin{bmatrix}n-1\ kend{bmatrix} ]

    通项公式

    很遗憾,第一类斯特林数不存在比较简洁的通项公式。

    生成函数

    虽然通项公式并不存在简洁形式,但是其生成函数或许是非常优美的。

    我们按行分开考虑,记(f_n)为第(n)行的生成函数,即(f_n=sum_{i=0}^negin{bmatrix}n\ iend{bmatrix}x^i),从递推式中不难发现(f_{n+1})(f_n)的关系即为:

    [f_{n+1}=(x+n)*f_n ]

    于是我们可以得到优美的生成函数:

    [f_n=prod_{i=0}^{n-1}(x+i)=x^{overline n} ]

    求法

    通过递推式,我们可以在(O(nk))的时间内求出(egin{bmatrix}n\ kend{bmatrix}),但我们有更快的方法。

    考虑从生成函数出发如何快速求出第(n)行的所有第一类斯特林数。不难发现上式可以简单的通过分治( ext{FFT})(O(nlog^2 n))的时间内求出。不过考虑到式子的特殊性,我们可以找到更快的方法:

    通过上升幂的性质,我们有:

    [x^{overline {2k}}=x^{overline k}*(x+k)^{overline k} ]

    考虑如何快速求((x+k)^{overline k})

    [(x+k)^{overline k}=sum_{i=0}^k egin{bmatrix}k\ iend{bmatrix}(x+k)^i ]

    [=sum_{i=0}^k egin{bmatrix}k\ iend{bmatrix} Big(sum_{j=0}^i{ichoose j}x^jk^{i-j}Big) ]

    [=sum_{j=0}^kx^jBig(sum_{i=j}^kegin{bmatrix}k\ iend{bmatrix}{ichoose j}k^{i-j}Big) ]

    [=sum_{j=0}^kfrac{x^j}{j!}Big(sum_{i=j}^k (egin{bmatrix}k\ iend{bmatrix}*i!)*frac{k^{i-j}}{(i-j)!}Big) ]

    不难发现上式其实是一个卷积,于是我们可以在(O(nlog n))的时间内从(x^{overline k})倍增到(x^{overline {2k}}),也可以很快的从(x^{overline {2k}})推到(x^{overline {2k+1}}),于是进行与多项式倍增类似的操作,我们可以在(O(nlog n))的时间内求出所有第(n)行的第一类斯特林数。

    一些性质

    通过生成函数我们可以发现第一类斯特林数实际上可以代表从上升幂到通常幂的变换,即:

    [x^{overline n}=sum_{i=0}^negin{bmatrix}n\ iend{bmatrix}x^i ]

    通过(prod_{i=0}^{n-1}(x+i))转化为(prod_{i=0}^{n-1}(x-i))我们可以将上升幂转化为更常用的下降幂,不难发现此时第一类斯特林数需要带上符号:

    [x^{underline n}=sum_{i=0}^n(-1)^{n-i}egin{bmatrix}n\ iend{bmatrix}x^i ]

    第二类斯特林数

    定义

    我们将把(n)个不同元素分成(k)个非空集合的方案数记为(egin{Bmatrix}n\ kend{Bmatrix}),两种方案不同当且仅当存在一个集合仅在其中一种方案中出现。所有形如(egin{Bmatrix}n\ kend{Bmatrix})都叫做第二类斯特林数,在某些地方第二类斯特林数也被记为(S_n^k)

    递推式

    考虑如何对第二类斯特林数进行递推。类似二项式系数的递推,我们对于第(n)个元素是否单独组成一个集合进行分类讨论。如果其单独组成一个集合,那么这种情况的方案数显然为(egin{Bmatrix}n-1\ k-1end{Bmatrix}),否则我们将第(n)个元素插入已有的集合中,显然一共存在(k)个本质不同的位置,于是这种情况的方案数就为(kegin{Bmatrix}n-1\ kend{Bmatrix})。于是我们可以得到一个形式优美的递推式:

    [egin{Bmatrix}n\ kend{Bmatrix}=egin{Bmatrix}n-1\ k-1end{Bmatrix}+kegin{Bmatrix}n-1\ kend{Bmatrix} ]

    通项公式

    幸运的是第二类斯特林数存在一个比较简洁的通项公式。

    我们考虑将(n)个元素划分为(k)不同的非空集合的方案数,容易发现由于元素互不相同且每个集合是非空的,所以集合也是互不相同的,因此方案数就是(egin{Bmatrix}n\ kend{Bmatrix}*k!)

    再考虑将(n)个元素划分为(k)不同的可空集合的方案数,显然是枚举每个元素放入哪个集合,方案数就是(k^n)

    考虑这两种方案之间的关系,对于划分为可空集合,我们可以通过枚举有几个集合是非空的从而得到下面这个等式:

    [k^n=sum_{i=0}^k{kchoose i}egin{Bmatrix}n\ iend{Bmatrix}*i! ]

    显然这是一个二项式反演的形式,对其进行二项式反演可以得到:

    [egin{Bmatrix}n\ kend{Bmatrix}=frac{1}{k!}Big(sum_{i=0}^k(-1)^{k-i}{kchoose i}i^nBig) ]

    生成函数

    通过通项公式,我们容易发现其生成函数的形式。考虑对通项公式进行转化:

    [egin{Bmatrix}n\ kend{Bmatrix}=frac{1}{k!}Big(sum_{i=0}^k(-1)^{k-i}{kchoose i}i^nBig) ]

    [=sum_{i=0}^kfrac{(-1)^{k-i}}{(k-i)!}*frac{i^n}{i!} ]

    那么我们记(f_n=sum_{igeq 0}egin{Bmatrix}n\ iend{Bmatrix}x^i)(g_n=sum_{igeq 0}frac{i^n}{i!}x^i),可以得到:

    [f_n=e^{-x}*g_n ]

    求法

    通过递推式,我们可以在(O(nk))的时间内求出(egin{Bmatrix}n\ kend{Bmatrix}),但我们有更快的方法。

    直接通过生成函数,我们就得到了(O(nlog n))求第(n)行所有第二类斯特林数的方法。

    一些性质

    第二类斯特林数与幂和下降幂也有关系,但是并不如第一类斯特林数那么显然。我们尝试用归纳法证明:

    [x^n=sum_{i=0}^n egin{Bmatrix}n\ iend{Bmatrix}x^{underline i} ]

    (n=0)时显然成立,考虑当(n-1)时成立如何推导(n)时也成立:

    [x^n=x*x^{n-1}=sum_{i=0}^{n-1} egin{Bmatrix}n-1\ iend{Bmatrix} x*x^{underline i} ]

    我们考虑到(x*x^{underline i}=i*x^{underline i}+(x-i)*x^{underline i}=i*x^{underline i}+x^{underline {i+1}}),于是有:

    [sum_{i=0}^{n-1} egin{Bmatrix}n-1\ iend{Bmatrix} x*x^{underline i}=sum_{i=0}^{n-1}iegin{Bmatrix}n-1\ iend{Bmatrix}x^{underline i}+egin{Bmatrix}n-1\ iend{Bmatrix}x^{underline {i+1}} ]

    [=sum_{i=0}^n (egin{Bmatrix}n-1\ i-1end{Bmatrix}+iegin{Bmatrix}n-1\ iend{Bmatrix})x^{underline i} ]

    [=sum_{i=0}^n egin{Bmatrix}n\ iend{Bmatrix} x^{underline i} ]

    以上可知证毕。

    类似的,我们也有关于上升幂的式子:

    [x^n=sum_{i=0}^n(-1)^{n-i}egin{Bmatrix}n\ iend{Bmatrix} x^{overline i} ]

    斯特林反演

    我们已经得到两个形式优美的式子:

    [x^{underline n}=sum_{i=0}^n(-1)^{n-i}egin{bmatrix}n\ iend{bmatrix}x^i ]

    [x^n=sum_{i=0}^n egin{Bmatrix}n\ iend{Bmatrix} x^{underline i} ]

    我们可以把两个式子组合一下:

    [x^{underline n}=sum_{i=0}^n (-1)^{n-i}egin{bmatrix}n\ iend{bmatrix} x^i ]

    [=sum_{i=0}^n (-1)^{n-i}egin{bmatrix}n\ iend{bmatrix} Big(sum_{j=0}^iegin{Bmatrix}i\ jend{Bmatrix}x^{underline j}Big) ]

    [=sum_{j=0}^nx^{underline j}Big(sum_{i=j}^n (-1)^{n-i}egin{bmatrix}n\ iend{bmatrix} egin{Bmatrix}i\ jend{Bmatrix}Big) ]

    那么可以发现(Big(sum_{i=j}^n (-1)^{n-i}egin{bmatrix}n\ iend{bmatrix} egin{Bmatrix}i\ jend{Bmatrix}Big)=[n=j]),类似的我们也有(Big(sum_{i=j}^n egin{Bmatrix}n\ iend{Bmatrix} (-1)^{i-j}egin{bmatrix}i\ jend{bmatrix}Big)=[n=j])

    于是就可以用来反演了。假设有两个数组(f,g)满足:

    [g_i=sum_{j=0}^iegin{Bmatrix}i\jend{Bmatrix}f_j ]

    那么我们就有:

    [f_i=sum_{j=0}^i(-1)^{i-j}egin{bmatrix}i\jend{bmatrix}g_j ]

    原文:

    斯特林数分为第一类斯特林数和第二类斯特林数,其形式和二项式系数非常像,都是二元函数。下面逐一介绍。

    第一类斯特林数,设两个变量为(n)(k),那么其表示为:

    [egin{bmatrix} n \ k end{bmatrix} ]

    第一类斯特林数还有另一种表示方式,(s_n^k)(注意(s)是小写的)。

    它的组合意义是,有(n)个不同的元素,将它们分成(k)个非空圆排列的方案数。这个定义看上去有点难理解,其实讲的通俗一点,就是你要将(n)个元素排成(k)个圈,一个圈如果可以通过旋转(但是不可以翻转)变成另一个圈,那么两个圈就是相同的。并且圈之间是没有顺序的。

    下面来推导一下第一类斯特林数的递推式。我们假设前(n-1)个元素已经摆放好了,那么第(n)个元素要么自成一个新的圈,要么插入到前(n-1)个元素组成的圈中,一共有(n-1)个空位,所以得到:

    [egin{bmatrix} n \ k end{bmatrix}=egin{bmatrix} n-1 \ k-1 end{bmatrix}+(n-1)* egin{bmatrix} n-1 \ k end{bmatrix}]

    注意判断边界条件,(egin{bmatrix} n \ 0 end{bmatrix}=[n=0])(egin{bmatrix} n \ n end{bmatrix}=1)

    我们可以发现用递推式来求解第一类斯特林数是(O(nk))的,有没有什么更快的方法?答案是肯定的,下面介绍的方法可以在(O(nlog n))的时间内求出(k=0,1,2,...,n)的所有(egin{bmatrix} n \ k end{bmatrix})

    我们首先来研究一下第一类斯特林数的性质,我们知道(x)(k)次上升幂为:

    [x^{overline k}=prod_{i=0}^{k-1}x+i ]

    我们把(x)看作多项式中的未知数,将上式理解为多项式,然后我们考虑将其展开:

    [x^{overline k}=x^k+...??? ]

    好像一下子推不出来了,这个任务还是比较困难的,那我们直接说出结论,尝试来证明它吧:

    [x^{overline k}=sum_{i=0}^{k} egin{bmatrix} k \ i end{bmatrix} x^i]

    (k=0)时,上式左右都为(1),显然成立。现在来证明如果这个式子对于(k-1)成立,那么对(k)也成立,首先按照递推式分解第一类斯特林数:

    [sum_{i=0}^k igg( egin{bmatrix} k-1 \ i-1 end{bmatrix} + (n-1)* egin{bmatrix} k-1 \ i end{bmatrix} igg) x^i]

    拆开来,得到:

    [sum_{i=0}^k egin{bmatrix} k-1 \ i-1 end{bmatrix} x^i +(k-1)sum_{i=0}^k egin{bmatrix} k-1 \ i end{bmatrix} x^i]

    去掉那些无意义的第一类斯特林数,再把第一个和式的(i)枚举范围从(1)(k)改成(0)(k-1)(差不多就是左移一位的意思),得到:

    [xsum_{i=0}^{k-1} egin{bmatrix} k-1 \ i end{bmatrix} x^i + (k-1)sum_{i=0}^{k-1} egin{bmatrix} k-1 \ i end{bmatrix} x^i]

    于是我们就可以合并了,得到:

    [(x+k-1)sum_{i=0}^{k-1} egin{bmatrix} k-1 \ i end{bmatrix} x^i ]

    因为性质已经对(k-1)成立,因此后面的和式就是(x^{overline{k-1}}),于是这个式子就等于(x^{overline k}),得证。

    还有一点要说的是,如果把上升幂换成下降幂,很显然每一项只是又乘了((-1)^{k-i})。其实((-1)^{k-i} egin{bmatrix} k \ i end{bmatrix})叫做带符号的第一类斯特林数,也是很有用的,接下来也会提及关于它的应用(如果我不咕咕咕的话)

    直接利用这个性质,我们就可以用分治(FFT)做到(O(nlog ^2 n)),这已经很优秀了。不过利用下面的技巧可以做到(O(n log n))

    假设我们已经求出了(x^{overline k})的结果,我们可以进行如下操作在(O(nlog n))时间内求出((x+k)^{overline k})

    假设我们已经求得:

    [x^{overline k}=sum_{i=0}^{k}a_i*x^i ]

    那么显然有:

    [(x+k)^{overline k}=sum_{i=0}^k a_i*(x+k)^i ]

    ((x+k)^i)进行二项式展开:

    [(x+k)^{overline k}=sum_{i=0}^k a_i* Big( sum_{j=0}^i {i choose j} x^j*k^{i-j} Big)]

    再交换一下前后的两个(sum)

    [(x+k)^{overline k}=sum_{j=0}^k x^j*Big(sum_{i=j}^k {ichoose j}a_i*k^{i-j} Big)]

    已经可以发现一点(FFT)的影子了,我们再把二项式系数拆开并把(j!)提到前面:

    [(x+k)^{overline k}=sum_{j=0}^k x^j *frac{1}{j!}*Big(sum_{i=j}^k (a_i*i!)*frac{k^{i-j}}{(i-j)!}Big)]

    于是就可以(FFT)了,接着我们计算(x^{overline {2k}}=x^{overline k}(x+k)^{overline k}),就可以倍增求答案了,如果要计算(x^{overline {2k+1}}),只要再暴力乘以(x+2k)即可。于是递归式就是(T(n)=T(frac{n}{2})+O(nlog n)),利用主定理计算得(T(n)=O(nlog n))

    下面来讲一下第一类斯特林数的应用,有一个应用是可以利用带符号的第一类斯特林数在(O(k^2))时间内求得(sum_{i=0}^n i^k),即自然数幂次和(不过好像有更直观的做法...其实这是一个(k+1)次的多项式,所以计算(k+2)个不同的值再暴力插值插出多项式也可以。用高斯消元是(O(k^3))的,而用拉格朗日插值是(O(k^2))或者(O(klog ^2 k))的,但第一类斯特林数的做法比较优美,并且是不需要求逆元的)。

    我们先来证明一个小引理:

    [sum_{i=0}^n {i choose k} = {n+1 choose k+1} ]

    证明非常简单,还是用归纳法。当(n=0)时左右两边都为([k=0]),显然正确。现在来证明如果对于(n-1)成立,那么对于(n)也成立。把右边的二项式系数拆开得到:

    [sum_{i=0}^k {i choose k} = {n choose k} + {n choose k+1} ]

    把等式两边的({n choose k})都消掉,得到:

    [sum_{i=0}^{n-1} = {n choose k+1} ]

    该式就是我们在(n-1)时已经证明的形式,因此引理得证。

    现在我们来用第一类斯特林数求自然数幂次和。由第一类斯特林数的性质得到:

    [{n choose k}*k!=frac{n!}{(n-k)!}=n^{underline k}=sum_{i=0}^k (-1)^{k-i} egin{bmatrix} k \ i end{bmatrix} n^i]

    因此:

    [n^k={n choose k}*k!-sum_{i=0}^{k-1} (-1)^{k-i} egin{bmatrix} k \ i end{bmatrix} n^i]

    我们设(S_{k,n}=sum_{i=0}^n i^k),则:

    [S_{k,n}=sum_{i=0}^n Bigg( {i choose k}*k!-sum_{j=0}^{k-1} (-1)^{k-j} egin{bmatrix} k \ j end{bmatrix} i^j Bigg)]

    把里面的两项拆开,得到:

    [S_{k,n}=sum_{i=0}^n {i choose k}*k!-sum_{i=0}^n sum_{j=0}^{k-1}(-1)^{k-j} egin{bmatrix} k \ j end{bmatrix} i^j]

    利用我们之前说的引理把第一项转化了并把后面一项的(i)合并,得到:

    [S_{k,n}={n+1 choose k+1} *k!-sum_{j=0}^{k-1} (-1)^{k-j} egin{bmatrix} k \ j end{bmatrix} S_{j,n}]

    再把第一项稍微进行一下转化,后面一项的(j)换成(i),得到:

    [S_{k,n}= frac{prod_{i=n-k+1}^n i}{k+1}-sum_{i=0}^{k-1} (-1)^{k-i} egin{bmatrix} k \ i end{bmatrix} S_{i,n}]

    注意到前面那项一定是可以整除的,所以不需要求逆元。接下来这要用这个式子(O(k^2))递推就好了,这里的第一类斯特林数可以直接用递推式(O(k^2))求。

    呼,第一类斯特林数终于告一段落了。现在来讲清真很多的第二类斯特林数。

    设两个变量是(n)(k),那么其表示为:

    [egin{Bmatrix} n \ k end{Bmatrix} ]

    它的组合意义是,有(n)个不同的元素,将它们划分为(k)个非空集合的方案数。集合内和集合间都是无序的。

    来考虑第二类斯特林数的递推,我们同样假设前(n-1)个元素已经放好了,那么第(n)个元素要么成为一个新的集合,要么加入到已有的集合中的一个,那么其递推式就是:

    [egin{Bmatrix} n \ k end{Bmatrix}=egin{Bmatrix} n-1 \ k-1 end{Bmatrix}+k*egin{Bmatrix} n-1 \ k end{Bmatrix}]

    现在来考虑一下第二类斯特林数的通项公式(没错第二类斯特林数是有简单的通项公式的,和第一类斯特林数相比就是这么清真。)

    考虑把(n)个不同元素分成(k)个可空集合,并且集合间是有序的方案数,很显然是(k^n)。而这些方案是由划分为(i=0,1,2,...,k)个非空集合的方案组成的,注意此时我们要求集合间有序,而非空集合之间因为元素互不相同所以显然是两两不同的,因此方案数就是(egin{Bmatrix} n \ i end{Bmatrix}*i!),那么我们就有:

    [k^n=sum_{i=0}^k {k choose i} egin{Bmatrix} n \ i end{Bmatrix}*i!]

    显然这又是一个二项式反演的形式(二项式反演真是好东西),于是我们就得到:

    [egin{Bmatrix} n \ k end{Bmatrix}*k!=sum_{i=0}^k (-1)^{k-i} {k choose i} i^n]

    因此:

    [egin{Bmatrix} n \ k end{Bmatrix} = frac{sum_{i=0}^k (-1)^{k-i} {k choose i} i^n}{k!}]

    但是直接根据通项公式求是(O(k))的,我们还是来考虑一下如何在(O(nlog n))的时间内求出(k=0,1,2,...,n)的所有(egin{Bmatrix} n \ k end{Bmatrix})

    我们把通项公式中的二项式系数拆开,于是分母与分子的(k!)就消掉了,得到:

    [egin{Bmatrix} n \ k end{Bmatrix}=sum_{i=0}^k frac{(-1)^{k-i}}{(k-i)!}*frac{i^n}{i!}]

    这是一个非常简单的(FFT)形式,直接做即可。复杂度(O(nlog n))

    第二类斯特林数的一个性质就是我们在求通项公式的时候用到的那个,这个稍加推导也可以求自然数幂次和,并且如果支持求逆元操作,那么其复杂度是(O(klog k))的,否则其复杂度是(O(k^2))的,这比第一类斯特林数更加实用。

    我们已经知道(为了方便我把上面式子的(n)(k)互换了):

    [n^k=sum_{i=0}^n {n choose i} egin{Bmatrix} k \ i end{Bmatrix} *i!]

    注意到(i>k)时第二类斯特林数的值一定为(0),因此枚举范围改为(0)~(k)

    [n^k=sum_{i=0}^k {n choose i} egin{Bmatrix} k \ i end{Bmatrix} *i!]

    因此:

    [S_{k,n}=sum_{j=0}^n sum_{i=0}^k {j choose i} egin{Bmatrix} k \ i end{Bmatrix} *i!]

    同样,交换两个(sum)的顺序:

    [S_{k,n}=sum_{i=0}^k egin{Bmatrix} k \ i end{Bmatrix}*i! Bigg( sum_{j=i}^n {j choose i} Bigg)]

    再次利用之前的引理,后面的和式就转化为:

    [S_{k,n}=sum_{i=0}^k egin{Bmatrix} k \ i end{Bmatrix} * i! * {n+1 choose i+1}]

    最终得到:

    [S_{k,n}=sum_{i=0}^k egin{Bmatrix} k \ i end{Bmatrix} frac{prod_{j=n-i+1}^{n+1} j}{i+1}]

    显然那个连乘是可以递推的,然后就好了。为什么感觉第二类斯特林数什么都比第一类斯特林数简洁。

  • 相关阅读:
    【MySQL笔记】数据定义语言DDL
    【MySQL笔记】SQL语言四大类语言
    《ggplot2:数据分析与图形艺术》,读书笔记
    【数据处理】为什么数据要取对数
    【R实践】时间序列分析之ARIMA模型预测___R篇
    【R笔记】使用R语言进行异常检测
    【R笔记】日期处理
    朴素贝叶斯分类器的应用
    数据分析的方法与技术
    爬虫 测试webmagic (一)
  • 原文地址:https://www.cnblogs.com/Mr-Spade/p/9643161.html
Copyright © 2011-2022 走看看