一、算法的导入
1.1 算法的概念
算法是解决问题的一种思想或方法
如果 a+b+c=1000,且 a ** 2+b ** 2=c ** 2(a,b,c 为自然数),如何求出所有a、b、c可能的组合?
第一种方式
import time
start_time = time.time()
for a in range(0,1001):
for b in range(0,1001):
for c in range(0,1001):
if (a + b + c == 1000 and a ** 2 + b ** 2 == c ** 2):
print('a,b,c: %d,%d,%d' %(a,b,c))
end_time = time.time()
tot_time = end_time - start_time
print(tot_time)
解决以上问题的方法就是一个算法(枚举法)。
1.2 算法的五大特性
1.输入:算法至少有0个或1个输入
2.输出:算法至少有1个或多个输出
3.有穷性:算法会在一定的步骤内结束而不会无限循环
4.确定性:算法中每一步都有确定的含义,不存在二义性
5.可行性:算法中的每一步都可以执行,也就是说算法的每一步都可以在有限的循环内完成
二、衡量算法的效率
第二种方式:
import time
start_time = time.time()
for a in range(0,1001):
for b in range(0,1001-a):
c = 1000 - a - b
if (a + b + c == 1000 and a ** 2 + b ** 2 == c ** 2):
print('a,b,c: %d,%d,%d' %(a,b,c))
end_time = time.time()
tot_time = end_time - start_time
print(tot_time)
第一种方式所用的时间远大于第二种方式,我们可以仅仅依靠算法执行的时间来衡量算法的优劣吗?
这种方式是不准确的,因为算法执行的时间还受到测试环境(电脑性能高低)和数据规模的影响。但在算法执行过程中,算法执行的基本运算步数是固定的,因此我们可以根据算法执行 步骤的多少来粗略衡量算法的优劣
2.1 大O时间复杂度表示法
T(n) = O(f(n))
T(n) 算法执行的总时间
n 数据规模
f(n) 基本运算的次数
O 表示T(n)与f(n)成正比,即随着f(n)的增加,T(n)也在不断增加
用大O表示法来分析第一种算法:
第一种算法的总执行步数: 1000*1000*1000*2
可抽象为 n^3 *2
T(n) = O(n^3 *2)
当n很大的时候,公式中的常量、地阶或系数三部分并不左右增长趋势,因此只需要考虑最大量级即可。
第二种方式的大O时间复杂度为O(n^2)
2.2 时间复杂度分析的方式
1.只关注循环次数最多的一段代码
def cal(n):
#无论n等于几,以下两行代码只执行一次,是常数阶,因此这两行代码对时间复杂度没有影响
sum = 0
i = 1
当n = 4时,for循环执行了4次,因此这段代码的时间复杂度为O(n)
for i in range(n):
sum += i
i += 1
return sum
print(cal(4))
以上代码的时间复杂度为O(n),即循环次数最多的一段代码的复杂度
2.加法规则:总复杂度等于量级最高的那段代码的复杂度
def cal(n):
sum = 0
i = 1
#无论n等于几,以下for循环都会循环100次,其时间复杂度为O(1),是常数阶
for i in range(100):
sum += i
#当n =4时,以下for循环会循环5次,f(n) = n +1 ,不考虑常数项,其时间复杂度为O(n)
for i in range(n+1):
sum += i
i+=1
#当 n = 4时,以下for循环会循环5次,ff(n) = n^2 +2n +1,不考虑低阶和常数项,其时间复杂度为O(n^2)
for i in range(n+1):
for j in range(n+1):
pass
print(cal(4))
该段代码的总时间复杂度为O(1) + O(n) + O(n^2),只考虑最大量级的一段代码的复杂度,则该代买的时间复杂度为T(n) = O(n ^ 2)
2.3 最坏时间复杂度
算法完成工作最少需要多少基本操作,即最优时间复杂度
算法完成工作最多需要多少基本操作,即最坏时间复杂度
算法完成工作平均需要多少基本操作,即平均时间复杂度
例如:查找列表中的某一个元素,最优的时间复杂度是O(1),即列表元素位于列表的第一位
查找列表中的某一个元素,最坏的时间复杂度是O(n),即列表元素位于列表的最后一位
我们在考虑一个算法的时间复杂度时,一般只考虑最坏时间复杂度。
2.4 常见的时间复杂度
常数阶 | O(1) |
---|---|
对数阶 | O(logn) |
线性阶 | O(n) |
nlogn阶 | O(nlogn) |
平方阶 | O(n^2) |
指数阶 | O(a ^n) |
三、Python内置类型性能分析
我们可以用Python自带的timeit模块中的Timer类来测量一个代码的执行时间
class timeit.Timer(stmt=‘pass’, setup=‘pass’, timer=)
stmt 要测试的代码语句
setup 是运行代码时需要的设置
timer 一个定时器函数,与平台有关
用不同的方式创建一个非空列表,各个方式的性能测试
from timeit import Timer
def test1():
l = []
for i in range(1000):
l = l + [i]
def test2():
l = []
for i in range(1000):
l.append(i)
def test3():
l = []
for i in range(1000):
l.extend([i])
def test4():
l = [i for i in range(1000)]
def test5():
l = []
for i in range(1000):
l.insert(0,i)
def test6():
l = list(range(1000))
def test7():
l = []
for i in range(1000):
l += [i]
t1 = Timer('test1()','from __main__ import test1')
print('add执行时间:',t1.timeit(number=1000))
t2 = Timer('test2()','from __main__ import test2')
print('append执行时间:',t2.timeit(number=1000))
t3 = Timer('test3()','from __main__ import test3')
print('extend执行时间:',t3.timeit(number=1000))
t4 = Timer('test4()','from __main__ import test4')
print('推导式执行时间:',t4.timeit(number=1000))
t5 = Timer('test5()','from __main__ import test5')
print('insert执行时间:',t5.timeit(number=1000))
t6 = Timer('test6()','from __main__ import test6')
print('list执行时间:',t6.timeit(number=1000))
t7 = Timer('test7()','from __main__ import test7')
print('+=执行时间:',t7.timeit(number=1000))
输出结果:
add执行时间: 2.9384223547167485
append执行时间: 0.2181493807683248
extend执行时间: 0.3046907205620495
推导式执行时间: 0.10262465739990567
insert执行时间: 1.1072418832214757
list执行时间: 0.03898072088704563
+=执行时间: 0.25340575995785297
用不同方式删除一个字典中值的性能
from timeit import Timer
def te1():
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7}
del(dict1['c'])
def te2():
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7}
dict1.popitem()
def te3():
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7}
dict1.pop('c')
t2 = Timer('te2()','from __main__ import te2')
print('popitem执行时间:',t2.timeit(number=1000))
t3 = Timer('te3()','from __main__ import te3')
print('pop执行时间:',t3.timeit(number=1000))
输出结果为:
popitem执行时间: 0.0009920934711697066
pop执行时间: 0.0011038465288416947
del执行时间: 0.0009225328944555099
四、数据结构导入
我们如何用Python中的类型来保存一个班的学生信息?
stu = [('小明','男','17'),('小李','女','17'),('小张','男','18')]
stu2 = {"小明":{"gender":"男","age":17},"小李":{"gender":"男","age":17},"小张":{"gender":"男","age":18}}
stu3 = [{"name":"小明","gender":"男","age":17},{"name":"小李","gender":"男","age":17},{"name":"小张","gender":"男","age":18}]
数据保存的方式或数据组合的方式其实就是数据结构。
算法是为了解决实际的问题,而数据结构是为了解决数据的问题。
程序 = 算法 +数据结构