【离散数学】高级计数技术
這是離散數(shù)學(xué)的第四篇,討論高級計數(shù)技術(shù)。同步發(fā)布與個人博客,上一篇(【離散數(shù)學(xué)】計數(shù)/排列組合)討論了計數(shù)以及排列組合,二項式定理等。但是僅憑排列組合等手段依然無法解決許多計數(shù)問題。這里首先討論通過遞推關(guān)系來求解計數(shù)問題,并介紹有遞推關(guān)系引出的兩個算法范式:動態(tài)規(guī)劃和分治。這兩種算法均是通過將問題分割為一系列的子問題來求解的,區(qū)別就是前者分割出來的子問題互相重疊,后者的子問題不重疊。這是兩種很重要的算法,加上貪心、回溯、分支定界為五種很常用的算法。這里僅僅簡單討論思路,并分析其復(fù)雜度。關(guān)于具體的算法分析以及算法設(shè)計以后應(yīng)該會討論。而后介紹了求解一類很常見的特定遞推關(guān)系——常系數(shù)線性齊次與非齊次遞推關(guān)系的形式解法。并且還介紹了一種求解計數(shù)問題的很重要的手段——生成函數(shù),這是冪級數(shù)的應(yīng)用。最后介紹容斥原理,對,就是集合的容斥原理。以上內(nèi)容以前均有涉及,這里再次探討。
遞推關(guān)系的應(yīng)用
經(jīng)典問題——漢諾塔
這是一個及其經(jīng)典的問題,見漢諾塔(Tower of Hanoi),三根柱子,n個盤子,從上到下盤子從小到大。將所有n個盤子從一根柱子移動到另一根柱子,移動過程中小的盤子不能放在大的盤子下面??梢郧蠼獬鏊枰淖钚〔綌?shù),還可以編寫算法打印出所有步數(shù)。
n=3的漢諾塔移動方法:
令移動n個盤子到另一個柱子所需最少次數(shù)為 HnH_nHn? ,考慮最下面一個最大的盤子,由于小的盤子不能放在大的盤子下面,所以必須首先將上面n-1個移動到另一個柱子,再將最下面的最大的一個移動到另一根柱子,再將n-1個移到其上,則完成了n個盤子的移動。則得到遞推關(guān)系 Hn=2Hn?1+1H_n = 2H_{n-1}+1Hn?=2Hn?1?+1。初始條件很容易知道:H0=0H_0 = 0H0?=0,H1=1H_1=1H1?=1。
求解遞推關(guān)系:容易看出 Hn+1=2(Hn?1+1)H_n+1 = 2(H_{n-1}+1)Hn?+1=2(Hn?1?+1),可以得到 Hn=2n?1H_n= 2^n-1Hn?=2n?1??梢宰C明這便是移動n個盤子所需的最少次數(shù)。
卡特蘭數(shù)
考慮一個在n+1個數(shù) $x_0\cdot x_1 \cdot x_2 \cdots x_n $ 的乘積中插入括號來規(guī)定乘法次序的方式數(shù),令其為 CnC_nCn?。
這里注意到一定會有一個乘法是在括號外面的(不需要加括號),假設(shè)其在xkx_kxk?和xk+1x_{k+1}xk+1?之間,則存在CkCn?k?1C_kC_{n-k-1}Ck?Cn?k?1?種方式,考慮最后一個乘號可能取n個位置,則有Cn=∑k=0n?1CkCn?k?1C_n = \sum_{k=0}^{n-1}C_kC_{n-k-1}Cn?=k=0∑n?1?Ck?Cn?k?1?
初始條件則為C0=1C_0 = 1C0?=1,C1=1C_1 = 1C1?=1。
利用生成函數(shù)的方法可以證明:Cn=C(2n,n)n+1C_n = \dfrac{C(2n,n)}{n+1}Cn?=n+1C(2n,n)?,被稱作第n個卡特蘭(Catalan)數(shù),序列{Cn}\{C_n\}{Cn?} 被稱為卡特蘭數(shù)的序列。參見 OEIS A000108,Catalan number - Wikipedia。
動態(tài)規(guī)劃與遞推關(guān)系
動態(tài)規(guī)劃(Dynamic programming,DP)是一種算法范式,遵循動態(tài)規(guī)劃范式的算法是將原問題分解為更簡單的重疊的子問題,通過子問題的求解來求解原問題。常用于求解最短路線、庫存管理、資源分配、設(shè)備更新、排序、裝載等問題。此類問題若用分治法來解,則分解得到的子問題數(shù)目太多,有些子問題被重復(fù)計算了很多次。所以DP通過保存已解決的子問題的答案,而在需要時再找出已求得的答案,這樣就可以避免大量的重復(fù)計算,從而降低復(fù)雜度。以后單獨討論,這里推薦閱讀:動態(tài)規(guī)劃解決01背包問題,什么是動態(tài)規(guī)劃?動態(tài)規(guī)劃的意義是什么?。
求解線性遞推關(guān)系
這部分給人的感覺相當(dāng)熟悉,在高等數(shù)學(xué)中有線性微分方程的求解,線性代數(shù)中有線性方程組的求解,與這里的線性遞推關(guān)系的求解十分類似,可以說是如出一轍。
求解常系數(shù)線性齊次遞推關(guān)系
一個常系數(shù)的kkk階線性齊次遞推關(guān)系指的是形如 an=c1an?1+c2an?2+?+ckan?ka_n = c_1a_{n-1}+c_2a_{n-2}+\cdots + c_ka_{n-k}an?=c1?an?1?+c2?an?2?+?+ck?an?k?,的遞推關(guān)系,其中cic_ici?為實數(shù),ck≠0c_k \neq 0ck???=0。
為了求解kkk階常系數(shù)線性齊次遞推關(guān)系,這里的基本方法是尋找形如an=rna_n = r^nan?=rn的解,其中r是常數(shù)。遞推關(guān)系要有形如an=rna_n = r^nan?=rn的解,則當(dāng)且僅當(dāng)rrr是方程rk?c1rk?1?c2rk?2???ck?1r?ck=0r^k-c_1r^{k-1}-c_2r^{k-2}-\cdots -c_{k-1}r-c_k=0rk?c1?rk?1?c2?rk?2???ck?1?r?ck?=0的解。
將上述方程稱為該遞推關(guān)系的特征方程。方程的解稱為特征根。(與求解常系數(shù)線性微分方程如出一轍)。特征根有可能是復(fù)數(shù),但這里僅考慮特征根為實數(shù)的情況。
定理:假設(shè)特征方程rk?c1rk?1???ck=0r^k-c_1r^{k-1}-\cdots -c_k=0rk?c1?rk?1???ck?=0有k個不相等的根r1,r2,...,rkr_1,r_2,...,r_kr1?,r2?,...,rk?。那么遞推關(guān)系an=c1an?1+c2an?2+?+ckan?ka_n = c_1a_{n-1}+c_2a_{n-2}+\cdots + c_ka_{n-k}an?=c1?an?1?+c2?an?2?+?+ck?an?k?的解為an=α1r1n+α2r2n+?+αkrkna_n = \alpha_1r_1^n+\alpha_2r_2^n+\cdots+\alpha_kr_k^nan?=α1?r1n?+α2?r2n?+?+αk?rkn?
n∈N,α1,α2,...,αkn\in N,\alpha_1,\alpha_2,...,\alpha_kn∈N,α1?,α2?,...,αk?是常數(shù)。
另外,對其中的每一個特征根rir_iri?,如果并非一重而是mim_imi?重時,則用 (αi,0+αi,1n+?+αi,mi?1nmi?1)rin(\alpha_{i,0}+\alpha_{i,1}n+\cdots+\alpha_{i,m_i-1}n^{m_i-1})r_i^n(αi,0?+αi,1?n+?+αi,mi??1?nmi??1)rin? 替代 上述解中的αirin\alpha_ir_i^nαi?rin?即可。
例:斐波那契數(shù)列(OEIS A000045)
遞推關(guān)系fn=fn?1+fn?2f_n = f_{n-1}+f_{n-2}fn?=fn?1?+fn?2?,初始條件f0=0,f1=1f_0 = 0,f_1=1f0?=0,f1?=1。
特征方程r2?r?1=0r^2-r-1=0r2?r?1=0 根為 r1=(1+5)/2r_1 = (1+\sqrt{5})/2r1?=(1+5?)/2,r2=(1?5)/2r_2 = (1-\sqrt{5})/2r2?=(1?5?)/2。
則fn=α1(1+52)n+α2(1?52)nf_n = \alpha_1\left(\dfrac{1+\sqrt5}{2}\right)^n + \alpha_2\left(\dfrac{1-\sqrt5}{2}\right)^nfn?=α1?(21+5??)n+α2?(21?5??)n代入f0,f1f_0,f_1f0?,f1?解出α1=15\alpha _1 = \dfrac1{\sqrt5}α1?=5?1?,α2=?15\alpha _2 = -\dfrac1{\sqrt5}α2?=?5?1?
則得到斐波那契數(shù)列的顯式公式為fn=15(1+52)n?15(1?52)nf_n = \dfrac1{\sqrt5}\left(\dfrac{1+\sqrt5}{2}\right)^n-\dfrac1{\sqrt5}\left(\dfrac{1-\sqrt5}{2}\right)^nfn?=5?1?(21+5??)n?5?1?(21?5??)n
求解常系數(shù)線性非齊次遞推關(guān)系
常系數(shù)線性非齊次遞推關(guān)系:形如 an=c1an?1+c2an?2+?+ckan?k+F(n)a_n = c_1a_{n-1}+c_2a_{n-2}+\cdots + c_ka_{n-k} +F(n)an?=c1?an?1?+c2?an?2?+?+ck?an?k?+F(n),與其相伴的齊次遞推關(guān)系為 an=c1an?1+c2an?2+?+ckan?ka_n = c_1a_{n-1}+c_2a_{n-2}+\cdots + c_ka_{n-k}an?=c1?an?1?+c2?an?2?+?+ck?an?k?,很顯然 通解 = 特解+齊次解。
齊次解即對應(yīng)的常系數(shù)線性齊次遞推關(guān)系的解,記作an(h)a_n^{(h)}an(h)?,特解記作an(p)a_n^{(p)}an(p)?。
不同形式的F(n)具有不同形式的特解
| an+ban+ban+b | cn+dcn+dcn+d |
| α?cn\alpha\cdot c^nα?cn | β?cn\beta\cdot c^nβ?cn |
若 F(n)F(n)F(n) 形如 (btnt+bt?1nt?1+?+b1n+b0)sn(b_tn^t+b_{t-1}n^{t-1}+\cdots+b_1n+b_0)s^n(bt?nt+bt?1?nt?1+?+b1?n+b0?)sn.
則當(dāng) sss 不是特征根時,特解形如 (ptnt+pt?1nt?1+?+p1n+p0)sn(p_tn^t+p_{t-1}n^{t-1}+\cdots+p_1n+p_0)s^n(pt?nt+pt?1?nt?1+?+p1?n+p0?)sn.
當(dāng) sss 是mmm重特征根時,特解形如 nm(ptnt+pt?1nt?1+?+p1n+p0)snn^m(p_tn^t+p_{t-1}n^{t-1}+\cdots+p_1n+p_0)s^nnm(pt?nt+pt?1?nt?1+?+p1?n+p0?)sn.
分治算法與遞推關(guān)系
與動態(tài)規(guī)劃相似,分治算法(Divide and conquer algorithm)范式也會將問題劃分為一個或者多個小問題,不過這些小問題是不重疊的。連續(xù)使用這種劃分直到可以快速找到這些小問題的解,然后將小問題的解合并為原問題的解。即三個步驟:分割原問題,解決子問題,合并得到最終解。常見簡單分治算法:歸并排序、二分查找。這里將說明怎樣用遞關(guān)系來分析分治算法的復(fù)雜度。
分治遞推關(guān)系
假設(shè)一個遞歸算法將規(guī)模為n的問題劃分為a個子問題,每個子問題規(guī)模為n/b,并且需要g(n)的額外運算來合并這些子問題。用f(n)表示求解問題規(guī)模為n的問題所需運算數(shù),則f(n)=af(n/b)+g(n)f(n)= af(n/b)+g(n)f(n)=af(n/b)+g(n)
令n=bkn = b^kn=bk,多次迭代后可以得到:
f(n)=akf(1)+∑j=0k?1ajg(n/bj)f(n)= a^kf(1)+\sum_{j=0}^{k-1}a^jg(n/b^j)f(n)=akf(1)+j=0∑k?1?ajg(n/bj)
很容易知道,
二分查找的分治遞推關(guān)系:f(n)=f(n/2)+2f(n)=f(n/2)+2f(n)=f(n/2)+2
歸并排序的分治遞推關(guān)系:M(n)=2M(n/2)+nM(n)=2M(n/2)+nM(n)=2M(n/2)+n
分治算法的復(fù)雜度分析
定理1:設(shè)f(n)f(n)f(n)是滿足f(n)=af(n/b)+cf(n)=af(n/b)+cf(n)=af(n/b)+c 的增函數(shù),nnn被bbb整除,a?1a\geqslant 1a?1,
bbb是大于1的整數(shù),ccc是正實數(shù)。那么
f(n)={O(nlogba)a>1O(log?n)a=1f(n) = \begin{cases} O(n^{log_ba})&a>1\\O(\log n)&a=1\end{cases}f(n)={O(nlogb?a)O(logn)?a>1a=1?證明:令n=bkn=b^kn=bk即可證得,當(dāng)n≠bkn\neq b^kn??=bk時,依然成立。
很容易得出二分查找復(fù)雜度為O(log?n)O(\log n)O(logn)。
主定理:若f(n)=af(n/b)+cndf(n)=af(n/b)+cn^df(n)=af(n/b)+cnd,則f(n)={O(nd)a<bdO(ndlog?n)a=bdO(nlogba)a?bdf(n) = \begin{cases} O(n^d)&a<b^d \\O(n^d\log n)& a=b^d \\O(n^{log_ba})&a \succ b^d \end{cases}f(n)=??????O(nd)O(ndlogn)O(nlogb?a)?a<bda=bda?bd?
同理,令n=bkn=b^kn=bk即可證得。(ps:上面的?\succ?表示大于號,但其實這個符號并不是這個意思。這里有一個bug,hexo自帶一個功能的會把一段內(nèi)連續(xù)的<>之間的內(nèi)容注釋掉,于是就只好將就一下。)
根據(jù)主定理,很容易得出歸并排序復(fù)雜度為O(nlog?n)O(n\log n)O(nlogn)。
可以看到,定理1只是主定理的特殊情況。
這里僅簡單分析分治算法,并解決了其復(fù)雜度的問題,并未涉及分治算法的設(shè)計及具體實現(xiàn)。
生成函數(shù)
表示序列的一個有效方法是生成函數(shù),把序列的項作為形式冪級數(shù)的變量x的冪的系數(shù)??梢杂蒙珊瘮?shù)解決許多類型的計數(shù)問題。拓展閱讀:Generating function - Wikipedia,什么是生成函數(shù)? | Matrix67: The Aha Moments。
定義
實數(shù)序列a0,a1,...,ak,...a_0,a_1,...,a_k,...a0?,a1?,...,ak?,...的(普通)生成函數(shù)是無窮級數(shù)G(x)=∑k=0∞akxkG(x)= \sum_{k=0}^{\infty}a_kx^kG(x)=k=0∑∞?ak?xk
例:序列{C(n,k)}\{C(n,k)\}{C(n,k)}的生成函數(shù)即是G(x)=(1+x)nG(x)=(1+x)^nG(x)=(1+x)n.
使用生成函數(shù)求解計數(shù)問題時,通??紤]形式冪級數(shù),即不需要考慮其收斂域(對發(fā)散或收斂并不感興趣)
廣義二項式定理
廣義二項式系數(shù):
uuu為實數(shù),kkk為非負(fù)整數(shù),廣義二項式系數(shù)定義為
(ku)={u(u?1)?(u?k?1)/k!k>01k=0\Big(^u_k\Big)=\begin{cases}u(u-1)\cdots(u-k-1)/k! & k>0 \\1&k=0\end{cases}(ku?)={u(u?1)?(u?k?1)/k!1?k>0k=0?
當(dāng)u為負(fù)整數(shù)時,展開即可有下列式子成立(k?n)=(?1)rC(n+r?1,r)\Big(^{-n}_k\Big) = (-1)^rC(n+r-1,r)(k?n?)=(?1)rC(n+r?1,r)
廣義二項式定理:
xxx是實數(shù),∣x∣<1|x|<1∣x∣<1,uuu 是實數(shù),那么(1+x)u=∑k=0∞(ku)xk(1+x)^u=\sum_{k=0}^{\infty}\Big(^u_k\Big)x^k(1+x)u=k=0∑∞?(ku?)xk
其實這就是(1+x)u(1+x)^u(1+x)u的冪級數(shù)展開,使用麥克勞林級數(shù)(即在x=0處泰勒展開)即可證明。
常用生成函數(shù)
這里給出一些最常用生成函數(shù),以及其對應(yīng)的序列一般項。
(1). (1+ax)n=∑k=0nC(n,k)akxk(1+ax)^n=\sum_{k=0}^nC(n,k)a^kx^k(1+ax)n=∑k=0n?C(n,k)akxk,ak=C(n,k)aka_k=C(n,k)a^kak?=C(n,k)ak,二項式定理得到
(2). 1?xr+11?x=∑k=0nxk\dfrac{1-x^{r+1}}{1-x}=\sum_{k=0}^nx^k1?x1?xr+1?=∑k=0n?xk,ak=1,k?na^k=1,k\leqslant nak=1,k?n;否則為0,幾何級數(shù)求和得到
(3). 11?ax=∑k=0∞akxk\dfrac1{1-ax}=\sum_{k=0}^{\infty}a^kx^k1?ax1?=∑k=0∞?akxk,ak=aka_k=a^kak?=ak,對∣x∣<1|x|<1∣x∣<1的幾何級數(shù)求和取極限得到
(4). 1(1?x)2=∑k=0∞(k+1)xk\dfrac 1{(1-x)^2}=\sum_{k=0}^{\infty}(k+1)x^k(1?x)21?=∑k=0∞?(k+1)xk,ak=k+1a_k=k+1ak?=k+1,對11?x\dfrac1{1-x}1?x1?求導(dǎo)得到
(5). 1(1?x)n=∑k=0∞C(n+k?1,k)xk\dfrac1{(1-x)^n}=\sum_{k=0}^{\infty}C(n+k-1,k)x^k(1?x)n1?=∑k=0∞?C(n+k?1,k)xk,ak=C(n+k?1,k)=C(n+k?1,n?1)a_k=C(n+k-1,k)=C(n+k-1,n-1)ak?=C(n+k?1,k)=C(n+k?1,n?1),由廣義二項式定理得到
(6). ex=∑k=0∞xkk!e^x=\sum_{k=0}^{\infty}\dfrac{x^k}{k!}ex=∑k=0∞?k!xk?,ak=1/k!a_k=1/k!ak?=1/k!,泰勒展開即可得到
(7). ln?(1+x)=∑k=0∞(?1)k+1kxk\ln(1+x)=\sum_{k=0}^{\infty}\dfrac{(-1)^{k+1}}{k}x^kln(1+x)=∑k=0∞?k(?1)k+1?xk,ak=(?1)k+1/ka_k={(-1)^{k+1}}/{k}ak?=(?1)k+1/k,同上泰勒展開得到
生成函數(shù)可以用來求解計數(shù)問題,還可以用來求解遞推關(guān)系和證明組合恒等式。這里不想寫了,略過吧!(笑)
容斥原理及其應(yīng)用
兩個集合的容斥原理是很熟悉的,∣A∪B∣=∣A∣+∣B∣?∣A∩B∣|A\cup B|=|A|+|B|-|A\cap B|∣A∪B∣=∣A∣+∣B∣?∣A∩B∣,那么對于多個幾個呢?很容易想到,但可能并不容易寫出來。Inclusion–exclusion principle。
容斥原理
設(shè)A1,A2....,AnA_1,A_2....,A_nA1?,A2?....,An?是有窮集,則∣A1∪A2∪?∪An∣=∑1?i?n∣Ai∣?∑1?i<j?n∣Ai∩Aj∣+∑1?i<k<j?n∣Ai∩Aj∩Ak∣+?+(?1)n+1∣A1∩A2∩?∩An∣|A_1\cup A_2 \cup \cdots \cup A_n|=\sum_{1\leqslant i \leqslant n}|A_i|-\sum_{1\leqslant i< j \leqslant n}|A_i\cap A_j|\\ +\sum_{1\leqslant i<k< j \leqslant n}|A_i\cap A_j\cap A_k|+\cdots+(-1)^{n+1}|A_1\cap A_2\cap\cdots \cap A_n|∣A1?∪A2?∪?∪An?∣=1?i?n∑?∣Ai?∣?1?i<j?n∑?∣Ai?∩Aj?∣+1?i<k<j?n∑?∣Ai?∩Aj?∩Ak?∣+?+(?1)n+1∣A1?∩A2?∩?∩An?∣
可以看到加號和減號是交替出現(xiàn)的,保證了沒有遺漏也沒有重復(fù)。
三個集合的容斥原理:
應(yīng)用很多,此處愉快地略過。
結(jié)語
其實我只想快速看完這本書,寫完了之后,好好想一想應(yīng)該怎樣去寫。還有慢慢肝算法導(dǎo)論,重新回顧數(shù)據(jù)結(jié)構(gòu),一堆技術(shù)書等著去肝呢!所有省略了很多內(nèi)容,對自己還未掌握的東西抄上來就沒有多大意義了。給了很多鏈接,但其實大部分鏈接我都未曾去認(rèn)真看過(笑)。
逐步積累,隨性記錄,知道自己要去的地方、要走的路,一直寫著就好?!?01.3.7
參考資料:《離散數(shù)學(xué)及其應(yīng)用》(本科教學(xué)版,Kenneth H.Rosen著,原書第七版)
總結(jié)
以上是生活随笔為你收集整理的【离散数学】高级计数技术的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最大流算法 - 标号法
- 下一篇: 人脸识别进水_万维|人脸识别闸机怎么选?