如何衡量程序运行的效率

文章目录

  • 复杂度是什么
  • 不同算法对复杂度的影响
    • 方法一
    • 方法二
  • 时间复杂度与代码结构的关系
    • 例 1:最大值
    • 例2:最大次数
  • 降低时间复杂度的必要性
  • 总结

当你在大数据环境中开发代码时,你一定遇到过程序执行好几个小时、甚至好几天的情况,或者是执行过程中电脑几乎死机的情况:

  • 如果这个效率低下的系统是离线的,那么它会让我们的开发周期、测试周期变得很长。
  • 如果这个效率低下的系统是在线的,那么它随时具有时间爆炸或者内存爆炸的可能性。

因此,衡量代码的运行效率对于一个工程师而言,是一项非常重要的基本功。本课时我们就来学习程序运行效率相关的度量方法。

复杂度是什么

复杂度是衡量代码运行效率的重要的度量因素。在介绍复杂度之前,有必要先看一下复杂度和计算机实际任务处理效率的关系,从而了解降低复杂度的必要性。

计算机通过一个个程序去执行计算任务,也就是对输入数据进行加工处理,并最终得到结果的过程。每个程序都是由代码构成的。可见,编写代码的核心就是要完成计算。但对于同一个计算任务,不同计算方法得到结果的过程复杂程度是不一样的,这对你实际的任务处理效率就有了非常大的影响。

举个例子,你要在一个在线系统中实时处理数据。假设这个系统平均每分钟会新增 300M 的数据量。如果你的代码不能在 1 分钟内完成对这 300M 数据的处理,那么这个系统就会发生时间爆炸和空间爆炸。表现就是,电脑执行越来越慢,直到死机。因此,我们需要讲究合理的计算方法,去通过尽可能低复杂程度的代码完成计算任务。

那提到降低复杂度,我们首先需要知道怎么衡量复杂度。而在实际衡量时,我们通常会围绕以下2 个维度进行。首先,这段代码消耗的资源是什么。 一般而言,代码执行过程中会消耗计算时间和计算空间,那需要衡量的就是时间复杂度和空间复杂度。

我举一个实际生活中的例子。某个十字路口没有建立立交桥时,所有车辆通过红绿灯分批次行驶通过。当大量汽车同时过路口的时候,就会分别消耗大家的时间。但建了立交桥之后,所有车辆都可以同时通过了,因为立交桥的存在,等于是消耗了空间资源,来换取了时间资源。

其次,这段代码对于资源的消耗是多少。 我们不会关注这段代码对于资源消耗的绝对量,因为不管是时间还是空间,它们的消耗程度都与输入的数据量高度相关,输入数据少时消耗自然就少。为了更客观地衡量消耗程度,我们通常会关注时间或者空间消耗量与输入数据量之间的关系。

好,现在我们已经了解了衡量复杂度的两个纬度,那应该如何去计算复杂度呢?

复杂度是一个关于输入数据量 n 的函数。假设你的代码复杂度是 f(n),那么就用个大写字母 O 和括号,把 f(n) 括起来就可以了,即 O(f(n))。例如,O(n) 表示的是,复杂度与计算实例的个数 n 线性相关;O(logn) 表示的是,复杂度与计算实例的个数 n 对数相关。

通常,复杂度的计算方法遵循以下几个原则:

  • 首先,复杂度与具体的常系数无关,例如 O(n) 和 O(2n) 表示的是同样的复杂度。我们详细分析下,O(2n) 等于 O(n+n),也等于 O(n) + O(n)。也就是说,一段 O(n) 复杂度的代码只是先后执行两遍 O(n),其复杂度是一致的。
  • 其次,多项式级的复杂度相加的时候,选择高者作为结果,例如 O(n²)+O(n) 和 O(n²) 表示的是同样的复杂度。具体分析一下就是,O(n²)+O(n) = O(n²+n)。随着 n 越来越大,二阶多项式的变化率是要比一阶多项式更大的。因此,只需要通过更大变化率的二阶多项式来表征复杂度就可以了。

值得一提的是,O(1) 也是表示一个特殊复杂度,含义为某个任务通过有限可数的资源即可完成。此处有限可数的具体意义是,与输入数据量 n 无关

例如,你的代码处理 10 条数据需要消耗 5 个单位的时间资源,3 个单位的空间资源。处理 1000 条数据,还是只需要消耗 5 个单位的时间资源,3 个单位的空间资源。那么就能发现资源消耗与输入数据量无关,就是 O(1) 的复杂度。

不同算法对复杂度的影响

为了方便你理解不同计算方法对复杂度的影响,我们来看一个代码任务:对于输入的数组,输出与之逆序的数组。例如,输入 a=[1,2,3,4,5],输出 [5,4,3,2,1]。

方法一

建立并初始化数组 b,得到一个与输入数组等长的全零数组。通过一个 for 循环,从左到右将 a 数组的元素,从右到左地赋值到 b 数组中,最后输出数组 b 得到结果。

代码如下:

public class s1_1 {public static void main(String[] args) {int a[] = {1,2,3,4,5};int b[] = new int[5];for (int i=0;i<a.length;i++){b[b.length-i-1] = a[i];}System.out.println(Arrays.toString(b));}
}

这段代码的输入数据是 a,数据量就等于数组 a 的长度。代码中有一个 for 循环,作用是赋值,其执行次数都与输入数据量相等。因此,代码的时间复杂度就是 O(n)。

空间方面主要体现在计算过程中,对于存储资源的消耗情况。上面这段代码中,我们定义了一个新的数组 b,它与输入数组 a 的长度相等。因此,空间复杂度就是 O(n)。

方法二

它定义了缓存变量 tmp,接着通过一个 for 循环,从 0 遍历到a 数组长度的一半(即 len(a)/2)。每次遍历执行的是什么内容?就是交换首尾对应的元素。最后打印数组 a,得到结果。

代码如下:

public class s1_2 {public static void main(String[] args) {int a[] = {1,2,3,4,5};for (int i=0;i<=a.length/2;i++){int tmp = a[i];a[i] = a[a.length-i-1];a[a.length-i-1] = tmp;}System.out.println(Arrays.toString(a));}
}

这段代码包含了一个 for 循环,执行的次数是数组长度的一半,时间复杂度变成了 O(n/2)。根据复杂度与具体的常系数无关的性质,这段代码的时间复杂度也就是 O(n)。

空间方面,我们定义了一个 tmp 变量,它与数组长度无关。也就是说,输入是 5 个元素的数组,需要一个 tmp 变量;输入是 50 个元素的数组,依然只需要一个 tmp 变量。因此,空间复杂度与输入数组长度无关,即 O(1)。

可见,对于同一个问题,采用不同的编码方法,对时间和空间的消耗是有可能不一样的。因此,工程师在写代码的时候,一方面要完成任务目标;另一方面,也需要考虑时间复杂度和空间复杂度,以求用尽可能少的时间损耗和尽可能少的空间损耗去完成任务。

时间复杂度与代码结构的关系

好了,通过前面的内容,相信你已经对时间复杂度和空间复杂度有了很好的理解。从本质来看,时间复杂度与代码的结构有着非常紧密的关系;而空间复杂度与数据结构的设计有关,关于这一点我们会在下一讲进行详细阐述。接下来我先来系统地讲一下时间复杂度和代码结构的关系。

代码的时间复杂度,与代码的结构有非常强的关系,我们一起来看一些具体的例子。

例 1:最大值

定义了一个数组 a = [1, 4, 3],查找数组 a 中的最大值

代码如下:

public class s1_3 {public static void main(String[] args) {int a[] = {1,4,3};int max_value = a[0];for (int i=0;i<a.length;i++){if (a[i]>max_value){max_value = a[i];}}System.out.println(max_value);}
}

这个例子比较简单,实现方法就是,暂存当前最大值并把所有元素遍历一遍即可。因为代码的结构上需要使用一个 for 循环,对数组所有元素处理一遍,所以时间复杂度为 O(n)。

例2:最大次数

下面的代码定义了一个数组 a = [1, 3, 4, 3, 4, 1, 3],并会在这个数组中查找出现次数最多的那个数字

代码如下:

public class s1_4 {public static void main(String[] args) {int a[] = {1, 3, 4, 3, 4, 1, 3};int count;int max_count = 1;int max_count_index = 0;for (int i=0;i<a.length;i++){count = 1;for (int j=i+1;j<a.length;j++){if (a[i] == a[j]){count++;}}if (count >max_count){max_count = count;max_count_index = i;}}System.out.println(a[max_count_index]+"出现的次数:"+max_count);}
}

这段代码中,我们采用了双层循环的方式计算:第一层循环,我们对数组中的每个元素进行遍历;第二层循环,对于每个元素计算出现的次数,并且通过当前元素次数 count和全局最大次数变量 max_count的大小关系,持续保存出现次数最多的那个元素及其出现次数。由于是双层循环,这段代码在时间方面的消耗就是 n*n 的复杂度,也就是 O(n²)。

在这里,我们给出一些经验性的结论:

  • 一个顺序结构的代码,时间复杂度是 O(1)。
  • 二分查找,或者更通用地说是采用分而治之的二分策略,时间复杂度都是 O(logn)。这个我们会在后续课程讲到。
  • 一个简单的 for 循环,时间复杂度是 O(n)。
  • 两个顺序执行的 for 循环,时间复杂度是 O(n)+O(n)=O(2n),其实也是 O(n)。
  • 两个嵌套的 for 循环,时间复杂度是 O(n²)。

有了这些基本的结论,再去分析代码的时间复杂度将会轻而易举。

降低时间复杂度的必要性

实际的在线环境中,用户的访问请求可以看作一个流式数据。假设这个数据流中,每个访问的平均时间间隔是 t。如果你的代码无法在 t 时间内处理完单次的访问请求,那么这个系统就会一波未平一波又起,最终被大量积压的任务给压垮。这就要求工程师必须通过优化代码、优化数据结构,来降低时间复杂度。

为了更好理解,我们来看一些数据。假设某个计算任务需要处理 10万 条数据。你编写的代码:

  • 如果是 O(n²) 的时间复杂度,那么计算的次数就大概是 100 亿次左右。
  • 如果是 O(n),那么计算的次数就是 10万 次左右。
  • 如果这个工程师再厉害一些,能在 O(log n) 的复杂度下完成任务,那么计算的次数就是 17 次左右(log 100000 = 16.61,计算机通常是二分法,这里的对数可以以 2 为底去估计)。

数字是不是一下子变得很悬殊?通常在小数据集上,时间复杂度的降低在绝对处理时间上没有太多体现。但在当今的大数据环境下,时间复杂度的优化将会带来巨大的系统收益。

总结

复杂度通常包括时间复杂度和空间复杂度。在具体计算复杂度时需要注意以下几点。

  1. 它与具体的常系数无关,O(n) 和 O(2n) 表示的是同样的复杂度。
  2. 复杂度相加的时候,选择高者作为结果,也就是说 O(n²)+O(n) 和 O(n²) 表示的是同样的复杂度。
  3. O(1) 也是表示一个特殊复杂度,即任务与算例个数 n 无关。

复杂度细分为时间复杂度和空间复杂度,其中时间复杂度与代码的结构设计高度相关空间复杂度与代码中数据结构的选择高度相关

人之为学,不可自小,又不可自大

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Lunix的奇妙冒险————权限篇

文章目录 一.什么是权限二.用户权限和类别。1.用户2.角色3.更换文件角色 三.文件的类别和对应权限1.文件的类别。2.文件属性权限1.权限说明。2.默认生成文件权限来源3.更改权限 3.文件的执行与删除 四.不同用户共同在一个目录下的权限。1.普通用户家目录2.在同一目录下文件的权…

LabVIEW的便携式车辆振动测试分析

随着计算机和软件技术的发展&#xff0c;虚拟仪器正逐渐成为机械工业测试领域的主流。在现代机械工程中&#xff0c;特别是车辆振动测试&#xff0c;传统的测试方法不仅设备繁杂、成本高昂&#xff0c;而且操作复杂。为解决这些问题&#xff0c;开发了一款基于美国国家仪器公司…

【新版Hi3559AV100 旗舰8K30 AI摄像机芯片】

新版Hi3559AV100 旗舰8K30 AI摄像机芯片 一、总体介绍 Hi3559AV100是专业的8K Ultra-HD Camera SOC&#xff0c;它提供了8K30/4K120广播级图像质量的数字视频录制&#xff0c;支持8路Sensor输入&#xff0c;支持H.265编码输出或影视级的RAW数据输出&#xff0c;并集成高性能ISP…

proE各版本安装指南

下载链接 https://pan.baidu.com/s/1BSaJxvPPGeIa4YKm7xk57g?pwd0531 1.鼠标右击【Proe5.0M280(64bit)】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;选择【解压到 Proe5.0M280(64bit)】&#xff08;解压的路径中不能有中文&#xff09;。 2.打开…

HarmonyOS应用配置文件app对象内部结构,deviceConfig内部结构(FA模型)

应用配置文件概述&#xff08;FA模型&#xff09; 每个应用项目必须在项目的代码目录下加入配置文件&#xff0c;这些配置文件会向HarmonyOS的编译工具、HarmonyOS操作系统和应用市场提供描述应用的基本信息。 应用配置文件需申明以下内容&#xff1a; 应用的软件包名称&…

存算分离降本增效,StarRocks 助力聚水潭 SaaS 业务服务化升级

作者&#xff1a;聚水潭数据研发负责人 溪竹 聚水潭是中国领先的 SaaS 软件服务商&#xff0c;核心产品是电商 ERP&#xff0c;协同350余家电商平台&#xff0c;为商家提供综合的信息化、数字化解决方案。公司是偏线下商家侧的 toB 服务商&#xff0c;员工人数超过3500&#xf…

Arthas常用命令

Arthas常用命令 help 显示Arthas帮助auth 对当前会话进行身份验证keymap 显示指定连接的所有可用的keymap。sc 搜索JVM加载的所有类sm 搜索JVM加载类的方法classloader 显示classloader信息jad 分解类getstatic 显示类的静态字段monitor 监…

Ubuntu安装K8S(1.28版本,基于containrd)

原文网址&#xff1a;Ubuntu安装K8S(1.28版本&#xff0c;基于containrd&#xff09;-CSDN博客 简介 本文介绍Ubuntu安装K8S的方法。 官网文档&#xff1a;这里 1.安装K8S 1.让apt支持SSL传输 sudo apt-get update sudo apt-get -y install apt-transport-https ca-certi…

计算机图形学光线追踪大作业C++基于Optix为框架实现的光线追踪算法合集,含直射光阴影效果、漫反射阴影效果、镜面反射效果等示例

MineRay 使用Optix为框架实现的光线追踪算法。 包含4个示例&#xff0c;直射光阴影效果、漫反射阴影效果、镜面反射效果、折射效果 环境需求 本项目在Windows 10中测试&#xff0c;以下环境为Windows中的环境 CUDA 10.1 OptiX 7 SDK cmake 编译方式 使用cmake编译 打开Mi…

TCP连接数据包解析

1、TCP的SYN标志位&#xff0c;它表示一个TCP连接的初始同步请求。这条信息描述了一个从端口184到端口80的TCP连接请求&#xff0c;其中包含了一些TCP连接的参数和标志位。这条信息的各个部分内容&#xff1a; 184**->80**: 这表示源端口是184**&#xff0c;目标端口是80**…

nbuntu 18.04 终端打开后无内容

1. 问题 2. 删除bash并重新安装 删除&#xff1a; sudo rm /bin/bash &#xff08;https://blog.csdn.net/u011128515/article/details/22896837&#xff09; 再安装&#xff1a; bash文件电脑中是有下载的deb文件的&#xff0c;按上图路径找到了并下载了出来 ar t bash_4.4…

反转链表、链表的中间结点、合并两个有序链表(leetcode 一题多解)

一、反转链表 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 思路一&#xff1a;翻转单链表指针方向 这里解释一下三个指针的作用&#xff1a; n1&#xff1…

如何在无公网IP环境下远程访问Serv-U FTP服务器共享文件

文章目录 1. 前言2. 本地FTP搭建2.1 Serv-U下载和安装2.2 Serv-U共享网页测试2.3 Cpolar下载和安装 3. 本地FTP发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 科技日益发展的今天&#xff0c;移动电子设备似乎成了我们生活的主角&#xff0c;智能…

英飞凌TC3xx之一起认识GTM系列(三)重点说一说GTM中断

英飞凌TC3xx之一起认识GTM系列(三)重点说一说GTM中断 GTM中断电平中断模式脉冲中断模式脉冲通知中断模式(常用)单脉冲中断模式GTM中断集中方法GTM中断对比GTM中断寄存器ATOM中断配置INT_TRIGxIRQ_NOTIFYIRQ_ENIRQ_FORCINTIRQ_MODETIM中断配置

Linux文件和目录管理命令---- tail 命令

Linux 中的 tail 命令是一个非常实用的工具,主要用于查看文本文件的最后部分。下面将详细介绍 tail 命令的不同用法 1. 基本用法 tail 命令最常见的用法是显示文件的最后几行。 命令: tail filename.txt结果: 这将显示 filename.txt 文件的最后 10 行。 2. 指定行数 可…

Java正则表达式

本文主要描述Java正则表达式&#xff08;Regular Expression&#xff09;&#xff0c;其作用是预先定义一个规则&#xff0c;然后&#xff0c;使用该规则匹配输入的字符串是否符合定义的规则&#xff0c;也可以从匹配的输出中提取字符串&#xff0c;正则表达式的常用使用场景包…

SSH是什么?有什么使用场景。

—ChatGPT-3.5 SSH&#xff08;Secure Shell&#xff09;是一种用于在网络上安全传输数据的协议。它主要用于在不安全的网络中提供加密的通信渠道&#xff0c;以防止窃听和数据篡改。SSH最初是为替代不安全的Telnet和FTP而设计的&#xff0c;但它现在被广泛用于安全地连接和管…

【Electron】富文本编辑器之文本粘贴

由于这个问题导致&#xff0c;从其他地方复制来的内容 粘贴发送之后都会多一个 换行 在发送的时候如果直接&#xff0c;发送innerHTML 就 可以解决 Electron h5 Andriod 都没问题&#xff0c;但是 公司的 IOS 端 不支持&#xff0c;且不提供支持&#xff08;做不了。&#xff…

UE5.1_移动端运行问题梳理

UE5.1_移动端运行问题梳理 目录 ue_移动端运行问题梳理 问题1.

Unity 旋转跟随

Unity 使用任意一个局部轴指向目标 效果&#xff1a; 主要用于在编辑器中可视化对象的朝向&#xff0c;同时提供了选择不同轴向的功能。在运行时&#xff0c;物体将根据所选择的轴向朝向目标&#xff0c;并在 Scene 视图中绘制一个带箭头的圆环。 定义轴向枚举&#xff1a;…