算法分析(python)

1.什么是算法分析?

如何对比两个程序?

  • 好的程序变量名清晰,无用垃圾代码少。
  • 代码风格,可读性

程序和算法的区别

  • 算法是对问题解决的分步描述
  • 程序则是采用某种编程语言实现的算法

算法分析的概念

  • 算法分析主要就是从计算资源消耗的角度来评判和比较算法。更高效利用计算资源,或者更少占用计算资源的算法,就是好算法。

那么何为计算资源?

  • 一种是算法解决问题过程中需要的存储空间或内存
  • 另一种是算法的执行时间

运行时间检测

  • 获取打印111所需时间
import time
start = time.time()
print(111)
end = time.time()
running_time = end-start
print('time cost : %.5f sec' %running_time)

运行结果

111
time cost : 0.00000 sec
  • 显示时间是0.00000秒,是因为打印111所需要的时间太短了,所以不是很精确。

如何比较算法的“好坏”?

  • 衡量算法可从时间空间两个角度分析

有两个不同的算法,都可以计算前n项数的和。

①迭代算法

import time
def sumOfN2(n) :
    start = time. time( )
    theSum = 0
    for i in range(1,n+1):
        theSum = theSum + i
        end = time. time( )
        return theSum, end-start
print("Sum is %d required %10.7f seconds"% sumOfN2 (10000) )
sum is 50005000 requires 0.0007980 seconds

②无迭代算法

import time
def sumOfN3(n) :
    start = time. time( )
    theSum=(n*(n+- 1))/2
    end =time. time( )
    return theSum ,end-start
sum is 50005000 requires 0.0000010 seconds

第二种算法所需时间更短

观察一下第一种迭代算法

  • 包含了一个循环,可能会执行更多语句 这个循环运行次数跟累加值n有关系,n增加,循环次数也增加,执行时间增加
import time
def sumOfN2(n) :
    theSum = 0
    for i in range(1,n+1):#就是这条循环语句!!
        theSum = theSum + i
        return theSum

2.大O表示法

赋值语句 是作为算法时间的度量指标

  • 赋值语句执行少,则程序运行时间短。

如何来看赋值语句执行的次数呢?

  • 在这个程序中,我们用T(n)来表示赋值语句的条数
def sumOfN2(n) :
    theSum = 0	#一条赋值语句,T(n)1=1
    for i in range(1,n+1):#一条循环语句,循环了n次T(n)2=n 
        theSum = theSum +i#一条赋值语句,T(n)2=n*1
        return theSum

所以对于"问题规模"n,赋值语句数量
T(n)=T(n)1+T(n)2=n+1

什么是问题规模

  • 问题规模就是影响算法执行时间最主要的因素。
  • 所以对于上面那个程序,前1000个整数求和,对比前100个整数求和,算是较大的问题规模,所以前n个整数累计求和,n就是问题规模了

算法分析目标?

  • 算法分析的目标是找出问题规模会怎样影响算法执行时间

数量级函数

  • 数量级函数就是用来描述T(n)中随着n增加增加速度最快的主导部分,称作“大O”表示法,记作O(f(n)),其中f(n)表示T(n)中的主导部分

主导部分怎么确定呢?就是T(n)中增长速度最快的部分
T(n)=n+1,主导部分是n
T(n)=200n3+20n+1000,主导部分是n3

影响算法运行时间的因素不只有问题规模,还有其他。

常见的大O数量级函数

f(n)名称
1常数
log(n)对数
n线性
n*log(n)对数线性
n2平方
n3立方
2n指数

从代码分析执行时间数量级函数

a=5
b=6
c = 10  #3条赋值语句,所以T(n)1=3
for i in range(n):  #循环语句n
    for j in range(n):  #循环语句n,所以T(n)2=n的平方
        X=i*i
        Y=j*j
        Z=i*1	#三条赋值语句,所以T(n)2=3n的平方
for k in range(n):#循环语句n,T(n)3=n
    W =a*k+45
    V=b*b	#两条赋值语句,所以T(n)3=2n
d=33	#1条赋值语句,所以T(n)4=1

所以,T(n)=T(n)1+T(n)2+T(n)3+T(n)4=3+3n2+2n+1=3n2+2n+4

其它算法复杂度表示法

  • 大O表示法
    表示了所有上限中最小的那个,上限。
  • 大Ω表示法
    表示了所有下限中最大的那个下限
    f(n) = Ω(g(n))当且仅当g(n) = o(f(n))
  • 大θ表示法
    如果上下限相同,那么就可以用大θ表示 f(n) =θ(g(n)) 当且仅当f(n) = O(g(n))且f(n) = Ω(g(n))

3.变位词的判断问题

  • 所谓“变位词”是指两个词之间存在组成字母的重新排列关系。如:heart和earth,python和typhon
  • 可以很好展示同一问题的不同数量级算法

写一个bool函数,以两个词作 为参数,返回这两个词是否变位词

解法1:逐字检查

  • 思路:将词1中的字符逐个到词2中检查是否存在,存在的字符“打勾”标记,如果每个字符都能找到,则两个词是变位词 。
    在这里插入图片描述
  • 将词2对应“打勾”的字符设为None。由于字符串是不可变类型,需要先复制到列表中:
    在这里插入图片描述
def anagramSolution(s1,s2):
	alist = list(s2) #复制s2到列表
	stillOK = True
	pos1 = 0
	while pos1<len(s1) and stillOK:#循环s1每个字符
		pos2 = 0
		found = False
		while pos2<len(alist) and not found:
			if s1[pos1]==alist[pos2]:#在s2逐个对比
				found=True
			else:
				pos2 = pos2 + 1
		if found:
			alist[pos2] = None#找到,打勾
		else:
			stillOK = False#未找到,失败
		pos1 = pos1 + 1
	return stillOK
print(anagramSolution("abcd","dcba"))
  • 问题规模:词中包含的字符个数n
  • 主要部分在于两重循环:
    外层循环遍历s1每个字符,将内层循环执行n次
    而内层循环在s2中查找字符,每个字符的对比次数,分别是1、2…n中的一个,而且各不相同
  • 所以总执行次数是1+2+3+…+n,可知数量级O(n2)
    在这里插入图片描述

解法2:排序比较

  • 思路:将两个字符串都按照字母顺序排好序,再逐个字符对比是否相同
    在这里插入图片描述
def anagramSolution2(s1,s2):
	alist1 = list(s1)
	alist2 = list(s2)	
	alist1.sort()
	alist2.sort()
	pos = 0
	matches = True
	while pos<len(s1) and matches:
		if alist1[pos]==alist2[pos]:
			pos = pos + 1
		else
			matches = False
	return matches
  • 粗看上去,本算法只有一个循环,最多执 行n次,数量级是O(n)
    但循环前面的两个sort并不是无代价的,如果查询下后面的章节,会发现排序算法采用不同的解决方案,其运行时间数量级差不多是 O ( n2 ) 或者O(n log n),大过循环的O(n).
  • 所以本算法时间主导的步骤是排序步骤
  • 本算法的运行时间数量级就等于排序过程的数量级O(n log n)

解法3:暴力法

  • 暴力法解题思路为:穷尽所有可能组合
  • 将s1中出现的字符进行全排列,再查看s2是否出现在全排列列表中
  • n个字符全排列,是n!个字符,例如,对于20个字符长的词来说,将产生 20!=2,432,902,008,176,640,000个候选词,如果每微秒处理1个候选词的话,需要近8万年时间来做完所有的匹配。所以我们就不用这个算法了,它不是个好算法。

解法4.计数比较

  • 若两个字符中每个字母出现次数相同,则为变位词
  • 具体做法:为每个词设置一个26位的计数 器,先检查每个词,在计数器中设定好每 个字母出现的次数,计数完成后,进入比较阶段,看两个字符 串的计数器是否相同,如果相同则输出是 变位词的结论
def anagramSolution4(s1,s2):
	c1 = [0]*26    #长度为26 全0的列表 代表每个字母出现的次数
	c2 = [0]*26
	for i in range(len(s1):
		pos = ord(s1[i])-ord('a') #ord返回字符的unicode编码 字母的编码是连续的
		c1[[pos] = c1[pos] + 1
	for i in range(len(s2):
		pos = ord(s2[i])-ord('a') #ord返回字符的unicode编码 字母的编码是连续的
		c2[[pos] = c2[pos] + 1
	j = 0
	stillOK = True
	while j<26 and stillOK:#计数器比较
		if(c1[j]==c2[j]):
			j = j + 1
		else:
			stillOK = False
	return stillOK
  • 前两个循环用于对字符串进行计数,操作次数等 于字符串长度n;第3个循环用于计数器比较,操作次数总是26次.
    所以总操作次数T(n)=2n+26,其数量级为O(n)
    这是一个线性数量级的算法,是4个变位词判断算法中性能最优的
  • 本算法依赖于两个长度为26的计数器列表,来保存字符计数,这相比前3个算法需要更多的存储空间.(牺牲空间换时间)

4. Python数据类型的性能

  • list和dict的常见操作

在这里插入图片描述

  • list基本操作的大O数量级
    在这里插入图片描述
  • dict数据类型大O数量级
    在这里插入图片描述
  • 字典的执行时间与规模无关,是常数O(1)
    而列表的执行时间则随着列表的规模加大,呈线性上升 O(n)
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铃音.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值