引入
例1 如果a+b+c=1000,且a^2+b^2=c^2(a、b、c为自然数),如何求出a,b,c所有的可能性?
直觉:采用枚举法
思路:
a=0
b=0
c=0~1000
import time
start=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()
print ("time:%d"%(end-start))
运行结果如下:
a,b,c:0,500,500
a,b,c:200,375,425
a,b,c:375,200,425
a,b,c:500,0,500
time:97
注:Ubuntu的CPU使用率命令:top
算法的提出
算法的概念
算法是计算机处理信息的本质,算法是独立存在的一种解决问题的方法和思想。
(实现的语言不重要,重要的是思想)
参考书:数据结构与算法Python语言描述 裘宗燕著
算法的五大特性
1.输入
2.输出
3.有穷性(有限的步骤&有限的时间)
4.确定性(每一步都有确切的含义)
5.可行性 (每一步执行有限的次数可以结束)
时间复杂度与大O表示法
还是例1。
考虑c是由ab确定好的,因此使c=1000-a-b,可以去掉a+b+c=1000的判断条件。程序设计如下:
import time
start=time.time()
for a in range(0,1001):
for b in range(0,1001):
c=1000-a-b
#for c in range(0,1001):
if a**2+b**2==c**2:
print ("a,b,c:%d,%d,%d"%(a,b,c))
end=time.time()
print ("time:%d"%(end-start))
运行结果:
a,b,c:0,500,500
a,b,c:200,375,425
a,b,c:375,200,425
a,b,c:500,0,500
time:0
最终执行时间不到1s。
可以发现改变算法后代码运行的时间大大减小,说明算法之间有优劣。
但是只用时间来衡量可信吗?
比如运行在古老的机器上的时候,计算量大的时候计算时间会增多,为了评判算法的优劣,因此引入时间复杂度概念
。
一段同样的程序在两台不同的机器上运行时间可能有差别,但是执行基本运算数量
大体相同,比如老机器一步需要几毫秒,而新的机器一步只要几微秒。通过计算运算步骤数量
衡量算法优劣,因此引入时间复杂度。
假设一个算法的规模是n,例如a+b+c=1000,n=1000就是上面算法的规模,完成这个算法的步骤T(n)=n*n*3(假设循环里面一行代码就是一个步骤)那么T(n)=n^2*3就是时间复杂度
。
如果底下的代码不是3行,是10行。则时间复杂度为T(n)=n^2*10。
为了方便描述,用大O记法
,类似与高数里面的高阶无穷小o(n),也许就叫渐进函数吧,去掉系数与常数项,保留特征项n^2,因此上面算法的时间复杂度为O(n^2)。
最坏时间复杂度与计算规则
最坏时间复杂度
算法完成工作最少需要多少基本操作,即最优时间复杂度,反之最坏时间复杂度
最优时间复杂度只是最乐观理想情况,没有参考价值。
因此最坏时间复杂度提供一种保证,表明算法在此程度的基本操作中一定完成任务。
时间复杂度的计算规则
- 基本操作,即只有常数项,算法复杂度为O(1)
- 顺序,条件,循环可以代表所有语言的基本步骤。
顺序结构
的算法复杂度按加法
进行计算,循环结构
按乘法
计算,分支结构
取算法复杂度的最大值
(例如两条分支的算法复杂度不同,考虑最坏的情况,因此取算法复杂度的最大值) - 只关注最高次项
- 没有特殊说明,我们分析的就是算法的
最坏算法复杂度
。
常见的时间复杂度
代码执行时间测量模块timeit
衡量函数调用,函数中封装了非常多的基本步骤,因此python一行代码有可能不是一个基本步骤。
引入python内置类型性能分析:timeit模块
class timeit.Timer(stmt='pass',setup='pass',time=<timer function>)
Timer是测量一小段代码执行速度的类。
stmt是要测量的代码语句(statement),一般是将函数名换成字符串。
setup参数是运行代码时需要的设置,一般指需要导入的包操作等,如果是从当前文件中导入XXX函数则需要写from __main__ import XXX
。
timer参数是一个定时器函数,一般不用管。
timeit.Timer.timeit(number =1000000)
Timer.timeit是Timer类中测试语句执行速度的对象方法,number参数是测试代码的测试次数,默认100W次,返回float类型的秒数,即执行代码的平均耗时。
Python列表类型不同操作的时间效率
对于列表:
import timeit
li1=[1.2]
li2=[23,5]
li=li1+li2
li=[li for i in range(10000)]
li=list(range(10000))
#python2与python3中range()的区别:python2中的range为一个列表对象,而
#python3中的range是一个可迭代对象,而python2中想要可迭代对象,就要使用
#xrange
def test1():
li=[]
for i in range(10000):
li.append(i)
def test2():
li=[]
for i in range(10000):
li+=[i]