Skip to content

Commit 9297401

Browse files
docs: Update dsa-anal: time
1 parent 6e006c3 commit 9297401

File tree

1 file changed

+107
-1
lines changed

1 file changed

+107
-1
lines changed

docs/notes/dsa/anal/time.md

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,110 @@ def throughout_arr(nums: list[int]) -> int:
147147

148148
### 平方阶 $O(n^2)$
149149

150-
平方阶的操作数可以看作是输入数据大小 $n$ 的一个二次函数,通常出现在**嵌套循环**中。
150+
平方阶的操作数可以看作是输入数据大小 $n$ 的一个二次函数,通常出现在**嵌套循环**中:
151+
```py
152+
def quadratic(n: int) -> int:
153+
count = 0
154+
for i in range(n):
155+
for j in range(n):
156+
count += 1
157+
return count
158+
```
159+
160+
在基础算法中,**冒泡排序**就是一个算法时间复杂度为 $O(n^2)$ 的典型案例:
161+
```py
162+
def bubble_sort(nums: list[int]) -> list[int]:
163+
ops_count = 0
164+
for i in range(len(nums) - 1, 0, -1):
165+
for j in range(i):
166+
if nums[j] > nums[j + 1]:
167+
tmp: int = nums[j]
168+
nums[j] = nums[j + 1]
169+
nums[j + 1] = tmp
170+
ops_count += 3
171+
return [nums, ops_count]
172+
```
173+
??? success "可视化运行"
174+
<iframe width="800" height="500" frameborder="0" src="https://pythontutor.com/iframe-embed.html#code=def%20bubble_sort%28nums%3A%20list%5Bint%5D%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20ops_count%20%3D%200%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%20-%201,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3E%20nums%5Bj%20%2B%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tmp%3A%20int%20%3D%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%5D%20%3D%20nums%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%20%2B%201%5D%20%3D%20tmp%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ops_count%20%2B%3D%203%0A%20%20%20%20return%20%5Bnums,%20ops_count%5D%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B2,%205,%209,%204,%201%5D%0A%20%20%20%20res%20%3D%20bubble_sort%28nums%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe>
175+
176+
[全屏查看>>>](https://pythontutor.com/render.html#code=def%20bubble_sort%28nums%3A%20list%5Bint%5D%29%20-%3E%20list%5Bint%5D%3A%0A%20%20%20%20ops_count%20%3D%200%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%20-%201,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3E%20nums%5Bj%20%2B%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tmp%3A%20int%20%3D%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%5D%20%3D%20nums%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%20%2B%201%5D%20%3D%20tmp%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ops_count%20%2B%3D%203%0A%20%20%20%20return%20%5Bnums,%20ops_count%5D%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B2,%205,%209,%204,%201%5D%0A%20%20%20%20res%20%3D%20bubble_sort%28nums%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false)
177+
178+
### 指数阶 $O(2^n)$
179+
180+
指数阶的增长速度极快。一个典型的案例是**细胞的分裂**
181+
```py
182+
def exp_cell(n: int) -> int:
183+
count = 0
184+
base = 1
185+
for _ in range(n):
186+
for _ in range(base):
187+
count += 1
188+
base *= 2
189+
return count
190+
```
191+
??? success "可视化运行"
192+
<iframe width="800" height="500" frameborder="0" src="https://pythontutor.com/iframe-embed.html#code=def%20exp_cell%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20base%20%3D%201%0A%20%20%20%20for%20_%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20for%20_%20in%20range%28base%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20%20%20%20%20base%20*%3D%202%0A%20%20%20%20return%20count%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20ops_count%20%3D%20exp_cell%28n%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe>
193+
194+
[全屏查看>>>](https://pythontutor.com/render.html#code=def%20exp_cell%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20base%20%3D%201%0A%20%20%20%20for%20_%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20for%20_%20in%20range%28base%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20%20%20%20%20base%20*%3D%202%0A%20%20%20%20return%20count%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20ops_count%20%3D%20exp_cell%28n%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false)
195+
196+
在实际应用中,指数阶常见于 ==递归函数== 中,下面是上方例子的递归改版:
197+
```py
198+
def exp_cell_recur(n: int) -> int:
199+
if n == 1:
200+
return n
201+
return exp_cell_recur(n - 1) + exp_cell_recur(n - 1) + 1 # +1 表示递归终止操作
202+
```
203+
??? success "可视化运行"
204+
<iframe width="800" height="500" frameborder="0" src="https://pythontutor.com/iframe-embed.html#code=def%20exp_cell_recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20if%20n%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%20n%0A%20%20%20%20return%20exp_cell_recur%28n%20-%201%29%20%2B%20exp_cell_recur%28n%20-%201%29%20%2B%201%20%20%23%20%2B1%20%E8%A1%A8%E7%A4%BA%E9%80%92%E5%BD%92%E7%BB%88%E6%AD%A2%E6%93%8D%E4%BD%9C%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20ops_count%20%3D%20exp_cell_recur%28n%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe>
205+
206+
[全屏查看>>>](https://pythontutor.com/render.html#code=def%20exp_cell_recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20if%20n%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%20n%0A%20%20%20%20return%20exp_cell_recur%28n%20-%201%29%20%2B%20exp_cell_recur%28n%20-%201%29%20%2B%201%20%20%23%20%2B1%20%E8%A1%A8%E7%A4%BA%E9%80%92%E5%BD%92%E7%BB%88%E6%AD%A2%E6%93%8D%E4%BD%9C%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20ops_count%20%3D%20exp_cell_recur%28n%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false)
207+
208+
指数阶常见于穷举算法(暴力搜索、回溯等)。对于数据规模较大的问题,指数阶通常是不可介绍的。
209+
210+
### 对数阶 $O(\log n)$
211+
212+
作为指数运算的逆运算,对数运算的原理不难理解;算法中的对数阶也是如此——上面介绍的指数阶可简单理解为“每轮递增为两倍($O(2^n)$)”,同理,对数阶就可以理解为“每轮缩减到一半($O(\log_2 n)$)”。
213+
214+
!!! tip "一分为 $m$"
215+
准确来说,这里提到的“每轮缩减到一半”只是对数阶的一个典型例子;广义上的对数阶应该是“一分为 $m$”,对应的时间复杂度就是 $O(\log_m n)$;同时,通过**对数换底公式**,我们也可将 $m$ 换成其他等效的底数:
216+
217+
$$
218+
O(\log_m n) = O(\frac{\log_k n}{\log_k m}) = O(\log_k n)
219+
$$
220+
221+
即底数 $m$ 可以在不影响复杂度的前提下转换。故我们在书写对数阶时往往会忽略底数将其直接记为 $O(\log n)$
222+
223+
```py
224+
def logarithmic(n: int) -> int:
225+
count = 0
226+
while n > 1:
227+
n = n / 2
228+
count += 1
229+
return count
230+
```
231+
??? success "可视化运行"
232+
<iframe width="800" height="500" frameborder="0" src="https://pythontutor.com/iframe-embed.html#code=def%20logarithmic%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20while%20n%20%3E%201%3A%0A%20%20%20%20%20%20%20%20n%20%3D%20n%20/%202%0A%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20return%20count%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%2032%0A%20%20%20%20ops_count%20%3D%20logarithmic%28n%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe>
233+
234+
[全屏查看>>>](https://pythontutor.com/render.html#code=def%20logarithmic%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20while%20n%20%3E%201%3A%0A%20%20%20%20%20%20%20%20n%20%3D%20n%20/%202%0A%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20return%20count%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%2032%0A%20%20%20%20ops_count%20%3D%20logarithmic%28n%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false)
235+
236+
与指数阶类似,对数阶也常出现于 ==递归函数== 中:
237+
```py
238+
def log_recur(n: int) -> int:
239+
if n <= 1:
240+
return 0
241+
return log_recur(n / 2) + 1
242+
```
243+
??? success "可视化运行"
244+
<iframe width="800" height="500" frameborder="0" src="https://pythontutor.com/iframe-embed.html#code=def%20log_recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20if%20n%20%3C%3D%201%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20return%20log_recur%28n%20/%202%29%20%2B%201%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%2032%0A%20%20%20%20ops_count%20%3D%20log_recur%28n%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe>
245+
246+
[全屏查看>>>](https://pythontutor.com/render.html#code=def%20log_recur%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20if%20n%20%3C%3D%201%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20return%20log_recur%28n%20/%202%29%20%2B%201%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%2032%0A%20%20%20%20ops_count%20%3D%20log_recur%28n%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false)
247+
248+
对数阶常出现于基于**分治策略**的算法中,其增长缓慢,是仅次于常数阶的理想时间复杂度。
249+
250+
## 最坏情况与平均情况
251+
252+
**最坏情况运行时间**是一种**保证**,可将其视为一个**效率安全值**,使得对应算法能够被全盘接受。==一般在没有特殊说明的情况下,所谓的**算法时间复杂度**指的都是**最坏时间复杂度**==
253+
254+
**平均时间时间复杂度**则是一种**数学期望**,可以体现算法在随机输入数据下的运行效率,使用 $\Theta (f(n))$ 表示。
255+
256+
在复杂算法中,平均时间复杂度的计算要比最坏时间复杂度复杂度得多,因为很难分析出在数据分布下的整体数学期望。

0 commit comments

Comments
 (0)