1.算法效率评估
(1)时间效率:算法运行速度的快慢
(2)空间效率:算法占用内存空间的大小
想平常论文中评定一个方法的优劣时,都会有一种实际测试和另一种理论估算的方法。实际测试多受限于设备条件、时间周期、计算资源等客观条件,难以实现;因此,考虑理论估算方法。
渐近复杂度分析(asymptotic complexity analysis),简称复杂度分析。
简而言之,与输入数据的大小相关。即随着输入数据大小的增加,算法执行所需时间和空间的增长趋势。
- “时间和空间资源”分别对应时间复杂度(time complexity)和空间复杂度(space complexity)。
- “随着输入数据大小的增加”意味着复杂度反映了算法运行效率与输入数据体量之间的关系。
- “时间和空间的增长趋势”表示复杂度分析关注的不是运行时间或占用空间的具体值,而是时间或空间增长的“快慢”。(???趋势?后续需要更多理解)
2.迭代
(1)for循环-------适合在预先知道迭代次数时使用
在K聚类工况划分中,使用的就是for循环,对于其迭代次数,是按照一定经验设定。
时间复杂度描述的是“线性关系”
(2)while循环
在 while
循环中,程序每轮都会先检查条件,如果条件为真,则继续执行,否则就结束循环。
在稳态判别中,使用的就是while循环,对于其迭代次数,是根据其(当前序号+窗长)不超过数据的总长为终止条件。--------[滑窗算法]
(3)嵌套循环
在这种情况下,函数的操作数量与 𝑛2 成正比,或者说算法运行时间和输入数据大小 𝑛 成“平方关系”。
我们可以继续添加嵌套循环,每一次嵌套都是一次“升维”,将会使时间复杂度提高至“立方关系”“四次方关系”,以此类推。
3.递归
递归(recursion) | |
递 | 归 |
程序不断深入地调用自身,通常传入更小或更简化的参数,直到达到“终止条件”。 | 触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 |
"""
求和函数的递归 n=3
(1)3+recur(2)
(2)3+2+recur(1)
(3)3+2+1=6
--------------
存储局部变量n
(1)3
(2)2+recur(1)
(3) 3+3=6
"""
def recur(n: int) -> int:
"""递归"""
# 终止条件
if n == 1:
return 1
# 递:递归调用
res = recur(n - 1)
# 归:返回结果
return n + res
迭代 | 递归 |
“自下而上”地解决问题。从最基础的步骤开始,然后不断重复或累加这些步骤,直到任务完成。 | 自上而下”地解决问题。将原问题分解为更小的子问题,这些子问题和原问题具有相同的形式。接下来将子问题继续分解为更小的子问题,直到基本情况时停止(基本情况的解是已知的)。 |
以上述求和函数为例,设问题 𝑓(𝑛)=1+2+⋯+𝑛 。 | |
在循环中模拟求和过程,从 1 遍历到 𝑛 ,每轮执行求和操作,即可求得 𝑓(𝑛) 。 | 将问题分解为子问题 𝑓(𝑛)=𝑛+𝑓(𝑛−1) ,不断(递归地)分解下去,直至基本情况 𝑓(1)=1 时终止。 |
(1)
递归函数每次调用自身时,系统都会为新开启的函数分配内存,以存储局部变量、调用地址和其他信息等。这将导致两方面的结果。
- 函数的上下文数据都存储在称为“栈帧空间”的内存区域中,直至函数返回后才会被释放。因此,递归通常比迭代更加耗费内存空间。
- 递归调用函数会产生额外的开销。因此递归通常比循环的时间效率更低。
(2)尾递归
如果函数在返回前的最后一步才进行递归调用,则该函数可以被编译器或解释器优化,使其在空间效率上与迭代相当。这种情况被称为尾递归(tail recursion)。
普通递归 | 尾递归 |
当函数返回到上一层级的函数后,需要继续执行代码,因此系统需要保存上一层调用的上下文。 | 递归调用是函数返回前的最后一个操作,这意味着函数返回到上一层级后,无须继续执行其他操作,因此系统无须保存上一层函数的上下文。 |
求和操作是在“归”的过程中执行的,每层返回后都要再执行一次求和操作。 | 求和操作是在“递”的过程中执行的,“归”的过程只需层层返回。 |
4.递归树
给定一个斐波那契数列 0,1,1,2,3,5,8,13,… ,求该数列的第 𝑛 个数字。
草稿:n=1 f(1)=0 n=2 f(2)=1 n=3 f(3)=1
考虑递归的话,遵循一个原则,就是自上而下,即将一个复杂的求解问题(多元问题)逐层拆解为多个简单的一元问题。【分治--分而治之】
对于该题来说,求解第n个数,即f(n)。之后是考虑到如何拆解的问题,这时候就要思考这组数列中所具有的关系式。从上述数列中发现(不能叫发现,叫看网页得到的知识,没有自己推理):每个数字等于前两个数字之后,从第三个数字开始,前两个数字固定不变。
哦!华生发现了盲点:前两个数字固定不变,那就将他们作为终止条件,即所能划分到的最小单元组(固定值)。推导斐波那契数列的公式为f(n)=f(n-1)+f(n-2)
迭代 | 递归 | |
实现方式 | 循环结构 | 函数调用自身 |
时间效率 | 效率通常较高,无函数调用开销 | 每次函数调用都会产生开销 |
内存使用 | 通常使用固定大小的内存空间 | 累积函数调用可能使用大量的栈帧空间 |
适用问题 | 适用于简单循环任务,代码直观、可读性好 | 适用于子问题分解,如树、图、分治、回溯等,代码结构简洁、清晰 |