時間複雜度分析在資訊學競賽中的應用

2021-07-09 22:57:37 字數 2793 閱讀 5459

時間複雜度分析是電腦科學中乙個非常基礎的內容,但同時也是乙個非常重要的內容,電腦科學中的許多內容都圍繞著時間複雜度的研究展開。而資訊學競賽由於其的競賽性,更為注重時間複雜度。

本次的noip2015初賽中有一道題目,大意是這樣的:

有乙個演算法的時間複雜度可用函式t(n)=t(n-1)+n定義,其中t(0)=1,則這個演算法的漸近時間複雜度為?

這一道題是一道選擇題,有四個答案,o(nlogn),o(logn),o(n)和o(n^2)。不少人選擇了第三個答案o(n),也有人選擇了o(nlogn)。但是真正的答案是最後乙個,o(n^2)。顯然把我們學校的不少人給坑到了。而正是因為對於時間複雜度分析的不熟悉,令得超時的「正確演算法」程式出現。在本篇文章中,我將會談到一些關於時間複雜度分析的內容和優化時間複雜度的同時拿高分的技巧。

【時間複雜度的分析】

時間複雜度,在某種意義上可以表示為「計算的次數」。在電腦科學中,我們對此的定義是:

對於乙個函式f(n),當輸入規模為n時,其的時間複雜度可以表示為另乙個函式t(n)的相應值。

舉個例子,比如最大連續和問題,給出乙個序列a,求出最大連續的非空子串中元素的和。換句話說,就是找到這個序列a中的一段數,其的和是最大的。

這一道例題顯然就是乙個動態規劃,但是我們先採用最簡單的方法:列舉。列舉每乙個子串的開頭與結尾,並算出這個子串的和,最後求乙個最大值就好了。**如下:

int count = 0;					//時間複雜度統計 

int bestans = a[0];

for (int i=1;i//開頭

for (int j=i;j//結尾

int sum = 0;

for (int k=i;k<=j;++k)

if (sum > bestans)}}

注意其中bestans不能為0,比如說該序列為-1,-2,-3,-4,-5,那非空的子串最小也就只能是-1了。如果bestans = 0,那樣的話就不滿足非空的要求了。

為什麼要有count這個變數呢?為什麼不能直接衡量執行的時間呢?既然叫時間複雜度,那為什麼不能直接是秒之類的東西呢?因為count與機器的執行速度無關。這一段程式執行在eniac上(當然,不一定能執行),還是執行在天河二號上(也不一定能執行),時間肯定有著顯著的區別,但是演算法是同樣的!也就是說,我們不能夠拘泥於具體的機器,而是應該用另外一種方法,比如用數學的方式,抽象地表達乙個演算法的執行速度。

這一段**的迴圈次數,可以分別表示為sum(i=1 to n),sum(j=i to n),sum(k=i to j),根據乘法原理,其運算的次數可以表示為sum(i=1 to n) * sum(j = i to n) * sum(k = i to j)。根據等差數列求和公式,可得:

t(n) = sum(i=1 to n) * sum(j=i to n) * (j-i-1)

t(n) = sum(i=1 to n)*(n-i+1)(n-i+2)/2

t(n) = n(n+1)(n+2)/6

t(n)=(n^3+3n^2+2n)/6

所以這個公式是乙個關於n的三次多項式,這意味著當n趨向於無窮時,二次方項、線性項和常數項對總的值影響不大。因此我們可以只取增長速度最高的一項,而且常數對於總值影響不大,所以我們也可以忽略常數

那麼我們該怎樣表示呢?我們利用一種叫大o表示法的方法,這稱為演算法複雜度的上界,差不多是最壞情況,也是許多資訊學書中所最常用的方法。在這裡,我們就知道這個演算法的時間複雜度是o(n^3),用符號表示,就是o(n^3)=t(n)。這個的意思是o(n^3)與t(n)是同階的。換一種表達方式,就是t(n)和o(n^3)的增長速度大致相同。

但是我們剛才做了這樣多的計算,難道每一次我們算的時候都要算上這麼半天麼?不是的,我們是可以估計的。估計的方法很簡單:首先看每個迴圈的執行次數,然後乘起來,就大概是時間複雜度了。下面我們來推一下:首先,外面的迴圈執行次數大概為n次,中間的一層,在最壞情況下,也是n次左右。而最裡面的一層在最壞情況下也是差不多n次,迴圈裡面的常數就直接忽略。所以大概就是o(n^3)。

【時間複雜度與程式設計複雜度的優化】

時間複雜度是衡量乙個演算法的標準,但是時間複雜度有時也不夠準確。比如有乙個o(1000000n^2)的演算法和另乙個o(n^5)的演算法,而n<100,那麼後面的演算法雖然沒有第乙個演算法那麼好,但是確實是更優的解法。

但是,時間複雜度也不一定要追求極致。比如說暴搜隨隨便便都能拿個70分,完美演算法要動態規劃+線段樹套平衡樹之類的超長程式,那這30分不拿也罷。畢竟比賽的時候我們可沒有這麼多的時間。這些時間,還不如直接將其他的程式寫個對拍,找找漏洞之類的更有意義。

所以我們發現,常數和低次項,應該盡量優化一下,因為這樣的優化相對簡單。比如說,在輸入大資料的時候,可以用scanf(),實在過大的時候,可以直接用getchar()之類的東西,但是似乎很少有。stl中的set,map,vector比起自己手寫的要慢很多,因為stl的實現是防止溢位的,防止錯誤的,而且其的功能很多,為了考慮相容性、統一性之類的問題,他用的是工程中的解法,而且真正競賽中是不開-o2優化的,如果開了-o2,才能體現出stl的神速優勢(絕對沒有人能夠當場手寫超過stl!)。所以因為不開這個優化,除非沒有時間寫,或者說不會寫,盡量不要用(具體多慢自己試一下就行了)。對於記憶體的申請,比如說鍊錶之類的,盡量直接用陣列,就是所謂的「靜態指標」,用陣列模擬乙個記憶體,首先降低了程式設計的複雜度,其次也在常數上大大提高了效率,避免了堆分配記憶體的低效。搜尋演算法中頻繁使用的小的布林陣列可以直接拿位運算做,就可以大大提高效率。

這樣的優化還有很多,「經典」的《騙分導論——oi》中寫到很多有關的內容。不過其實這樣的優化也在聯賽中應用不多,一般在省賽中才能夠騙到一些比較坑的資料點,以能夠進到省隊之類。

時間複雜度的分析

時間複雜度和空間複雜度是對演算法和 進行衡量的量。演算法分析主要是指分析演算法的效率,時間複雜度越 明 的利用效率就越高。時間複雜度有兩種計算方法 1.事後統計方法 在演算法關鍵位置插入計時器,演算法開始執行開啟計時器,終止時關閉計時器,最終經過計算的演算法執行時間的差值,可以計算出演算法利用效率。...

演算法中時間複雜度分析

演算法的時間複雜度表示演算法執行所需要的時間 大o表示法 是一種體現演算法時間複雜度的記法,如果用n表示資料規模,那麼o f n 表示演算法說需要執行的指令數 消耗的時間 和 f n 成正比。大o表示法指出了演算法執行的最低上限。大o表示法的前邊省略了乙個常數 例子 有乙個字元陣列,將陣列中的每乙個...

演算法中時間複雜度分析

常見時間複雜度 最好 最壞 平均情況時間複雜度 int factorial int n return sum 上面是乙個階乘的函式,第二三行消耗兩個unit time,四五行for語句消耗2n個unit time,這個 的總執行時間t n 2n 2 unit time,我們只需要取其中最大量級,其時...