算法分析

     1.首先我们通过 ‘计算前n个整数的和’ 的两个不同算法,来体验下同种问题下不同算法之间的差异。

  • 计算前n个整数的和,这里使用初始化值为0的累加器变量来迭代n个整数。(一个简单的累加函数)
def sumOfN(n):theSum=0for   i in range(1,n+1):theSum=theSum   +ireturn theSum
print(sumOfN(10))

在Python中,我们可以通过记录相对于系统的开始时间和结束时间来对函数进行基准测试。在time模块中有一个time函数,它可以在任意被调用的地方返回系统时钟的当前时间(以秒为单位)。通过在开始和结束的时候分别调用两次time函数,然后计算差异,就可以得到一个函数执行花费的精确秒数(大多数情况下是这样)。

import time
def sum(n):start=time.time()theSum=0for i in range(1,n+1):theSum=theSum+iend =time.time()return theSum,end-start
for i in range(5):print('总和:%d ;   用时:%10.7f seconds'%sum(10000))

我们执行这个函数5次,每次计算前10000个整数的和将得到如下结果(不同计算机运行的结果不同):

总和:50005000 ;   用时: 0.0009999 seconds
总和:50005000 ;   用时: 0.0009999 seconds
总和:50005000 ;   用时: 0.0009999 seconds
总和:50005000 ;   用时: 0.0009999 seconds
总和:50005000 ;   用时: 0.0010002 seconds

我们发现时间是相当一致的,执行这段代码平均需要0.0009秒。如果我们运行计算前 100,000 个整数的和的函数呢?

总和:5000050000 ;   用时: 0.0079999 seconds
总和:5000050000 ;   用时: 0.0080001 seconds
总和:5000050000 ;   用时: 0.0079999 seconds
总和:5000050000 ;   用时: 0.0079999 seconds
总和:5000050000 ;   用时: 0.0080001 seconds

得到的结果是,运行的时间更长了,但是每次运行所需的时间也是非常一致的。(可自行测试下n=1000000的情况)

  • 这里我们使用封闭方程来计算前n个整数的和,来体验下与上面算法的差异。

封闭方程如下图:

代码实现:

import time
def sum(n):start=time.time()m=(n * (n + 1)) / 2end =time.time()return m,end-start
for i in range(5):print('总和:%d ;   用时:%10.7f seconds'%sum(10000000000))

如果我们对 sum函数做同样的基准测试,使用 5个不同的n(10,000, 100,000, 1,000,000,10,000,000和 100,000,000),我们得到如下结果:

总和:50005000 ;   用时: 0.0000000 seconds
总和:5000050000 ;   用时: 0.0000000 seconds
总和:500000500000 ;   用时: 0.0000000 seconds
总和:50000005000000 ;   用时: 0.0000000 seconds

在这个输出中有两件事需要重点关注,首先上面记录的执行时间比之前任何例子都短,另外,他们的执行时间和n无关,看起来sum函数几乎不受n的影响。

  • 这个基准测试能告诉我们,使用迭代的解决方案需要做更多的工作,因为一些程序步骤被重复执行。这可能是它需要更长时间的原因。此外,迭代方案执行所需时间随着n递增。

2.大O符号

对于先前的求和算法,一个比较好的基本计算单位是对执行语句进行计数。在sum中,赋值语句的计数为1(Sum = 0加上n的值(我们执行Sum = Sum + i的次数)。我们通过函数T表示T(n) = 1+ n。参数n通常称为“问题 的规模”,我们称作“T(n)是解决问题大小为n所花费的时间,即1+n步长”。在上面的求和函数中,使用n来表示问题大小是有意义的。我们可以说,100,000个整数和比1000个问题规模大。因此,所需时间也更长。我们的目标是表示出算法的执行时间是如何相对问题规模大小而改变的。当问题规模变大时,T(n)函数某些部分的分量会超过其他部分。函数的数量级表示了随着n的值增加而增加最快的那些部分。数量级通常称为大O符号,写为O(f(n))。它表示对计算中的实际步数的近似。函数f(n)提供了T(n)最主要部分的表示方法。在上述示例中,T(n) = 1+ n。当n变大时,常数1对于最终结果变得越来越不重要。如果我们找的是T(n)的近似值,我们可以删除1,运行时间是O(n)。要注意,1对于T(n)肯定是重要的。但是当n变大时,如果没有它,我们的近似也是准确的。

另外一个示例,假设对于一些算法,确定的步数是T(n) = 5n2+27n+1005。当n很小时,例如1或2,常数 1005似乎是函数的主要部分。然而,随着n变大,n这项变得越来越重要。事实上,当n真的很大时,其他两项在它们确定最终结果中所起的作用变得不重要。当n变大时,为了近似T(n),我们可以忽略其他项,只关注5n2。系数5也变得不重要。我们说,T(n)具有的数量级为f(n) = n2,或者O(n2)。

下面用图来说明n的变化

Figure1表示了Table1中的函数图。注意,当n很小时,函数彼此间不能很好的定义。很难判断哪个是主导的。随着n 的增长,就有一个很明确的关系,很容易看出它们之间的大小关系。

最后一个例子,假设我们有哦如下代码段。虽然这个程序没有做任何事,但是对我们获取实际的代码和性能分析是有益的。

a=5
b=6
c=10
for i in range(n):for j in range(n):x=i*iy=j*jz=i*j
for k in range(n):w=a*k+45v=b*b
d=33

分配操作数,分为四个项的总和。第一个项是常数3,表示片段开始的三个赋值语句。第二项是3n2,因为由于嵌套迭代,有三个语句执行n次。第三项是2n,两个语句迭代n次。最后,第四项是常数1,表示最终赋值语句。最后得出T(n) = 3+3n2+2n +1 = 3n2+2n +4,通过查看指数,我们可以看到n项是显性的,因此这个代码段是O(n2)。当n增大时,所有其他项以及主项上的系数都可以忽略。

3.一个乱序字符串检查的例子:

显示不同量级的算法的一个很好的例子是字符串的乱序检查。乱序字符串是指一个字符串只是另一个字符串的重新排列。例如,'heart'和'earth'就是乱序字符串。'python'和'typhon'也是。为了简单起见,我们假设所讨论的两个字符串具有相等的长度,并且他们由 26个小写字母集合组成。

  • 解法一:检查
def anagramSolution1(s1,s2):alist = list(s2)pos1 = 0stillOK = True
#正序和长度不等的情况if s1==s2 or len(s1)!=len(s2):return Falseelse:while pos1 < len(s1) and stillOK:pos2 = 0found = Falsewhile pos2 < len(alist) and not found:if s1[pos1] == alist[pos2]:found = Trueelse:pos2 = pos2 + 1if found:alist[pos2] = Nonepos1 = pos1 + 1else:stillOK = Falsereturn stillOK
print('是否是反序字符串:',anagramSolution1('abcdd','ddcba'),)

为了分析这个算法,我们注意到 s1 的每个字符都会在 s2 中进行最多 n 个字符的迭代。s2 列 表中的 n 个位置将被访问一次来匹配来自 s1 的字符。访问次数可以写成 1 到 n 整数的和, 可以写成

当 n 变大,n2 这项占据主导,1/2 可以忽略。所以这个算法复杂度为 O(n2)。

  • 解法二:排序和比较

另一个解决方案是利用这么一个事实:即使 s1,s2 不同,它们都是由完全相同的字符组成的。所以,我们按照字母顺序从a到z排列每个字符串,如果两个字符串相同,那这两个字符串就是乱序字符串。

def anagramSolution2(s1,s2):alist1 = list(s1)alist2 = list(s2)alist1.sort()alist2.sort()pos = 0matches = Truewhile pos < len(s1) and matches:if alist1[pos]==alist2[pos]:pos = pos + 1else:matches = Falsereturn matches
print('是否是反序字符串:',anagramSolution2('abcde','cdabe'))

首先你可能认为这个算法是 O(n),因为只有一个简单的迭代来比较排序后的 n 个字符。但是,调用 Python 排序不是没有成本。正如我们将在后面的章节中看到的,排序通常是 O(n2) 或 O(nlogn)。所以排序操作比迭代花费更多。最后该算法跟排序过程有同样的量级。

  • 解法三:计数和比较

我们最终的解决方法是利用两个乱序字符串具有相同数目的 a, b, c 等字符的事实。我们首先 计算的是每个字母出现的次数。由于有 26 个可能的字符,我们就用一个长度为 26 的列表,每个可能的字符占一个位置。每次看到一个特定的字符,就增加该位置的计数器。最后如果两个列表的计数器一样,则字符串为乱序字符串。

def anagramSolution4(s1,s2):if s1==s2:return Falseelse:c1 = [0]*26c2 = [0]*26for i in range(len(s1)):
#ord()返回值是对应的十进制整数。返回对应的 ASCII 数值,或者 Unicode 数值,如果所给的 Unicode 字符超出了你的 Python 定义范围,则会引发一个 TypeError 的异常。pos = ord(s1[i])-ord('a')c1[pos] = c1[pos] + 1for i in range(len(s2)):pos = ord(s2[i])-ord('a')c2[pos] = c2[pos] + 1j = 0stillOK = Truewhile j<26 and stillOK:if c1[j]==c2[j]:j = j + 1else:stillOK = Falsereturn stillOK
print('是否是反序字符串:',anagramSolution4('apple','apple'))

同样,这个方案有多个迭代,但是和第一个解法不一样,它不是嵌套的。两个迭代都是n, 第三个迭代,比较两个计数列表,需要 26 步,因为有 26 个字母。一共 T(n)=2n+26 ,即 O(n),我们找到了一个线性量级的算法解决这个问题。

在结束这个例子之前,我们来讨论下空间花费,虽然最后一个方案在线性时间执行,但它需要额外的存储来保存两个字符计数列表。换句话说,该算法牺牲了空间以获得时间。很多情况下,你需要在空间和时间之间做出权衡。这种情况下,额外空间不重要,但是如果有数百万个字符,就需要关注下。

python算法(基础)---算法分析相关推荐

  1. python算法基础设计模式,python常见的设计模式

    Python有设计模式么 Python设计模式主要分为三大类:创建型模式.结构型模式.行为型模式;三 大类中又被细分为23种设计模式,以下这几种是最常见的. 单例模式:是一种常用的软件设计模式,该模式 ...

  2. python算法(基础)----无序列表抽象数据类型

    下面给出了一些可能的无序列表操作. List() 创建一个新的空列表.它不需要参数,并返回一个空列表. add(item) 向列表中添加一个新项.它需要 item 作为参数,并不返回任何内容.假定该 ...

  3. C/C++ 算法基础

    如果要真正掌握算法,必须要写代码的,那这时候就必须选择一门语言来进行,而具体的语言其实无所谓 ,C.C++.Java.Python,go甚至JavaScript.VB都可以,关键是自己要用的熟悉,面试 ...

  4. 《数据结构与算法:Python语言描述》一1.3算法和算法分析

    本节书摘来自华章出版社<数据结构与算法:Python语言描述>一书中的第1章,第1.3节,作者 裘宗燕,更多章节内容可以访问云栖社区"华章计算机"公众号查看 1.3算法 ...

  5. 送书 | 你一定能看懂的算法基础书(代码示例基于Python)

    本文引自图灵教育<算法图解> 你一定能看懂的算法基础书:代码示例基于Python:400多个示意图,生动介绍算法执行过程:展示不同算法在性能方面的优缺点:教会你用常见算法解决每天面临的实际 ...

  6. python小白-day4递归和算法基础

    递归&算法基础 一.递归 递归函数的优点是定义简单,逻辑清晰.理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰. 使用递归函数需要注意防止栈溢出.在计算机中,函数调用是通过 ...

  7. 【python】一道LeetCode搞懂递归算法!#131分割回文串 #以及刷LeetCode的一点点小心得 [数据结构与算法基础]

    题目:给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串.返回 s 所有可能的分割方案. # 示例 输入: "aab" 输出: [["aa",&q ...

  8. OI-wiki 算法基础 模拟 NOIP2014 生活大爆炸版石头剪刀布 python

    OI-wiki 算法基础 模拟 https://oi-wiki.org/basic/simulate/ 习题答案 NOIP2014 生活大爆炸版石头剪刀布 python https://uoj.ac/ ...

  9. 《Python程序设计与算法基础教程(第二版)》江红 余青松,第九章课后习题答案

    推荐阅读 <Python程序设计与算法基础教程(第二版)>江红 余青松 全部章节的课后习题,上机实践,课后答案,案例研究 文章目录 例9.1~例9.53 填空题:2 思考题:3~11 上机 ...

  10. 《Python程序设计与算法基础教程(第二版)》江红 余青松 全部章节的课后习题,上机实践,课后答案,案例研究

    (还在更新中-) 这篇博客花费了我的大量时间和精力,从创作到维护:若认可本篇博客,希望给一个点赞.收藏 并且,遇到了什么问题,请在评论区留言,我会及时回复的 这本书对Python的知识点的描述很详细, ...

最新文章

  1. 摄影测量(计算机视觉)中的三角化方法
  2. php轮播代码生成器,最简单的Banner轮播左右切换效果代码及实现思路(附带源码)...
  3. 8、使用SELECTI...INTO OUTFILE导出表数据
  4. 微分算子为什么也是空间滤波器
  5. openresty开发系列17--lua中的正则表达式
  6. 代腾飞(一读者再为IT诗人代腾飞名字作诗)
  7. [Linux 使用(2)] 64位Linux下安装jboss-as-7.1 以及jdk1.7
  8. Mysql学习总结(26)——MySQL子查询
  9. ecnu1244 积木游戏
  10. 电脑怎么重装系统Win11?需要什么条件
  11. Windows 10专业版下如何启用语音识别功能
  12. 本示例主要展示如何在XtraGrid网格控件(包含在DevExpress WinForms套包中)的主视图中指定HyperLinkEdit控件作为列编辑器...
  13. 《Go程序设计语言》- 第11章:测试
  14. 详解汽轮机的TSI系统
  15. Android开发之指南针
  16. 谷歌李开复 我的传奇人生源于十句箴言
  17. html 大转盘游戏,HTML5 Canvas大转盘抽奖活动页面代码
  18. frida 挂钩_您必须知道的预提交挂钩
  19. 绿皮书——iOS导出微信聊天记录,并用python制作词云
  20. 记录一些视频直播测试地址 rtmp rtsp http

热门文章

  1. UI设计师的市场需求怎样
  2. React中如何引入原生JS库
  3. 常用电子方面的网站4(通信电子)
  4. 计算机博士英文复试自我介绍,博士复试英文自我介绍模版(仅供参考)
  5. OSC职位推荐:来米米乐,有舞台让你施展才华
  6. bash: ifconfig: command not found 解决办法
  7. python中显示功能的实现,如何用python 实现老板键功能
  8. [SSL证书].pfx格式和.Cer格式的区别以及格式互相转换
  9. pythonwin10
  10. 关于 VMware 磁盘空间 只增不减 问题的解决