算法的复杂性

通常情况下,一个问题可能对应有多种解决方案,每种解决方案都是一种算法。因此,我们可能经常需要做一件事:从众多算法中挑选出一个最好的算法。所谓“最好”的算法,即最适合当前场景使用的算法。

不同的应用场景,挑选算法的侧重点也有所不同。多数情况下,挑选算法主要考虑以下两个因素:

  • 算法的执行效率:一个算法的执行时间越短,执行效率就越高;
  • 占用存储空间的大小:有些机器的存储空间很少,因此挑选算法时,应优先选择所需存储空间小的算法。

在适合特定场景的前提下,算法的执行效率越高、占用的存储空间越小,算法就越好。

判断算法的“好坏”,有以下两种方式:

  1. 事后统计:将各个算法都编写成可运行的程序,然后交给计算机执行,记录程序的运行时间和占用的存储空间的实际大小,从而挑选出最好的算法;
  2. 预先估算:根据算法包含的各个步骤,估算出算法的运行时间和占用存储空间的大小。通过比较各个算法的估算值,挑选出最好的算法。


大多数情况下,我们选择第 2 种方式。原因很简单,将众多算法一一实现的工作量太大,得不偿失。此外,不同计算机的软、硬件环境不同,即便使用同一台计算机,不同时间段的运行环境也不相同,事后统计的结果会受到各种因素的影响,不一定准确。

我们习惯用时间复杂度表示一个算法的运行时间,用空间复杂度表示算法占用存储空间的大小。

时间复杂度

时间复杂度用来表示算法运行所需要的时间。

计算一个算法的时间复杂度,需要经历以下几个步骤:

1) 统计算法中各个步骤的执行次数

我们知道,算法是有限的执行步骤的集合,每个算法都可以用伪代码来表示。因此,一个算法的执行时间可以用伪代码中所有指令执行次数的总和来表示。

举个例子,计算从 1 加到 n 的和并输出。如下用伪代码的形式描述了一个可以解决此问题的算法:

sum <- 0                      // 将 0 赋值给 sum,执行 1 次
for i <- 1 to n+1:         // i 从 1 循环到 n+1(i<n+1),执行 n+1 次
    sum <- sum+i;         // 每次循环将 sum+i 的值赋值给 sum,执行 n 次
print sum                     // 输出 sum 的值,执行 1 次

整段伪代码共有 4 条指令,它们的执行次数分别为 1 次、n+1 次、n 次、1 次,总的执行次数为 2*n+3 次。因此,我们可以用 2*n+3 表示该算法的执行时间,其中 n 是一个变量。这意味着,算法的执行时间和 n 的大小有直接的关系。

显然,通过此方法统计得到的算法的执行时间,并不是固定值,更多时候得到的是类似 2*n+3、n2+2*n+3 这样的表达式。这就产生一个问题,如何通过比较不同的表达式挑选出效率最高的算法呢?

2) 大 O 记法比较表达式的大小

某个问题对应有 3 种算法,它们各自的执行时间分别为 10、2*n+3、n2+2*n+3,如何比较它们的大小呢?

很简单,我们只需要确定 n 的值,就可以轻松比较出它们的大小。例如当 n =1 时,它们的大小关系是 10 > n2+2*n+3 > 2*n+3,因此 2*n+3 对应算法的执行效率最高;再比如 n =10 时,它们的大小关系为 n2+2*n+3 > 2*n+3 > 10,因此 10 对应算法的执行效率最高。

大多数场景下,我们都遵循这样的比较原则:假设 n 的值无限大,比较各个表达式的大小,从而找到执行效率最高的算法。仍以 10、2*n+3、n2+2*n+3 为例,当 n 的值趋于无限大时,显然 10 对应算法的执行效率最高。

对于累加项个数较少的表达式,我们很容易就能比较它们的大小,但如果算法的执行时间是类似 3*n+2*n2+4+logn+... 这样的表达式,比较起来会有些困难。针对这种情况,我们可以依照如下规则对表达式进行简化:

  1. 去掉表达式中所有的加法常数项,比如 3*n2+2*n+3 简化为 3*n2+2*n。当 n 值无限大时,常数项对整个表达式的值的影响可以忽略不计;
  2. 去掉表达式中指数较低的加法项,例如 3*n2+2*n 简化为 3*n2。当 n 无限大时,n2 的值远远大于 n,此时 n 值的变化对 n2 的影响可以忽略不计。
  3. 去掉 n 的系数,例如 3*n2 简化为 n2。当 n 无限大时,系数对整个表达式值的影响可以忽略不计。


根据此规则,我们就将 3*n2+2*n+3 简化为了 n2。同理,我们可以将 3*n+2*n2+4+logn 简化为 n2。通过简化表达式,降低了表达式的复杂度,便于我们比较各个表达式的大小关系。

此外,为了避免人们随意使用 a、b、c 等字符来表示算法的运行时间,大家逐渐达成了一种共识,即采用大 O 记法(注意,是大写的字母 O,不是数字 0)表示算法(程序)的运行时间,又称为算法的时间复杂度。

发展至今,大 O 记法已为大多数人所采纳,表示方法也很简单,格式如下:

O(频度)

其中,频度就是简化之后的表达式。注意,对于执行时间为常数的(例如上面的 10),算法的时间复杂度用 O(1) 表示。

例如,n2+2*n+3 对应算法的时间复杂度为 O(n2)。如下列举了常用的几种时间复杂度,以及它们之间的大小关系(值越小,算法的运行效率越高):

O(1)常数阶 < O(logn)对数阶 < O(n)线性阶 < O(n2)平方阶 < O(n3)(立方阶) < O(2n) (指数阶)

空间复杂度

算法的空间复杂度,指的是该算法执行过程占用的存储空间的大小。

根据算法编写出的程序,执行过程中占用的存储空间大致可分为以下几部分:

  • 程序代码本身所占用的存储空间;
  • 程序中如果需要输入输出数据,也会占用一定的存储空间;
  • 程序在运行过程中,可能会临时申请额外的存储空间。


程序自身所占用的存储空间取决于其包含的代码量,如果要压缩这部分存储空间,只需要精简程序代码即可。程序运行过程中输入输出的数据往往由实际问题而定,也就是说,程序输入输出所占用的存储空间和选用的算法之间,没有必然的联系。

因此,比较算法占用的存储空间,针对的往往是算法执行过程中额外申请的存储空间的大小。不同的算法,其执行时额外申请的存储空间的大小也有所不同。

举个例子,如下是一段伪代码:

输入 n               // 输入 n 值
for i<-1 to n     // 将从 1 到 n 的值存储在 A 数组中
    A[i] <- i        //  将 i 存储在 A 数组中第 i 个元素的位置

可以看到,根据 n 的值,伪代码中会额外申请可存放 n 个元素的存储空间。

和时间复杂度一样,算法的空间复杂度也采用大 O 记法表示:

  • 如果算法中额外申请的存储空间和输入值无关,则算法的空间复杂度就为 O(1);
  • 如果随着输入值 n 的增大,算法申请的存储空间成线性增长,则程序的空间复杂度用 O(n) 表示;
  • 如果随着输入值 n 的增大,程序申请的存储空间成 n2 关系增长,则程序的空间复杂度用 O(n2) 表示;
  • 如果随着输入值 n 的增大,程序申请的存储空间成 n3 关系增长,则程序的空间复杂度用 O(n3) 表示;
  • 等等。


以上面的这段伪代码为例,随着 n 值的增大,A[1 ... n] 申请的存储空间也会增加,n 增大的速率和额外申请空间的速率之间呈线性关系,因此这部分伪代码的空间复杂度为 O(n)。

多数实际场景中,一个好的算法往往更注重的是时间效率,空间复杂度只要在一个合理的范围内即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/141365.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springboot整合SSE技术开发经验总结及心得

springboot整合SSE技术开发经验总结及心得 一、开发背景二、快速了解SSE1、概念2、特性 三、开发思路四、代码演示1、引入依赖2、服务端代码3、后端定时任务代码 4、解决乱码的实体类4、前端代码 五、核心代码分析 一、开发背景 公司需要开发一个大屏界面&#xff0c;大屏页面…

使用内网穿透实现U8用友ERP本地部署与异地访问

文章目录 前言1. 服务器本机安装U8并调试设置2. 用友U8借助cpolar实现企业远程办公2.1 在被控端电脑上&#xff0c;点击开始菜单栏&#xff0c;打开设置——系统2.2 找到远程桌面2.3 启用远程桌面 3. 安装cpolar内网穿透3.1 注册cpolar账号3.2 下载cpolar客户端 4. 获取远程桌面…

【C++笔记】AVL树的模拟实现

【C笔记】AVL树的模拟实现 一、AVL树的概念二、AVL树的模拟实现2.1、定义节点2.2、插入2.3、旋转2.3.1、左单旋2.3.2、右单旋2.3.3、左右双旋2.3.4、右左双旋2.3.5、插入接口的整体代码实现 三、验证AVL树3.1、验证 一、AVL树的概念 二叉搜索树虽然在一般情况下可以提高查找的…

生成式AI以及当前趋势

ChatGPT 激发了人们的想象力和好奇心。自 2022 年 11 月推出后&#xff0c;短短两个月内其月活用户便达到 1 亿&#xff0c;成为有史以来增长速度最快的消费类应用和第一个杀手级的生成式 AI 应用。随着创新节奏的加快&#xff0c;想要紧跟生成式 AI 的发展速度&#xff0c;难度…

web前端-Gulp入门

web前端-Gulp入门 gulp的概述使用gulp准备工作gulp的常用APIgulp的常用插件gulpfile.js的初体验打包css文件打包scss文件打包js打包html打包images创建一个默认任务创建一个删除任务gulp启动服务创建一个监控任务 gulp的概述 gulp&#xff1a; 前端自动化打包固件工具&#xf…

Ansible playbook详解

playbook是ansible用于配置&#xff0c;部署&#xff0c;和被管理被控节点的剧本 playbook常用的YMAL格式&#xff1a;&#xff08;文件名称以 .yml结尾&#xff09; 1、文件的第一行应该以 "---" (三个连字符)开始&#xff0c;表明YMAL文件的开始。    2、在同一…

IIC子系统测温湿度

采用stm32MP157AAA芯片&#xff0c;温度传感器 si7006 0x40 1、在内核空间不支持浮点数进行打印&#xff0c;所以需要将读取到的数据拷贝到用户空间&#xff0c;执行用户程序打印 2、在probe函数中 分步注册字符设备驱动自动创建设备节点 3、在i2c驱动代码中&#xff0c;需要自…

通用的链栈实现(C++)

template<class T> class MyStack//链栈 { private:struct StackNode{T data;StackNode* next;StackNode(const T& val T(), StackNode* p nullptr) :data(val), next(p) {}//};StackNode* top;int cursize;void clone(const MyStack& s){Clear();cursize s.c…

cgo与调用c的回调函数指针

cgo直接调用函数&#xff0c;使用基本数据类型非常简单&#xff0c;包括一些结构体也比较简单&#xff0c;嵌套的稍微复杂些&#xff0c;但也可以&#xff0c;但有的时候&#xff0c;cgo调用c函数&#xff0c;会需要传递一个回调函数的指针&#xff0c;这时候就比较复杂了&…

office365 outlook邮件无法删除

是否遇到过&#xff0c;office365邮件存储满了&#xff0c;删除邮件无法删除&#xff0c;即便用web方式登录到outlook&#xff0c;删除邮件当时是成功的&#xff0c;但一会儿就回滚回来了&#xff0c;已删除的邮件&#xff0c;你想清空&#xff0c;最后清理后还是回到原样。 请…

YTM32的循环冗余校验CRC外设模块详解

YTM32的循环冗余校验CRC外设模块详解 文章目录 YTM32的循环冗余校验CRC外设模块详解引言原理与机制CRC算法简介从CRC算法到CRC硬件外设 应用要点&#xff08;软件&#xff09;CRC16 用例CRC32 用例 总结参考文献 引言 在串行通信帧中&#xff0c;为了保证数据在传输过程中的完…

基于Python优化图片亮度与噪点

支持添加噪点类型包括&#xff1a;添加高斯噪点、添加椒盐噪点、添加波动噪点、添加泊松噪点、添加周期性噪点、添加斑点噪点、添加相位噪点&#xff0c;还提供清除噪点的功能。 我们先看一下实测效果&#xff1a;&#xff08;test.jpg为原图&#xff0c;new.jpg为添加后的图片…

自动化测试的成本高效果差,那么自动化测试的意义在哪呢?

一、自动化测试的成本高效果差&#xff0c;那么自动化测试的意义在哪呢&#xff1f; 我觉得这个问题带有很强的误导性&#xff0c;是典型的逻辑陷阱之一。“自动化测试的成本高效果差”是真的吗&#xff1f;当然不是。而且我始终相信&#xff0c;回答问题的最好方式是把问题本身…

达索系统3DEXPERIENCE WORKS 2024流体仿真功能增强

设计工作中&#xff0c;网格划分和设计验证十分重要&#xff0c;它可以方便我们把复杂组件简单化处理&#xff0c;从而提升工作效率。 轻松应对&#xff0c;精准划分 在优化设计以获得更好的空气动力学性能时&#xff0c;需要了解空气在其周围产生的流动方式。达索系统3DEXPE…

(论文阅读29/100 人体姿态估计)

29.文献阅读笔记 简介 题目 DeepCut: Joint Subset Partition and Labeling for Multi Person Pose Estimation 作者 Leonid Pishchulin, Eldar Insafutdinov, Siyu Tang, Bjoern Andres, Mykhaylo Andriluka, Peter Gehler, and Bernt Schiele, CVPR, 2016. 原文链接 h…

STM32 X-CUBE-AI:Pytorch模型部署全流程

文章目录 概要版本&#xff1a;参考资料STM32CUBEAI安装CUBEAI模型支持LSTM模型转换注意事项模型转换模型应用1 错误类型及代码2 模型创建和初始化3 获取输入输出数据变量4 获取模型前馈输出模型应用小结 小结 概要 STM32 CUBE MX扩展包&#xff1a;X-CUBE-AI部署流程&#xf…

Accelerate 0.24.0文档 一:两万字极速入门

文章目录 一、概述1.1 PyTorch DDP1.2 Accelerate 分布式训练简介1.2.1 实例化Accelerator类1.2.2 将所有训练相关 PyTorch 对象传递给 prepare()方法1.2.3 启用 accelerator.backward(loss) 1.3 Accelerate 分布式评估1.4 accelerate launch1.4.1 使用accelerate launch启动训…

k8s集群搭建(ubuntu 20.04 + k8s 1.28.3 + calico + containerd1.7.8)

环境&需求 服务器&#xff1a; 10.235.165.21 k8s-master 10.235.165.22 k8s-slave1 10.235.165.23 k8s-slave2OS版本&#xff1a; rootvms131:~# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.5 LTS Release: …

Java自学第11课:电商项目(4)重新建立项目

经过前几节的学习&#xff0c;我们已经找到之前碰到的问题的原因了。那么下面接着做项目学习。 1 新建dynamic web project 建立时把web.xml也生成下&#xff0c;省的右面再添加。 会询问是否改为java ee环境&#xff1f;no就行&#xff0c;其实改过来也是可以的。这个不重要。…