關於時間複雜度的一點簡單總結

2021-08-20 14:07:48 字數 3854 閱讀 2153

演算法的複雜度分為時間複雜度和空間複雜度,今天在這裡主要講一下時間複雜度。

​ 時間複雜度,簡單來說,就是通過程式語句的執行次數來估計程式執行時間的乙個函式,用o表示。(在相同硬體和軟體的情況下);可以把它認為就是花費時間的函式的數量級。

簡單來說,就是為了提高計算機工作的效率,因為現實生活中,通常處理的是大量的資料,所以需要通過優化程式,使得提公升工作效率。以及為分析程式效率提供了便利;

一般情況下,演算法中基本操作重複執行的次數是問題規模n的某個函式,用t(n)表示,若有某個輔助函式f(n),使得當n趨近於無窮大時,t(n)/f(n)的極限值為不等於零的常數,則稱f(n)是t(n)的同數量級函式。記作t(n)=o(f(n)),稱o(f(n))為演算法的漸進時間複雜度(o是數量級的符號 ),簡稱時間複雜度。

以上是時間複雜的數學定義:

在這裡f(n)代表的是演算法需要的增長速度,o(n)就是把f(n)的最高項係數去掉之後,保留的最高項次數,就是表示的是乙個數量級。簡單來說就是我們估算出f(n)的函式,然後再對它進行o運算的結果。

幾個簡單的判定方法:

1.常數級是o(1):就是不管n多大,始終是乙個常數;

比如說執行一條語句,a+b;

2.通常迴圈是o(n)(一層);(因為執行n次常數級操作)

​ 巢狀通常情況是o(n^m);

3.採用二分策略可以降到log2(n);

e.g.1(乙個簡單的例子)

計算(a^b)%mod;

​ 通常我們會這樣做:

#include

#include

const

long

long mo = 500;

#define ll long long

int main() ;

clock_t end = clock();

printf("%f", (double)(end - start) / clocks_per_sec);

printf("%lld\n", a);

return

0;}

通過簡單的估算我們可以得到時間複雜度是o(n)

這個程式在一定資料範圍下的執行時間我們是可以接受的,但是想想如果b很大的情況下呢?

比如b=10^17呢?(通常1s約等於o(10^8));意思就是說在b很大的情況下,我們要等很久才能出結果;

那麼這種情況我們該怎麼解決呢?

其實剛剛的過程就像是我們乙個個的列舉計算,就像用手搬磚,一次能做的拿的東西就只有乙個。

我們其實可以換種思路來運算:

比如我們要求2^64

我們可以把它看成(4)^32,(16)^16…..,就是每次給降冪的一半,把底數上公升到它的平方;

這樣我們的計算次數就會減少;

但是對於2^65來說,我們不能直接分解,我們可以把它看成2^64*2,設定乙個臨時變數來儲存結果,當指數市集是奇數的時候就用臨時變數乘一下,然後繼續分解;

#include

#include

#define ll long long

const ll mo = 500;

ll quick_pow(ll a, ll b)

return ans;

}int main()

這裡我用了同一組資料來測試程式

​ 2 100000000000000000(17個0)

對於第乙個程式執行的時間是:。。。。。。

​ 很久 ,我用手機測得時間,10分鐘都沒出來結果

而對於第二個執行的時間就少的多:

分析:因為每次都將指數除二,比如我們計算2^64,我們最多計算幾次(2^64約等於10^18);

我們就可以把它轉換成log2(n)的演算法;

總結:計算機雖然計算速度比較快,但是我們也得讓它有效率,有些情況下我們可以借助數學性質來降低時間複雜度;

e.g.2

* 關於排序:

​ 就是想到排序,我們大一的可能在c語言課上接觸到的有冒泡,選擇排序等,這裡我主要講兩個排序:

​ 氣泡排序,歸併排序(降序):

​ 先看氣泡排序:

for(int i=0

;i }

}

這個演算法的時間複雜度就是o(n^2);

還是剛剛那個問題,如果資料範圍很大呢?

這裡我們介紹的是歸併排序:

歸併歸併,就是先歸再並,簡單來說,就是我們把乙個數列一分為二,然後再把子區間一分為二,直到整個區間只有乙個數字(構造乙個樹形結構),然後我們合併兩個區間的時候就可以利用樹的後序遍歷合併,就是每次將兩個區間的首位數進行比較,把數字大的放在新陣列的前面,對與有乙個區間沒有元素了,就把另乙個區間的數依次加入隊尾,再從小區間合併到大區間,最後完成排序。

**實現如下:

void sort_x(int l,int r)

else c[tmp++]=a[i++];

}if(i<=mid) for(;i<=mid;) c[tmp++]=a[i++];

if(j<=r) for(;j<=r;) c[tmp++]=a[j++];

for(i=l;i<=r;i++) a[i]=c[i];}}

例項對比:

對於這樣一組資料:

程式一:

​ 程式二

可以看到:

下面那個程式執行時間遠遠比第乙個快;

e.g.3;

判斷素數:

思路一:試除法:

bool

is(int x)

return

true;

}

時間複雜度:o(sqrt(n))對於這個程式有兩個問題,第乙個就是無法在短時間內判斷乙個大數為素數,第二個問題就是無法靈活的處理多次詢問的問題。

我們先來看第二個問題:(下面的範圍:n<=1^6);

如果我們要查詢很多個數是不是素數,最暴力的方法就是每個數都判斷一下,假設有m次詢問(m<10^6),

那麼每次的時間複雜度就是o(sqrt(n)),當然對於一定範圍內我們可以利用陣列打表,詢問就只用一次就可以解決:

bool ok[1000006]=}}

分析:雖然是乙個二重迴圈,但是我們每次都會把後面的資料,給篩選掉,並且內層迴圈裡面的次數取決於素數的個數,而素數的又比較少,所以該執行次數的數量級就是o(n)級別的,這樣就比上面的程式快了很多。

第乙個問題的話有興趣的讀者可以去了解一下公尺勒拉賓演算法。這個演算法可以在短時間內判斷乙個大數是不是素數。

總結:​ 解決問題的思路有很多,我們解決問題的思路決定了我們解決問題的時間。從上面的例子我們可以看出 ,通常情況下我們可以借助數學解決問題,比如說數列求和。。。也可以借助一些資料結構:比如說我們的排序還有一種堆排序,也有利用二進位制性質的樹狀陣列可以快速求出字首和,解決集合關係的並查集。同時我們可以借助一些演算法思想,比說分治。當然我們也可以借助一下計算機本身的運算機制(二進位制),位運算。

以上就是我的簡單總結,總結內容偏簡單,有什麼不足請大家指出。

關於程式的運算時間複雜度的一點思考

一般考察演算法的複雜度,我們常用到時間複雜度,和空間複雜度來定義。其中時間,其實就是計算機執行這個演算法執行的指令次數,是乙個計算量的概念 而空間複雜度是指執行這個演算法所需的記憶體空間,即暫存器的資源。這篇部落格主要 一下自己關於時間複雜度的一些思考。對比上述四個例子,可以發現,程式的時間複雜度和...

關於程式的運算時間複雜度的一點思考

一般考察演算法的複雜度,我們常用到時間複雜度,和空間複雜度來定義。其中時間,其實就是計算機執行這個演算法執行的指令次數,是乙個計算量的概念 而空間複雜度是指執行這個演算法所需的記憶體空間,即暫存器的資源。對比上述四個例子,可以發現,程式的時間複雜度和基本操作被執行的次數有關。這裡很重要的一點是 基本...

關於時間複雜度

1.演算法複雜度分為 時間複雜度和空間複雜度。作用 時間複雜度是度量演算法執行的時間長短 而空間複雜度是度量演算法所需儲存空間的大小。2.一般情況下,演算法的基本操作重複執行的次數是模組n的某乙個函式f n 因此,演算法的時間複雜度記做 t n o f n 分析 隨著模組n的增大,演算法執行的時間的...