数据结构与算法——3.时间复杂度分析1(概述)
前面我们已经介绍了,研究算法的最终目的是如何花费更少的时间,如何占用更少的内存去完成相同的需求,并且也通过案例演示了不同算法之间时间耗费和空间耗费上的差异,但我们并不能将时间占用和空间占用量化。因此,接下来我们要学习有关算法时间耗费和算法空间耗费的描述与分析。有关算法时间耗费分析,我们称之为算法的时间复杂度分析。
1.时间复杂度分析方法
我们要计算算法耗费时间情况,首先我们得度量算法的执行时间,那么如何度量呢?
1.1事后分析估算方法
比较容易想到的方法就是我们把算法执行若干次,然后拿个计时器在旁边计时,这种事后统计的方法看上去的确不错,并且也并非要我们真的拿个计算器在旁边计算,因为计算机都提供了计时的功能。这种统计方法主要是通过设计好的测试程序和测试数据,利用计算机计时器对不同的算法编制的程序的运行时间进行比较,从而确定算法效率的高低,但是这种方法有很大的缺陷︰必须依据算法实现编制好的测试程序,通常要花费大量时间和精力,测试完了如果发现测试的是非常糟糕的算法,那么之前所做的事情就全部白费了,并且不同的测试环境(硬件环境)的差别导致测试的结果差异也很大。
如下例所示:
public static void main(String[] args) {long start = System.currentTimeMillis();int sum = 0;int n = 100;for (int i = 1; i <= n; i++) {sum +=i;}System.out.println("sum="+sum);long end = System.currentTimeMillis();System.out.println(end-start);}
如何理解该方法的缺陷?
举例说明:比如我们用来三天的时间写了一个测试某个确定算法的程序,然后运行它,测试这个算法的时间,发现测试时间很长,那在实际中这个算法肯定就不能使用啊,那也就是说这个算法是不好的,是有缺陷不能用的,那么你写的这个测试程序也是不能用的,也就是说你白花了三天时间。这就是一个极大的缺陷。
1.2事前分析估算方法
在计算机程序编写之前,依据统计方法对算法进行估算,经过总结,我们发现一个高级语言编写的程序,程序在计算机上运行消耗的时间取决于下列因素:
- 算法采用的策略和方案;
- 编译产生的代码质量;(不可人为干预)
- 问题的输入规模(所谓的问题输入规模就是输入量是多少);
- 机器执行指令的速度;(不可人为干预)
由此可见,抛开那些与计算机硬件、软件有关的因素,一个程序的运行时间依赖于算法的好坏和问题的输入规模。如果算法固定,那么该算法的执行时间就只和问题的输入规模有关系了。
2.案例说明
2.1案例一
题目:计算1到100的和
方法一:
//如果输入量n为1,则需要计算1次//如果输入量n为1亿,则需要计算1亿次
public static void main(String[] args) {int sum = 0; //执行1次int n = 100; //执行1次for (int i = 1; i <= n; i++) {//执行n+1次sum +=i; //执行n次}System.out.println("sum="+sum);}
方法二:
//如果输入量n为1,则需要计算1次//如果输入量n为1亿,则需要计算1亿次public static void main(String[] args) {int sum = 0; //执行1次int n = 100; //执行1次sum = (n+1)*n/2; //执行1次System.out.println("sum="+sum);}
分析:
方法一,当输入规模为n时,方法一执行了1+1+(n+1)+n=2n+3次
方法二,当输入规模为n时,方法二执行了1+1+1=3次
定量具体分析来看,很明显,方法二的执行次数是要远少于方法一的。
下面,我们来深入分析一次这两段代码:
方法一,这个算法求和的核心代码是那个for循环,如果我们把这个for循环看成是一个整体,不考虑什么判断条件啊,什么次数啊,只考虑这个循环里面的内容,然后再忽略其他的一些简要的一次就执行的代码,那么方法一的执行次数就简化为n次
方法二,同样的道理,我们简化那么简单的一次就行的代码语句,重点关注算法的核心语句的执行次数,那么方法二的执行次数就简化为1次
再对比一下,两个方法运行时间的差距就是n与1的差距。
注意:这里我们用了简化思维,直接去抓取每个算法的核心部分,并分析这个核心部分执行的次数
问题:
为什么循环判断条件在方法一中执行了n+1次,看起来是个不小的数字,但是可以忽略呢?
答:看2.2案例二
2.2案例二
题目:计算100个1+100个2+100个3+,,,,,,+100个100的结果
public static void main(String[] args) {int sum = 0;int n = 100;for (int i = 1; i <= n; i++) {for (int j = 1; j <=n ; j++) {sum+=i;}}System.out.println("sum="+sum);}
上面这个例子中,如果我们要精确的研究循环的条件执行了多少次,是一件很麻烦的事情,并且,由于真正计算和的代码是内循环的循环体,所以,在研究算法的效率时,我们只考虑核心代码的执行次数,这样可以简化分析。
下面给出2.1中问题的四个答案,可以酌情选择思考
答案1:精确循环次数很麻烦
第一个for循环的条件判断执行了100次,第二个for循环的条件判断执行了10000次,但是请思考下面的几种情况:如果有5层循环,每层次数不一样,让你求第4层循环的判断次数,是不是很麻烦?如果上题中,n=1234,求第二层循环的判断次数是不是也很麻烦?所以,我们忽略循环的判断次数。
答案2:只关心得到结果的最核心部分
上面的所有循环都是为了sum+=i 这句服务的,并且这个算法的最核心部分也是sum+=i,最后的结果也是由这一句得到的,所以,我们只关心这一句。
答案3:当做整体看
因为你每执行一次sum+=i 这句,那就比如执行了前面的for循环的条件判断啊,这二者是一个整体的,是密不可分的,所以就忽略了for的条件判断,只关心那一句
答案4:老师讲的!
我们研究算法复杂度,侧重的是当输入规模不断增大时,算法的增长量的一个抽象(规律),而不是精确地定位需要执行多少次,因为如果是这样的话,我们又得考虑回编译期优化等问题,容易主次跌倒。
我们不关心编写程序所用的语言是什么,也不关心这些程序将跑在什么样的计算机上,我们只关心它所实现的算法。这样,不计那些循环索引的递增和循环终止的条件、变量声明、打印结果等操作,最终在分析程序的运行时间时,最重要的是把程序看做是独立于程序设计语言的算法或一系列步骤。我们分析一个算法的运行时间,最重要的就是把核心操作的次数和输入规模关联起来。
3.函数渐进增长
概念:
给定两个函数f(n)和g(n)如果存在一个整数N,使得对于所有的n>N,f(n)总是比g(n)大,那么我们说f(n)的增长渐近快于(gn)。概念似乎有点艰涩难懂,那接下来我们做几个测试。
3.1测试1
假设四个算法的输入规模都是n :
- 算法A1要做2n+3次操作,可以这么理解︰先执行n次循环,执行完毕后,再有一个n次循环,最后有3次运算
- 算法A2要做2n次操作
- 算法B1要做3n+1次操作,可以这个理解∶先执行n次循环,再执行一个n次循环,再执行一个n次循环,最后有1次运算
- 算法B2要做3n次操作
那么,上述算法,哪一个更快一些呢?
通过数据表格,比较算法A1和算法B1 ∶
- 当输入规模n=1时,A1需要执行5次,B1需要执行4次,所以A1的效率比B1的效率低;
- 当输入规模n=2时,A1需要执行7次,B1需要执行7次,所以A1的效率和B1的效率一样;
- 当输入规模n>2时,A1需要的执行次数一直比B1需要执行的次数少,所以A1的效率比B1的效率高
所以我们可以得出结论︰
当输入规模n>2时,算法A1的渐近增长小于算法B1的渐近增长
通过观察折线图,我们发现,随着输入规模的增大,算法A1和算法A2逐渐重叠到一块,算法B1和
算法B2逐渐重叠到一块,所以我们得出结论︰
随着输入规模的增大,算法的常数操作可以忽略不计
3.2测试2
假设四个算法的输入规模都是n :
- 算法C1需要做4n+8次操作
- 算法C2需要做n次操作
- 算法D1需要做2n^2次操作
- 算法D2需要做n^2次操作
那么上述算法,哪个更快一些?
通过数据表格,对比算法C1和算法D1 :
- 当输入规模n<=3时,算法C1执行次数多于算法D1,因此算法C1效率低一些;
- 当输入规模n>3时,算法C1执行次数少于算法D1,因此,算法D2效率低一些,所以,总体上,算法C1要优于算法D1
通过折线图,对比对比算法C1和C2:
随着输入规模的增大,算法C1和算法C2几乎重叠
通过折线图,对比算法C系列和算法D系列:
随着输入规模的增大,即使去除n^2前面的常数因子,D系列的次数要远远高于C系列。
因此,可以得出结论:
随着输入规模的增大,与最高次项相乘的常数可以忽略
3.3测试3
假设四个算法的输入规模都是n:
- 算法E1:2n^2+3n+1;
- 算法E2:n^2
- 算法F1:2n^3+3n+1
- 算法F2:n^3
那么上述算法,哪个更快一些?
通过数据表格,对比算法E1和算法F1 :
- 当n=1时,算法E1和算法F1的执行次数一样;
- 当n>1时,算法E1的执行次数远远小于算法F1的执行次数;
所以算法E1总体上是由于算法F1的。
通过折线图我们会看到,算法F系列随着n的增长会变得特块,算法E系列随着n的增长相比较算法F
来说,变得比较慢,所以可以得出结论:
最高次项的指数大的,随着n的增长,结果也会变得增长特别快
3.4测试4
假设五个算法的输入规模都是n :
- 算法G :n^3;
- 算法H:n^2;
- 算法I:n;
- 算法J:logn;
- 算法K:1;
那么上述算法,哪个效率更高呢?
通过观察数据表格和折线图,很容易可以得出结论:
算法函数中n最高次幂越小,算法效率越高
3.5小结
总上所述,在我们比较算法随着输入规模的增长量时,可以有以下规则:
- 算法函数中的常数可以忽略
- 算法函数中最高次幂的常数因子可以忽略
- 算法函数中是高次幂越小,算法效率越高
4.大O计法
4.1具体定义
定义:
在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随着n的变化情
况并确定T(n)的量级。算法的时间复杂度,就是算法的时间量度,记作:T(n)=O(f(n))。它表示随
着问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简
称时间复杂度,其中f(n)是问题规模n的某个函数。
在这里,我们需要明确一个事情:执行次数=执行时间
用大写O()来体现算法时间复杂度的记法,我们称之为大O记法。一般情况下,随着输入规模n
的增大,T(n)增长最慢的算法为最优算法。
4.2案例分析
下面用大O表示法来表示一些求和算法的时间复杂度
算法一:
public static void main(String[] args) {int sum = 0;//执行1次int n = 100;//执行1次sum = (n+1)*n/2;//执行1次System.out.println("sum="+sum);}
算法二:
public static void main(String[] args) {int sum = 0;//执行1次int n = 100;//执行1次for (int i = 1; i <= n; i++) {sum+=i;//执行n次}System.out.println("sum="+sum);}
算法三:
public static void main(String[] args) {int sum = 0;//执行1次int n = 100;//执行1次for (int i = 1; i <= n; i++) {for (int j = 1; j <=n ; j++) {sum+=i;//执行n^2次}}System.out.println("sum="+sum);}
如果忽略判断条件的执行次数和输出语句的执行次数,那么当输入规模为n时,以上算法执行的次
数分别为:
算法一:3次
算法二:n+3次
算法三:n^2+2次
如果用大o记法表示上述每个算法的时间复杂度,应该如何表示呢?基于我们对函数渐近增长的分
析,推导大O阶的表示法有以下几个规则可以使用:
- 用常数1取代运行时间中的所有加法常数
- 在修改后的运行次数中,只保留高阶项
- 如果最高阶项存在,且常数因子不为1,则去除与这个项相乘的常数
所以,上述算法的大O计法分别为:
算法一:O(1)
算法二:O(n)
算法三:O(n^2)
5.小结
这篇文章,首先我们给出了算法的时间复杂度的分析方法,引导我们如何去分析一个算法的时间复杂度,然后讲了一系列的函数渐进增长,通过具体的实例总结了一些结论,最后我们在这些结论的基础上提出来大O计法,然后结合大O计法和那些结论,我们实际的分析了一些求和算法的时间复杂度。
相关文章:
数据结构与算法——3.时间复杂度分析1(概述)
前面我们已经介绍了,研究算法的最终目的是如何花费更少的时间,如何占用更少的内存去完成相同的需求,并且也通过案例演示了不同算法之间时间耗费和空间耗费上的差异,但我们并不能将时间占用和空间占用量化。因此,接下来…...
FPGA学习之日常工作复位电路
最近一个多月没有写博客了,然后最近工作中也遇到一个复位信号的问题。问题是这样的,关于外部复位信号,之前我们的处理方式都是通过PLL产生的Lock信号作为内部的复位信号。但是由于换到A54上面没有IP核,所以只有不用PLL,…...
【洛谷 P1177】【模板】快速排序 题解(快速排序+指针)
【模板】快速排序 题目描述 利用快速排序算法将读入的 NNN 个数从小到大排序后输出。 快速排序是信息学竞赛的必备算法之一。对于快速排序不是很了解的同学可以自行上网查询相关资料,掌握后独立完成。(C 选手请不要试图使用 STL,虽然你可以…...
Pthon--自动化实用技巧篇--文件目录处理
为什么要讲这一篇,主要是因为这个在自动化测试框架或者脚本的编写的时候会用到,还是比较方便的。看上述两个函数。getcwd()、chdir()。使用 os.getcwd() 函数获得当前工作目录。使用 os.chdir()函数改变当前工作目录。所以在用chdir()函数的时候别忘记指…...
想招到实干派程序员?你需要这种面试法
技术招聘中最痛的点其实是不精准。技术面试官或CTO们常常会向我们吐槽: “我经常在想,能不能把我们项目中的代码打印出来,作为候选人的面试题的一部分?” “能不能把一个Bug带上环境,让候选人来试试怎么解决…...
cesium常见操作:鼠标点击获取对象
目录 一、viewer.scene.pick(获取Cartesian2) 二、 viewer.scene.pickPosition(获取Cartesian3) 三、viewer.scene.drillPick(穿透拾取,获取所有对象) 四、viewer.scene.globe.pick…...
【玩转c++】git的安装和使用以及可视化处理
本期主题:git的安装和使用(windows环境)博客主页:小峰同学分享小编的在Linux中学习到的知识和遇到的问题 小编的能力有限,出现错误希望大家不吝赐1.两个工具介绍第一个工具git,链接gitee或者github等代码托…...
第三阶段02-Mybatis框架
Mybatis框架 Mybatis框架是目前最流行的数据持久层框架, 使用Mybatis框架可以帮助程序员自动生成JDBC代码, 程序员只需要通过注解或xml配置文件提供需要执行的SQL语句,以及对象和表的映射关系, Mybatis框架会根据此映射关系和SQL自动生成出JDBC代码,从而提高开发效率 Mybatis框…...
基于超像素的多视觉特征图像分割算法研究
0.引言 背景: 经典聚类算法:Kmeans、FCM 现有问题: 1)现有算法大都是基于单一的视觉特征而设计的,eg:基于颜色特征的分割。 2)没有考虑像素周围的空间信息;分割结果:多噪…...
mysql的三大日志
摘自https://blog.csdn.net/chuige2013/article/details/123027580 一. 初步认识 binlog二进制日志 redolog undolog 二. binlog binlog记录写入行操作 作用 1)、主从复制:在Master端开启binlog,然后将binlog发送到各个Slave端,S…...
API接口及社区电子商务化的解释
API是应用程序的开发接口,在开发程序的时候,我们有些功能可能不需要从到到位去研发,我们可以拿现有的开发出来的功能模块来使用,而这个功能模块,就叫做库(libary)。比如说:要实现数据传输的安全,…...
[蓝帽杯 2021]One Pointer PHP
知识点:php 数组整型溢出,open_basedir 绕过分析 利用数组整型溢出绕过,因为PHP 会对溢出的数字处理为 float 类型。 <?php include "user.php"; if($userunserialize($_COOKIE["data"])){$count[$user->count]…...
【JAVA】xxl-job服务搭建
xxl-job服务搭建 1.下载xxl-job项目 https://github.com/xuxueli/xxl-job 2.数据库表创建 3.修改配置 注意:这是两个项目,一个是xxl-job前台,一个是xxl-job执行器,找到这两个项目得配置文件,修改配置。 配置文件地址…...
毕业设计 基于STM32单片机生理监控心率脉搏TFT彩屏波形曲线设计
基于STM32单片机生理监控心率脉搏TFT彩屏波形曲线设计1、项目简介1.1 系统构成1.2 系统功能2、部分电路设计2.1 STM32F103C8T6核心系统电路设计2.2心率检测电路设计2.3 TFT2.4寸彩屏电路设计3、部分代码展示3.1 ADC初始化3.2 获取ADC采样值3.3 LCD引脚初始化3.3 在LCD指定位置显…...
【10k~30k的区别】=== 功能测试、自动化测试、性能测试的区别
按测试执行的类型来分:功能测试、自动化测试、性能测试 1.功能测试 功能测试俗称点点点测试。初级测试人员的主要测试任务就是执行测试工程师所写的测试用 例,记录用例的执行状态及bug情况。与开发人员进行交互直到bug被修复。 功能测试理论…...
《MySQL学习》 索引失效的三种特殊情况
一.条件字段使用函数 explain select * from bpm_proc_instance bpi where CREATED_AT > 2022-06-01 CREATED_AT 字段建立了索引,此时explain分析的结果表明能使用到索引 但如果我们对 CREATED_AT 字段使用函数 explain select * from bpm_proc_instance bpi w…...
wafw00f 防火墙探测
kali机器自带防火墙探测工具wafw00,它可以通过发送正常以及不正常甚至包含恶意代码的HTTP请求,来探测网站是否存在防火墙,并识别防火墙的厂商及类型。安装:git clone https://github.com/EnableSecurity/wafw00f.git python setup…...
MySQL学习(1)[参考书籍:mysql是怎么运行的]
目录 一、mysql设计模式和技术 二、mysql服务器和客户端 启动mysql服务 启动mysql客户端程序 三、mysql存储引擎 四、mysql配置 五、mysql系统变量 六、mysql字符集 编码和解码: 常见字符集(五种): 相关概念࿱…...
用Python制作邮件检测器
github地址: https://github.com/CaLlMeErIC/MailDetective 因为需求需要写一个简单的邮件检测系统的框架,这里记录下思路 首先第一反应,这个检测系统不应该是各个邮件收件系统都有自带的吗,于是搜索了下是否有相关的邮件检测开源软件&#…...
K8S---pod基础概念
目录 一、资源限制 二、Pod 的两种使用方式 三、Pod 资源共享 四、底层容器Pause 1、Pause共享资源 1.1 网络 1.2 存储 1.3 小结 2、Pause主要功能 3、Pod 与 Pause 结构的设计初衷 五、Pod容器的分类 1、基础容器(infrastructure container)…...
激活函数入门学习
本篇文章从外行工科的角度尽量详细剖析激活函数,希望不吝指教! 学习过程如下,先知道这个东西是什么,有什么用处,以及怎么使用它: 1. 为什么使用激活函数 2. 激活函数总类及优缺点 3. 如何选择激活函数 …...
小文智能结合ChatGPT的产业未来
最近几个月,由人工智能实验室OpenAI发布的对话式大型语言模型ChatGPT在国内外各大平台掀起了一阵AI狂潮。短短几天时间,其用户量就突破了百万大关,注册用户之多一度导致服务器爆满。 继AI画图之后,ChatGPT成为了新的顶流…...
Linux-编写一个自己的命令
前言(1)在Linux中,我们对文件路径进行操作都需要输入命令。那么,有人可能就会有疑惑了,命令是什么东西?我们是否也可以创造出自己的命令呢?答案是可以的。命令本身其实就是可执行文件。但是与普…...
Nacos架构篇 - Distro协议
Distro 它是 Nacos 社区自研的一种 AP 分布式协议(也是最终一致性协议)。它面向临时实例,保证了在某些 Nacos 节点宕机后,整个临时实例处理系统依旧可以正常工作。作为一种有状态的中间件应用的内嵌协议,Distro 保证了…...
和月薪3W的聊过后,才知道自己一直在打杂...
前几天和一个朋友聊面试,他说上个月同时拿到了腾讯和阿里的offer,最后选择了阿里。 我了解了下他的面试过程,就一点,不管是阿里还是腾讯的面试,这个级别的程序员,都会考察项目管理能力,并且权重…...
关于Ubuntu18.04 root账户登录的问题
关于Ubuntu18.04 root账户登录的问题一、 Ubuntu 18.04添加root用户登录1. 设置root用户2. 修改/root/.profile3. 修改/etc/pam.d目录下的gdm-autologin和gdm-password4. 修改50-ubuntu.conf5. 登录root账户二、Ubuntu18.04不能远程使用root账户登录的问题1. 修改sshd_config2.…...
基于jeecgboot的flowable的H5版本在演示系统发布
目前在NBCIO 亿事达企业管理平台上发布了H5的在线演示系统,欢迎大家批评指正。 在nbcio-vue nbcio-vue: NBCIO 亿事达企业管理平台前端代码,基于ant-design-vue-jeecg的前端版本: 3.0.0代码和和flowable6.7.2,初步完成了集流程设…...
【代码训练营】day44 | 完全背包理论 518. 零钱兑换 II 377. 组合总和 Ⅳ
所用代码 java 完全背包 01背包物品只能使用一次 – 倒序遍历 for(i 0; i < weight.length; i){ 物品for (j bagWeight; j > weight[i]; j--){ 背包dp[j] max(dp[j], dp[j-weight[i]] value[i])} }完全背包物品可以使用无限次 – 正序遍历 for(i 0; i < weigh…...
ICA简介:独立成分分析
1. 简介 您是否曾经遇到过这样一种情况:您试图分析一个复杂且高度相关的数据集,却对信息量感到不知所措?这就是独立成分分析 (ICA) 的用武之地。ICA 是数据分析领域的一项强大技术,可让您分离和识别多元数据集中的底层独立来源。 …...
②【Java 组】蓝桥杯省赛真题解析 [振兴中华] [三部排序] 持续更新中...
个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~ 蓝桥杯真题--持续更新中...一、振兴中华二、三…...
为wordpress首页添加关键词/百度查重工具
一、ELK 相关资料 ELK官网: 点击打开链接 ELKstack 中文指南:点击打开链接 二、安装过程 节点1:172.214.5.19 节点2:172.216.18.40 节点3:172.216.33.100 1、Java安装 # yum -y install java-1.8.0 # v…...
网站空间可以自己做吗/最新国际新闻热点事件
原因一:没有打开MSDTC服务 步骤: Componet Services-->右击My Computer--->Start MSDTCComponet Services-->右击My Computer-->属性--->MSDTC-->安全配置--->勾选上我红线标注的部分。原因二: 防火墙阻止 解决方法&…...
手机网站与电脑网站的区别/网络营销技巧
题库来源:安全生产模拟考试一点通公众号小程序 广西省安全员B证最新解析是安全生产模拟考试一点通总题库中生成的一套广西省安全员B证新版试题,安全生产模拟考试一点通上广西省安全员B证作业手机同步练习。2021年广西省安全员B证最新解析及广西省安全员…...
家庭安全卫士论坛WordPress/百度seo指数查询
湘潭H5课件制作 sa1d45湘潭H5课件制作在他看来,自己的技术还很粗糙,制作速度和质量都有差距,既然大伙儿喜欢3D动画这种教学方式,那就好好学,以后再搞出几部其它岗位的“大片”给兄弟们看看。是和技术狂飙方向截然相反的…...
浙江建设信息港手机版/优化服务平台
上周我们组在alpha阶段后进行了短暂的休整,因为连续一个月的高强度工作确实需要休息一下。 今天主要的工作就是和学霸网站组讨论数据库连接,统一接口。 迭代图: 转载于:https://www.cnblogs.com/hotsbuaa/p/4152270.html...
网站跳出率计算/seo费用
php session 错误更新时间:2009年05月21日 12:59:31 作者:关于session的问题集锦解决方案1.错误提示Warning: Cannot send session cookie - headers already sentWarning: Cannot send session cache limiter - headers already sent分析及解决办法这…...