一些奇奇怪怪的時間複雜度分析(持續更新)

前言

整理了一些長相清奇的時間複雜度分析……
約定:
1.如果沒有特殊說明,默認T(0)=T(1)=1T(0)=T(1)=1
2.如果沒有特殊說明,默認log的底數爲2。
3.一些地方會說T(n)=O(...)T(n)=O(...),意會就好qwq

0.一些定義

0.1 大O記號定義

對於函數f,gf,g,若\exist常數n0,cn_0,c,使得對於n>n0\forall n>n_0f(n)<cg(n)f(n)<c*g(n),那麼說f(n)f(n)O(g(n))O(g(n))的,即f(n)O(g(n))f(n)\in O(g(n))

0.2 時間複雜度定義

對於一個程序所需要執行的運算次數T(n)T(n),若T(n)O(f(n))T(n)\in O(f(n)),則把O(f(n))O(f(n))稱爲這個程序的時間複雜度。
例如當T(n)=13n3+100n+10086T(n)=\frac{1}{3}n^3+100n+10086時,我們說這個程序的時間複雜度是O(n3)O(n^3)。證明是顯然的,隨便取個n0=100,c=100n_0=100,c=100就可以了,不多說。
當然,上面的T(n)T(n)也可以說是O(n4)O(n^4)的,這並不矛盾。一般來說,我們用的時間複雜度是增長速度儘量慢的,也就是說取儘量貼近實際的T(n)T(n)的增長速度的複雜度。所以一般我們用O(n3)O(n^3)而不是O(n4)O(n^4)來表示上面的T(n)T(n)

再舉幾個例子:
T(n)=n+lognT(n)=n+logn,那麼T(n)T(n)O(n)O(n)的。對於n>10n>10n+logn<2nn+logn<2n
T(n)=2n+logn=n2nT(n)=2^{n+logn}=n2^nT(n)T(n)O(n2n)O(n2^n)的。注意這裏不能從n+lognn+lognO(n)O(n)的推出2n+logn2^{n+logn}2n2^n的,要注意。至於爲什麼這個不是O(2n)O(2^n),因爲n0n_0cc必須是\color{red}常數

1.基本時間複雜度分析

1.0 主定理

對於遞歸函數,如果滿足T(n)=aT(nb)+f(n)T(n)=aT(\frac{n}{b})+f(n),分類討論:
假設cc是一個正常數。
(1)若f(n)=nlogbaf(n)=n^{log_ba},那麼T(n)=O(nlogbalogn)T(n)=O(n^{log_ba}logn)
(2)若f(n)=nlogbacf(n)=n^{log_ba-c},那麼T(n)=O(nlogba)T(n)=O(n^{log_ba})
(3)若f(n)=nlogba+cf(n)=n^{log_ba+c},且對於所有足夠大的nnaf(nb)<f(n)af(\frac{n}{b})<f(n),那麼T(n)=O(f(n))T(n)=O(f(n))

1.1 線性遞推式

1.T(n)=T(n1)+c1.T(n)=T(n-1)+ccc是常數),則T(n)=O(cn)T(n)=O(cn)
2.T(n)=T(n1)+n2.T(n)=T(n-1)+n,則T(n)=O(n2)T(n)=O(n^2)
奇水……

1.2 普通分治

T(n)=2T(n2)+nT(n)=2T(\frac{n}{2})+n,則T(n)=O(nlogn)T(n)=O(nlogn)
證明:展開,T(n)=O(n)+2T(n2)T(n)=O(n)+2T(\frac{n}{2})
=O(n)+2O(n2)+4T(n4)=O(n)+2*O(\frac{n}{2})+4T(\frac{n}{4})
=...=...
=O(n)+O(n)+...+O(n)=O(n)+O(n)+...+O(n) (每次除以22,除lognlogn次到1,所以共lognlognO(n)O(n)
=O(nlogn)=O(nlogn)

主定理版:a=2,b=2,logba=log22=1a=2,b=2,log_ba=log_22=1
f(n)=O(n1)=O(nlogba)f(n)=O(n^1)=O(n^{log_ba})(打不出中間有一橫的符號……意會一下qwq)
=>T(n)=O(f(n)logn)=O(nlogn)=>T(n)=O(f(n)*logn)=O(nlogn)

1.3 詭異的分治(也許這不是分治……)

T(n)=2T(n2)+kT(n)=2T(\frac{n}{2})+k,則T(n)=O(nk)T(n)=O(nk)
證明:展開,T(n)=O(k)+2T(n2)T(n)=O(k)+2T(\frac{n}{2})
=O(k)+2O(k)+4T(n4)=O(k)+2O(k)+4T(\frac{n}{4})
=...=...
=O(k)+2O(k)+4O(k)+...+2t1O(k)+2tT(1)=O(k)+2O(k)+4O(k)+...+2^{t-1}O(k)+2^{t}T(1) (爲了方便,記t=lognt=logn
=(2t1)O(k)+O(2t+1)=(2^{t}-1)O(k)+O(2^{t+1})
=O(nk)+O(2n)=O(nk)+O(2n)
=O(nk)=O(nk)

主定理:????

1.4 玄學分治

T(n)=2T(n2)+nlognT(n)=2T(\frac{n}{2})+nlogn,則T(n)=O(nlog2n)T(n)=O(nlog^2n)
證明:考慮T(2n)T(2^n)
T(2n)=2T(2n1)+2nnT(2^n)=2T(2^{n-1})+2^n*n
=2(2T(2n2)+2n1(n1))+2nn=2(2T(2^{n-2})+2^{n-1}*(n-1))+2^n*n
=4T(2n2)+2n(n1)+2nn=4T(2^{n-2})+2^n*(n-1)+2^n*n
=...=...
=O(2n)1+O(2n)2+...+O(2n)n=O(2^n)*1+O(2^n)*2+...+O(2^n)*n
=O(2nn2)=O(2^nn^2)
然後T(n)=T(2logn)=O(2logn(logn)2)=O(nlog2n)T(n)=T(2^{logn})=O(2^{logn}(logn)^2)=O(nlog^2n)
之前嘗試用數學歸納法……發現竟然弄出普通分治也是O(nlog2n)O(nlog^2n)……然後才發現那樣用數學歸納法是不嚴謹的……

主定理版:a=2,b=2,logba=log22=1a=2,b=2,log_ba=log_22=1
f(n)=O(nlogn)=O(nlogbalogn)f(n)=O(nlogn)=O(n^{log_ba}logn)
=>T(n)=O(f(n)logn)=O(nlog2n)=>T(n)=O(f(n)*logn)=O(nlog^2n)

2.長相奇特的遞歸式

2.1 巨神mhy弄出的奇怪東西

有一次巨神mhy在CF比賽的時候弄出一個T(n)=2T(n)+lognT(n)=2T(\sqrt n)+logn的東西……
那麼T(n)=O(lognloglogn)T(n)=O(logn*loglogn)
證明:上式不好直接證,考慮它的等價形式:
因爲2logn=n2^{logn}=n,所以上式等價於T(2n)=O(nlogn)T(2^n)=O(nlogn)
由已知,T(2n)=2T(2n)+O(log(2n))T(2^n)=2T(\sqrt{2^n})+O(log(2^n))
化簡,T(2n)=2T(2n2)+O(n)T(2^n)=2T(2^\frac{n}{2})+O(n)
發現nn每次都變成n2\frac{n}{2},所以類比普通分治的證明,T(2n)=O(nlogn)T(2^n)=O(nlogn)
於是T(n)=O(lognloglogn)T(n)=O(lognloglogn)

主定理:????

2.2 一道詭異的初賽題

給出式子T(n)=2T(n4)+nT(n)=2T(\frac{n}{4})+\sqrt n
感覺比上面的那一個還水……思路很自然qwq
同樣考慮T(4n)=2T(4n1)+4nT(4^n)=2T(4^{n-1})+\sqrt{4^n}
=2(2T(4n2)+4n1)+2n=2(2T(4^{n-2})+\sqrt{4^{n-1}})+2^n
=4T(4n2)+24n1+2n=4T(4^{n-2})+2*\sqrt{4^{n-1}}+2^n
=4T(4n2)+2n+2n=4T(4^{n-2})+2^n+2^n
=...=...
=2nT(1)+2n+2n+...+2n=2^nT(1)+2^n+2^n+...+2^n (共nn2n2^n
=O(2nn)=O(2^n*n)
然後T(n)=T(4log4n)=O(2log4nlog4n)T(n)=T(4^{log_4n})=O(2^{log_4n}log_4n)
因爲4log4n=n4^{log_4n}=n,所以2log4n=n2^{log_4n}=\sqrt n,而log4nlog_4nlognlogn同級
所以T(n)=O(nlogn)T(n)=O(\sqrt n logn)

主定理版:a=2,b=4,logba=log42=0.5a=2,b=4,log_ba=log_42=0.5
f(n)=O(n)=O(n12)=O(nlogba)f(n)=O(\sqrt n)=O(n^\frac{1}{2})=O(n^{log_ba})
=>T(n)=O(f(n)logn)=O(nlogn)=>T(n)=O(f(n)*logn)=O(\sqrt nlogn)

2.3 惡臭學軍題

T(n)=25T(n5)+nnT(n)=25T(\frac{n}{5})+n\sqrt n
考慮T(5n)=25T(5n1)+5n5nT(5^n)=25T(5^{n-1})+5^n\sqrt{5^n}
=252T(5n2)+255n15n1+5nsqrt5n=25^2T(5^{n-2})+25*5^{n-1}\sqrt{5^{n-1}}+5^n*sqrt{5^n}
=...=...
=25nT(1)+25n155+25n22525+...+5n5n=25^nT(1)+25^{n-1}5\sqrt 5+25^{n-2}25\sqrt{25}+...+5^n\sqrt{5^n}
=54n+54n1+54n2+...+53n=\sqrt{5^{4n}}+\sqrt{5^{4n-1}}+\sqrt{5^{4n-2}}+...+\sqrt{5^{3n}}
=54n+153n51=\frac{\sqrt{5^{4n+1}}-\sqrt{5^{3n}}}{\sqrt 5-1} (等比數列求和)
=552n5n5n51=\frac{\sqrt 5*5^{2n}-5^n\sqrt{5^n}}{\sqrt 5-1}
5n5^n換成nn,得T(n)=5n2nn51=O(n2)T(n)=\frac{\sqrt 5n^2-n\sqrt n}{\sqrt 5-1}=O(n^2)

主定理版:a=25,b=5,f(n)=O(nn)=O(n1.5)a=25,b=5,f(n)=O(n\sqrt n)=O(n^{1.5})
logba=log525=2log_ba=log_5{25}=2
f(n)=O(n1.5)=O(nlogba0.5)f(n)=O(n^{1.5})=O(n^{log_ba-0.5})
=>T(n)=O(nlogba)=O(n2)=>T(n)=O(n^{log_ba})=O(n^2)

2.4 證明主定理

3.攤還分析

注:爲了方便,部分內容裏寫了O(0)O(0)qwq(不嚴謹,但是是這個意思)

3.1 進棧1個/出棧k個

你需要維護一個棧,初始爲空,要求資磁兩種操作:
1.把xx壓進棧
2.彈出kk個元素(kk\le 棧大小)
總共qq中操作,棧大小<=n<=n
暴力即珂……時間複雜度?1操作O(1)O(1),2操作O(k)O(k)
時間複雜度O(qk)=O(nq)?O(qk)=O(nq)?
口胡出時間複雜度爲線性qwq

發現每次彈出kk個元素,kk的範圍是與棧大小,也就是有多少個xx沒被彈出的1操作有關。

覈算法:(覈算法大法吼!)
每一個1操作,以後最多把這個元素彈出一次(廢話qwq)
記1操作的攤還代價爲11,則2操作的複雜度變成O(0)O(0)
然後1操作的總代價爲O(1)O(1),共qq次操作,時間複雜度O(q)O(q)

聚合分析:(聚合分析也還珂以)
發現如果1操作有xx個,那麼2操作的總時間複雜度最高爲O(x)O(x)
所以考慮最壞情況,1操作有qq個時,2操作的總時間複雜度爲O(q)O(q)
所以總時間複雜度爲O(q)+O(q)=O(q)O(q)+O(q)=O(q)

勢能法:(惡臭)
勢能法蜃孫……
T(i,)T(i,勢能)表示前ii次操作珂能達到的最高複雜度(即勢能qwq),T(i,)T(i,實際)表示前ii次操作實際達到的複雜度。
f(i)f(i)表示執行了第ii個操作後的勢能
勢能公式:T(i)=T(i,)+f(i)f(i1)T(i)=T(i,實際)+f(i)-f(i-1)
即:第ii步操作的(最後平均)總代價等於第ii步操作的實際代價加上從第i1i-1步操作到第ii步操作的勢能變化。
回到例子,讓勢能=棧中有多少個元素,那麼1操作會讓勢能+1,2操作會讓勢能-k。
於是1操作的總代價爲1+1=21+1=2,2操作總代價爲kk=0k-k=0
因此1操作總複雜度爲O(1)O(1),2操作總複雜度爲O(0)O(0)
最後的總複雜度爲O(q)O(q)
惡臭無比……

3.2 bzoj3133 ball machine複雜度分析

推銷一波題解
懶得寫了……和3.1沒多大差別……去題解裏看吧qwq

3.3 表擴張(vector的push_back?)

維護一個數組,初始大小爲1,要求資磁插入一個數,當數組滿了的時候,開一個大小爲原來的兩倍的數組,把當前的數據複製過去。(規定單次擴容複雜度爲O()O(擴容前數組大小)
每一次最壞複雜度爲O(n)O(n)……時間複雜度O(n2)O(n^2)
一共lognlogn次擴容,每次O(n)O(n),時間複雜度O(nlogn)O(nlogn)
然而並不是qwq

覈算法:(蜃香)
每次把數組擴大到原來的兩倍的時候,這O(n)O(n)的複雜度均攤到每個插入操作上qwq
於是每個插入操作的攤還代價爲O(1)O(1),擴容操作變爲O(0)O(0)
插入操作總複雜度是O(1)O(1),所以最後總複雜度O(n)O(n)

聚合分析:
假設最後數組大小爲nn,則擴容了lognlogn
擴容複雜度爲O(1)+O(2)+O(4)+...+O(2logn)=O(n)O(1)+O(2)+O(4)+...+O(2^{logn})=O(n)
插入複雜度爲O(1)n=O(n)O(1)*n=O(n)
所以總複雜度爲O(n)O(n)

勢能法:(惡臭)
還沒想到什麼直觀的勢能函數……但是想到一個詭異的東西不知道是否正確……口胡一波qwq
令1操作的勢能變化爲1(意義大概是在擴容的道路上走了多遠?),那麼2操作的勢能變化爲-n。
口胡得出1操作總代價爲O(1)O(1),2操作總代價爲O(0)O(0)
於是總複雜度爲O(n)O(n)



//兩條分割線以表敬意
令勢能函數f(i)=2numsizf(i)=2num-siz,其中numnum表示數組內元素的個數,sizsiz表示數組大小。
易證若一個插入操作沒有觸發擴容,引起的勢能變化是2。攤還代價爲O(1)O(1)
若某次插入操作引起了擴容,假設插入前的數組大小是nn,那麼裏面的元素個數也是nn
擴容前:勢能f=2numsiz=2nn=nf=2num-siz=2n-n=n
擴容後:勢能f=2numsiz=2(n+1)2n=2f=2num-siz=2(n+1)-2n=2
所以勢能變化爲2n2-n,那麼引起擴容操作的插入操作的攤還代價爲n+(2n)=O(1)n+(2-n)=O(1)
綜上,插入操作的攤還複雜度爲O(1)O(1)
不得不說,勢能函數還是很妙的……

3.4 表擴張&刪除(push_back,pop_back)

//咕咕咕
問:如果你需要維護一個數據結構,需要資磁在尾部插入/刪除數,還要使浪費的空間儘量少,怎麼做?
(擴容,收縮複雜度均爲O()O(數的個數)
如果模仿3.3,當數組個數小於siz2\frac{siz}{2}時收縮,顯然會gg(當數的個數爲siz1siz-1時不斷插入、刪除、插入、刪除……)
所以考慮減少收縮次數:
不妨讓數的個數少於siz4\frac{siz}{4}時收縮,這樣就不會有毒瘤(LXL)來卡掉了qwq
時間複雜度分析:

覈算法:
每次擴容的複雜度分攤到至少nn個插入操作上,每個插入操作分攤到O(1)O(1)
收縮時,假設最後一次擴容時數組大小爲sizsiz,那麼數的個數最多的時候最少也是siz2\frac{siz}{2}個,而收縮時數的個數會變成siz4\frac{siz}{4}個,所以中間最少會有siz4\frac{siz}{4}級別的刪除操作。
O(siz)O(siz)的複雜度分攤到siz4\frac{siz}{4}級別的操作上,每個操作分到O(1)O(1)
綜上,插入/刪除的攤還複雜度均爲O(1)O(1)

聚合分析:
假設插入、刪除操作共有nn個。那麼插入和收縮操作最多都是nn的數量級。
由3.3的聚合分析珂知,擴容操作的總複雜度爲O(n)O(n)
假設O(2x)O(2^x)的收縮操作出現了axa_x次,那麼收縮操作複雜度F(n)=O(1)a1+O(2)a2+...+O(2n)anF(n)=O(1)*a_1+O(2)*a_2+...+O(2^n)*a_n(忽略一些邊界問題qwq)
因爲若一個收縮操作是O(2siz)O(2^{siz})的,那麼這個收縮操作珂以支配2siz2^{siz}級別的刪除操作,
所以axa_xO(2x)O(2^{x})的收縮操作珂以支配2xax2^x*a_x個收縮操作。
因爲收縮操作的總數不超過nn,所以1a1+2a2+...+2nann1*a_1+2*a_2+...+2^{n}*a_n\le n
所以F(n)=O(n)F(n)=O(n)
單次插入/刪除的複雜度是O(1)O(1)nn次就是O(n)O(n)
綜上,總時間複雜度爲O(n)O(n)

勢能法:
咕咕咕……

暫時就這些qwq

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章