It takes 0.08275080 seconds to calculate the 10000th item of Fibonacci sequence
It takes 0.08277822 seconds to calculate the 10000th item of Fibonacci sequence
It takes 0.08176851 seconds to calculate the 10000th item of Fibonacci sequence
It takes 0.08178067 seconds to calculate the 10000th item of Fibonacci sequence
It takes 0.08081150 seconds to calculate the 10000th item of Fibonacci sequence
但是这种方法计算的是执行算法的实际时间,有两个明显的缺陷:1) 必须先运行依据算法编制的程序;2) 依赖于特定的计算机、编译器与编程语言等软硬件环境,容易掩盖算法本身的优劣。因此,我们希望找到一个独立于程序或计算机的指标,以用来比较不同实现下的算法。
2.1 大 O O O 表示法
为了摆脱与计算机硬件、软件有关的因素,我们需要一种事前分析估算的方法。可以认为特定算法的“运行工作量”大小取决于问题的规模,或者说,它是问题规模的函数,这时我们就需要量化算法的操作或步骤。一个算法是由控制结构和基本操作构成的,因此可以将算法的执行时间描述成解决问题所需重复执行的基本操作数。需要注意的是,确定合适的基本操作取决于不同的算法。例如在计算斐波那契数列第 n 项时,赋值语句就是一个基本操作,而在计算矩阵乘法时,乘法运算则是其基本操作。
在上一节的 fibo
函数中,整个算法的执行时间与基本操作(赋值)重复执行的次数n 成正比,具体而言是 1 加上 n-2 个赋值语句,如果使用将其定义为函数可以表示为 T ( n ) = n − 1 T(n)=n-1 T(n)=n−1,其中 n n n 为大于 2 的正整数。 n n n 常用于表示问题规模,我们可以使用问题规模 n n n 的某个函数 f ( n ) f(n) f(n) 表示算法中基本操作重复执行的次数,算法的时间量度可以表示如下:
T ( n ) = O ( f ( n ) ) T(n)=O(f(n)) T(n)=O(f(n))
随问题规模 n n n 的增大, T ( n ) T(n) T(n) 函数的某一部分会比其余部分增长得更快,算法间进行比较时这一起部分起决定性作用, T ( n ) T(n) T(n) 增长最快的部分也称为数量级函数。算法执行时间的增长率和 f ( n ) f(n) f(n) 的增长率相同,称作算法的渐近时间复杂度 (asymptotic time complexity),简称时间复杂度。数量级 (order of magnitude) 常被称作大 O O O 记法或大 O O O 表示法。
通过以上分析,我们可以将算法的渐近复杂度规则描述如下:
-
如果运行时间是一个多项式的和,那么仅保留增长速度最快的项,去掉其他各项;
-
如果剩下的项是个乘积,那么去掉所有常数。
假设某一算法的基本步骤数为 T ( n ) = 3 n 2 + 50 n + 2000 T(n)=3n^2+50n+2000 T(n)=3n2+50n+2000,当 n n n 很小时 2000 对于函数的影响最大,但是随着 n n n 的增长 n 2 n^2 n2 将逐渐变得更重要,以至于可以忽略其他两项以及 n 2 n^2 n2 的系数 3,因此可以说 T ( n ) T(n) T(n) 的数量级是 n 2 n^2 n2 或写为 O ( n 2 ) O(n^2) O(n2)。
算法的性能有时不仅依赖问题的规模,还取决于算法的输入值,输入令算法运行最慢的情况称为最坏情况,输入令算法运行最快的情况称为最好情况,随机输入的情况下算法的性能介于两种极端情况之间,称为平均情况。
2.2 常见算法复杂度
下表列出了一些常见的大 O O O 表示法实例:
| 复杂度 | 解释 | 示例 |
| — | — | — |
| O ( 1 ) O(1) O(1) | 常数复杂度 | 100, 500, 1, 30, … |
| O ( l o g n ) O(logn) O(logn) | 对数复杂度 | l o g 2 n log_2n log2n, l o g 10 n log_{10}n log10n, 2 l o g 2 n 2log_2n 2log2n, … |
| O ( n ) O(n) O(n) | 线性复杂度 | 8 n + 10 8n+10 8n+10, n n n, 100 n 100n 100n, … |
| O ( n l o g n ) O(nlogn) O(nlogn) | 对数线性复杂度 | 10 n l o g n + 50 10nlogn+50 10nlogn+50, 5 n l o g n + 30 n 5nlogn+30n 5nlogn+30n, … |
| O ( n k ) O(n^k) O(nk) | 多项式复杂度,其中 k k k 为常数 | 4 n 2 − 10 n 4n^2-10n 4n2−10n, 2 n 3 + 10 n 2 2n3+10n2 2n3+10n2, 4 n 2 + 5 n l o g n 4n^2+5nlogn 4n2+5nlogn, … |
| O ( c n ) O(c^n) O(cn) | 指数复杂度,其中 c c c 为常数 | 2 n + 5 n 2 2n+5n2 2n+5n2, 4 n + 10 n l o g n 4^n+10nlogn 4n+10nlogn, … |
常见算法复杂度的增长率比较如下:
2.2.1 常数复杂度
常数复杂度表示,算法的渐进复杂度域输入的规模无关,例如求列表的长度等都属于常数复杂度。常数复杂度和代码中是否包含循环没有必然关系,例如循环打印 100 次 “Hello world”,这与输入规模并没有什么关系,因此其也是属于常数复杂度。
2.2.2 对数复杂度
对数复杂度表示函数的增长速度至少是输入规模的对数,当我们谈论对数复杂度时,我们并不关系对数的底数,这是由于可以使用换底公式,将原来底数的对数乘以一个常数转换为另一个底数:
l o g a n = l o g a b ∗ l o g b n log_an=log_ab*log_bn logan=logab∗logbn
其中, a a a 和 b b b 均为常数。例如以下代码,将一个正整数转换为字符串:
def int_to_str(num):
digits = “0123456789”
result = ‘’
if num == 0:
result = ‘0’
else:
while num > 0:
result = digits[num % 10] + result
num = num // 10
return result
上述代码中只包括一个循环,且没有调用其它函数,因此我们只需找出循环迭代次数——在 num 为 0 之前所需的整数除法的次数 l o g 10 n log_{10}n log10n。因此函数 int_to_str 的复杂度是 O ( l o g n ) O(logn) O(logn)。
2.2.3 线性复杂度
线性复杂度在列表中等序列数据类型总十分常见,因为算法通常需要遍历处理序列中的每一个元素。例如将列表中的每个元素加上常数 10:
def add_constant(list_o):
for i in range(len(list_o)):
list_o[i] += 10
这个函数的复杂度就与列表的长度成线性关系,也就是 O ( n ) O(n) O(n)。
2.2.4 线性对数复杂度
线性对数复杂度是两项的乘积,每个项都依赖于输入的规模,例如将列表中每一项正整数转换为字符串。很多实用算法的复杂度都是对数线性的。
2.2.5 多项式复杂度
多项式复杂度的增长速度是输入规模的 k k k 次幂,其中最常见的是平方复杂度,例如求列表 list_a 和 list_b 的交集:
def intersect(list_a, list_b):
第一部分
temp = []
for i in list_a:
for j in list_b:
if i == j:
temp.append(i)
break
第二部分
result = []
for i in temp:
if i not in result:
result.append(i)
return result
intersect 函数第一部分的复杂度显然是 O ( l e n ( l i s t _ a ) ) ∗ O ( l e n ( l i s t _ b ) ) O(len(list\_a))*O(len(list\_b)) O(len(list_a))∗O(len(list_b)),第二部分代码用于去除第一部分得到结果列表中的重复元素,虽然其中仅包含一个循环语句,但是测试条件 if i not in result
需要检查 result 中的每个元素,因此第二部分的复杂度为 O ( l e n ( t e m p ) ) ∗ O ( l e n ( r e s u l t ) ) O(len(temp))*O(len(result)) O(len(temp))∗O(len(result)),tmp 和 result 的长度取决于 list_a 和 list_b 中长度较小的那个,根据渐进复杂度规则可以将其忽略。最终,intersect 函数的复杂度就是 O ( n 2 ) O(n^2) O(n2)。
2.2.6 指数复杂度
指数复杂度算法的解决时间随输入规模的指数增长。在以下示例中,由于 1 左移 num 位得到 end,因此 end 实际上等于 2 n u m 2^{num} 2num,因此循环中计算了 2 n u m 2^{num} 2num 次加法,时间复杂度为 O ( 2 n ) O(2^{n}) O(2n)。
def calculate(num):
result = 0
end = 1 << num
for i in range(end):
result += i
return result
2.3 复杂度对比
为了直观的观察到各种复杂度的增长情况,使用统计图来对比各种复杂度算法的运行时间增长速度。
从上图可以看出,对数复杂度随问题规模的增长,运行时间的增长很小,几乎和常数复杂度算法一样优秀,通常只有当输入规模很大时才能直观的看出两者之间的差别,而线性复杂度和对数复杂度的区别在输入规模很小时就非常明显了。
虽然 O ( l o g n ) O(logn) O(logn) 的增长速度很慢,但是在线性乘法因子的加持下,其增长速率高于线性复杂度,但与平方复杂度的增长速度相比,就不值一提了,因此在实际情况下,具有 O ( n l o g n ) O(nlogn) O(nlogn) 复杂度的算法执行速度还是很快的。而指数复杂度除了对那些规模特别小的输入,其运行时间都是不现实的,即使立方复杂度和其相比都相形见绌。
在以上内容中讨论的都是代码的时间复杂度。这是由于,与时间复杂度相比,要想感觉到空间复杂度 (space complexity) 的影响比较困难。对于用户来说,程序运行完成需要 1 分钟还是 10 分钟是明显能够感觉到的,但程序使用的内存是 1 兆字节还是 10 兆字节则无法直观觉察。这也就是时间复杂度通常比空间复杂度更受关注的原因。通常只有当运行程序所需的存储空间超过了计算机内存时,空间复杂度才会受到关注。
类似于算法的时间复杂度,空间复杂度作为算法所需存储空间的量度,可以表示为:
S ( n ) = O ( f ( n ) ) S(n)=O(f(n)) S(n)=O(f(n))
一个程序的执行除了需要存储空间来寄存本身所用指令、常数变量和输入数据外,也需要一些辅助空间用于存储数据处理的中间数据。若输入数据所占空间只取决于问题本身,和算法无关,则只需要分析除输入和程序之外的额外空间,否则应同时考虑输入本身所需空间。若额外空间相对于输入数据量来说是常数,则称此算法为原地工作。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Python开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注Python)
一、Python所有方向的学习路线
Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
二、学习软件
工欲善其事必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。
三、入门学习视频
我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
center)
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算