甚麼是時間複雜度?
時間複雜度對我(你)來說很重要嗎?
怎麼估複雜度?
雖然是遠距上課,但還是希望大家能有多一點互動
有想法就可以提出來
課程內容的難易度未必適合所有人
有問題就問
覺得太簡單不用急,後面的講師一定可以滿足你
雖然這堂課可能偏理論,但並非那麼嚴謹
一個描述該演算法(或程式)執行時間的函式
通常假設各種基本操作都花一單位時間
加、減、乘、除、取模
賦值
條件判斷
存取變數
…...
F(N)=N2+2N+3
cin >> N;
i = 0;//1
j = 0;//1
while(i < N)//N+1
{
while(j < i)//1+2+3+..+N=(N+1)N/2
{
j++;//1+2+3+..+(N-1)=(N-1)N/2
}
i++;//N
}
f(x)=O(g(x)) 代表存在M,x0>0,使得所有x≥x0都有∣f(x)∣≤M∣g(x)∣
也就是說,當x很大的時候,存在常數M,使得M∣g(x)∣一定會大於等於f(x)
其實也就表示,g(x)是f(x)的某種上界
看例子!
f(x)
g(x)
f(x)=O(g(x))?
x
100x
Yes
x
100x
Yes
x3
x2
No
x2
10000x2log(x)
Yes
Yes
x−1000x−100
x
可以看出,只要有了big-O符號,我們就可以無視低次項跟常數,更方便地表示複雜度
因此,在剛剛的例子中,原本複雜度是
F(N)=N2+N+4,現在也可以用O(N2)來表示該程式的時間複雜度
雖然也可以用O(N3)來表示,但我們追求的是複雜度越小越好!
這樣估複雜度就不用那麼麻煩了!
O(N3)
cin >> N;
i = 0;
j = 0;
while(i < N*N+N-2)
{
while(j < N*3-10)
{
j++;
}
i++;
}
O(N2N)
cin >> N;
i = 0;
j = 0;
while(i < N*N+N-2)
{
while(j*j < N)
{
j++;
}
i++;
}
即使你的程式是正確的,如果他的執行時間超過了題目的限制,你會得到一個 而無法得到分數
一般的judge一秒可以跑108筆運算左右,所以想要知道自己的程式會跑幾秒,就把題目的範圍限制代入所估的複雜度中,最後再除以108。然後再跟題目的限制秒數比比看就可以知道會不會TLE了
注意我們要估的通常是最糟的情形
給定一個單字,請你找出其中"最長的"連續的一段,使得那段正著唸反著唸都一樣。(單字長度L≤5000)
範例輸入:
abcbbd
範例輸出:
bcb
枚舉所有的「連續的一段」,再把那一段掃過一遍就好囉!
abac
abac
abac
abac
abac
abac
abac
abac
abac
abac
因為長度為1的區間有L個,2的區間有L−1個,...,L的區間有1個,所以複雜度是1×L+2×(L−1)+...+L×1=6L3+3L2−4L
複雜度是:
枚舉連續的一段 O(L2)
然後再把那段掃一遍 最糟O(L)
所以就是O(L3)囉!
跑100秒都跑不完QQ
枚舉所有的「中間點」,然後看可以往外擴張多少!
枚舉所有的「中間點」,然後看可以往外擴張多少!
a b c b b d
長度:3
枚舉所有的「中間點」,然後看可以往外擴張多少!
a b c b b d
長度:2
複雜度是:
枚舉中間點(包含中間字母語中間空隙) O(L)
然後看可以往外擴張多少 最糟O(L)
所以就是O(L2)囉!
50002=2.5×107,理論上一秒內可以跑完!
回文好玩好文回(6)
10 min
除了判斷自己的演算法可不可以拿到想要的分數之外,因為比較難的題目通常都會把限制的範圍出的盡量大,所以看到範圍後也可以得到一些關於作法複雜度的線索
給你一個正整數Q,接下來有Q筆詢問,每次詢問你一個整數N是不是質數。
範例輸入:
3
1
2
3
範例輸出:
No
Yes
Yes
給你一個正整數Q,接下來有Q筆詢問,每次詢問你一個整數N是不是質數。
範圍:Q≤100,N≤1010
給你一個正整數Q,接下來有Q筆詢問,每次詢問你一個整數N是不是質數。
範圍:Q≤106,N≤106
說到最直接判斷質數的方法
試除法?
質數判斷(7)
9 min
說到找出比K小的質數的方法
篩法?
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
這樣做的複雜度是2K+3K+5K...
有點難估所以抓個上界:
2K+3K+5K...≤K(1+21+31+...+K1)
質數判斷2(11)
9 min
Q≤100,N≤1010
Q≤106,N≤106
O(QN)
O(Klog(K)+Q)
AC
AC
TLE
TLE
範圍
做法複雜度
N≤10
O(4N),O(N7),O(N!),...
N≤20
O(N×2N),O(N6),...
N≤50~ 100
O(N4)
N≤200
O(N3log(N))
N≤500
O(N3)
N≤2000
O(N2log(N))
範圍
作法複雜度
N≤5000
O(N2)
N≤5×104
O(NN)
N≤105
O(NN),O(Nlog2(N)),O(Nlog(N))
N≤106
O(Nlog(N)),O(N)
N≤107
O(N)
N≤1012
O(N)
N≤1018
O(log(N)),O(log2(N)),...
其實大家都會了?
根據之前的例子,基本上就是看每一層迴圈最多跑多少次,然後全部乘起來?
但是......
給你一個單字,你要從中找到其中連續的一段,使得該段是包含全部的字母中最短的(很多個的話請輸出最先出現的,不存在請輸出"QQ")
範例輸入:
aabbabcdefghijklmnoqrstuvwxyz
範例輸出:
abcdefghijklmnoqrstuvwxyz
a b c b a
a | b | c |
---|---|---|
L
a b c b a
a | b | c |
---|---|---|
1 |
L
a b c b a
a | b | c |
---|---|---|
1 | 1 |
L
a b c b a
a | b | c |
---|---|---|
1 | 1 | 1 |
L
長度:3
a b c b a
a | b | c |
---|---|---|
L
a b c b a
a | b | c |
---|---|---|
1 |
L
a b c b a
a | b | c |
---|---|---|
1 | 1 |
L
a b c b a
a | b | c |
---|---|---|
2 | 1 |
L
a b c b a
a | b | c |
---|---|---|
1 | 2 | 1 |
L
長度:4
a b c b a
a | b | c |
---|---|---|
L
a b c b a
a | b | c |
---|---|---|
1 |
L
a b c b a
a | b | c |
---|---|---|
1 | 1 |
L
a b c b a
a | b | c |
---|---|---|
1 | 1 | 1 |
L
長度:3
a b c b a
a | b | c |
---|---|---|
1 | 1 |
L
a b c b a
a | b | c |
---|---|---|
2 | 1 |
L
a b c b a
a | b | c |
---|---|---|
1 | 2 | 1 |
L
長度:4
for(L = 1;L <= N;L++)
{
for(R = L;R <= N;R++)
{
//do something
if(something happens)
{
ans = min(ans,R-L+1);
break;
}
}
}
int R = 1;
for(L = 1;L <= N;L++)
{
while(R <= N)
{
//do something
if(something happens)
{
ans = min(ans,R-L+1);
//do something
break;
}
R++;
}
}
電皇的小寫英文字母(5)
15 min
int f(int n)
{
if(n == 1) return 1;
return n*f(n-1);
}
f(n)→f(n−1)→...→f(1)
每個函式本身都O(1)
總共O(n)!
int Pow(int a,int b)
{
if(n == 0) return 1;
int t = Pow(a,b/2);
if(b & 2 == 1) return t*t*a;
else return t*t;
}
每個函式本身都O(1),共有O(log(b))次函式被呼叫
總共O(log(b))!
void f(int n)
{
if(n <= 1) return 1;
//Do some O(n) things
f(n/2);
f(n/2);
}
f(n)
f(⌊2n⌋)×2
f(⌊4n⌋)×4
⋯
⋯
⋯
f(1)×n
每層都是跑O(n)
總共有O(log(n))
總複雜度O(nlog(n))!
上課例題:
回家練習: