算法性能分析

文章详细阐述了算法复杂度中的时间复杂度和空间复杂度,包括时间复杂度的公式、如何求解、从硬件分析的时间复杂度测试,以及递归算法的时间复杂度分析。同时,提到了空间复杂度并不等于程序执行文件的大小,而是运行时内存占用的估计。通过对不同复杂度级别的算法进行测试,展示了它们在实际运行时的表现。
摘要由CSDN通过智能技术生成

算法复杂度


简单来说,算法复杂度就是用来描述程序运行所花费的时间和空间,因此又分为了时间复杂度和空间复杂度。

1、时间复杂度

1.1 时间复杂度公式

时间复杂度可以说是一个函数,它定性描述了算法的运行时间。这里假设算法的问题规模为n,使用函数f(n)表示算法执行时每条语句执行的次数之和,则有时间复杂度

T(n)=O(f(n))

那‘大O’又是什么含义呢???
这里我理解为 大O 则表示正比例关系,即算法执行时间的增长率和f(n)的增长率相同,我们称这个公式为算法的渐进时间复杂度,不过根据算法导论中解释:大O表示上界,用它表示算法的最坏运行情况的上界,是对任意数据输入后运行时间的上界。

这里列举插入排序和快速排序的例子:插入排序的时间复杂度是O( n 2 n^2 n2),快速排序的时间复杂度是O( n l o g n nlogn nlogn),但是这都是一般情况下,因为输入数据不同,从而会导致运行时间不一样。对于插入排序,当输入数据有序时,时间复杂度为O( n n n),但如果是逆序时,时间复杂度为O( n 2 n^2 n2)。同理,对于快速排序,当数据有序时,时间复杂度为O( n 2 n^2 n2),当数据无序时,时间复杂度为O( n l o g n nlogn nlogn)。日常来说我们更关注于一般情况,其次考虑的是最坏的情况。

但是并不是时间复杂度越低越好,因为简化后省略了常数项,主要根据输入的数据规模。这里假设数据规模极大为n,则有以下排列(a,b为常数):

O(1)常数阶<O(logn)对数阶<O(n)线性阶<O(n^a)次方阶<O(b^n)指数阶

如果常数足够大哦,超过了10的五次方则不能随意忽视

1.2 常见问题

如何求解复杂度公式?

这里可以分为三步:

  1. 去除加法的常数项
  2. 去除常数系数
  3. 保留最高项
O(logn)的log以什么为底?

这里的底数并没有定值,可以为2、3、4、10等等,同时表示忽略底数。我们可以根据对数的数学公式来理解:

log ⁡ i n \log_i^n login= log ⁡ i j ⋅ log ⁡ j n \log_i^j\cdot\log_j^n logijlogjn

这里公式右端的前部分 log ⁡ i j \log_i^j logij可以看成常数,即所有的log函数都可以化简成同一个底数的对数,因此可以忽略底数。

1.3 从硬件分析时间复杂度

计算机的运算速度主要取决于自身的配置,这里以自己的CPU为例,
CPU:Intel® Core™ i7-8550U CPU @ 1.80GHz 2.00 GHz,这里有两个频率,一个为主频1.80GHz,一个为最大睿频2.00Hz,前者可以理解为平均值,后者可以理解为最大值。1Hz=1/s,即1Hz为电脑的一次脉冲(可以理解为改变一次电平的高低状态),称为赫兹, 1 G H z = 1 0 9 H z 1GHz=10^9Hz 1GHz=109Hz,即一秒可以改变10亿次状态。但是一台电脑的CPU需要运行多个进程,因此我们的程序只会占用少部分的CPU,下面通过一些测试检验1s内cpu可以处理的数据的数量级。

测试用例

这里使用我自己的电脑运行,分别计算时间复杂度为 O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( n log ⁡ n ) O(n\log n) O(nlogn)的程序,并用加法统一测试,以下分别为对应的三段代码:

// O(n)
void function1(long long n) {
    long long k = 0;
    for (long long i = 0; i < n; i++) {
        k++;
    }
}

O ( n ) O(n) O(n)的结果为:

input: 100000000
consume: 219 ms
input: 10000000000
consume: 22208 ms
input: 1000000000
consume: 2157 ms
input: 500000000
consume: 1084 ms

// O(n^2)
void function2(long long n) {
    long long k = 0;
    for (long long i = 0; i < n; i++) {
        for (long j = 0; j < n; j++) {
            k++;
        }
    }
}

O ( n 2 ) O(n^2) O(n2)的结果为:

input: 100000
consume: 16360 ms
input: 1000
consume: 2 ms
input: 10000
consume: 164 ms
input: 50000
consume: 4093 ms

// O(nlogn)
void function3(long long n) {
    long long k = 0;
    for (long long i = 0; i < n; i++) {
        for (long long j = 1; j < n; j = j*2) { // 注意这里j=1
            k++;
        }
    }
}

O ( n log ⁡ n ) O(n\log n) O(nlogn)的结果为:

input: 10000
consume: 1 ms
input: 1000000
consume: 53 ms
input: 100000000
consume: 6312 ms
input: 10000000
consume: 558 ms

综合以上测试这里可以检测得到1s内可以处理的数据的数量级:

时间复杂度数量级
O ( n ) O(n) O(n) 1 0 8 10^8 108
O ( n 2 ) O(n^2) O(n2) 1 0 4 10^4 104
O ( n log ⁡ n ) O(n\log n) O(nlogn) 1 0 7 10^7 107

这里附上主函数的代码:

int main() {
    long long n; // 数据规模
    while (1) {
        cout << "input: ";
        cin >> n;
        clock_t start,ends;		//定义一个clock_t的数据,可以精确到ms,需要应用time.h库
        start=clock();			//开始时间
        function1(n);		//调用对应时间复杂度的函数
        ends=clock();		//结束时间
        cout << "consume: " << ends - start << " ms"<< endl;
    }
}

1.4 从内存分析时间复杂度

理解代码的内存消耗,可以更有利于对算法的选择。而不同的编程语言都有各自的内存管理方式

  • C/C++语言的内存堆空间的申请和释放完全依靠自己管理
  • Java语言依赖JVM来管理内存,对JVM机制的不熟悉容易造成内存泄漏或者内存溢出
  • Python语言的内存管理是由私有堆空间负责的,所有的Python对象和数据结构都存在私有堆空间中,因此python的基本数据类型所用的内存会远大于存放数据类型所占的内存,而且采用了垃圾回收机制和内存计数机制管理内存。

这里以C++为例,介绍一下它的内存管理,可以分为两部分——固定部分和可变部分。如下图:
在这里插入图片描述
这里主要考虑可变部分的内存占用情况,而栈区会系统自动回收,因此只需关注堆区,在分配内存时就应该考虑内存的回收,防止内存泄漏(程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果)。
由于CPU读取内存不是一次读取单个字节,而是一块一块的来读取内存,块的大小可以是2,4,8,16个字节,具体取多少个字节取决于硬件。这也就产生了内存对齐的方式,那为什么会采用内存对齐的方式呢?
* 方便不同的硬件平台都可以正常访问对应地址的数据
* 方便CPU对内存数据的读取

1.5 递归算法时间复杂度的分析

递归算法的时间复杂度关键是看:递归的次数*每次递归的操作次数,但是不同的递归写法会耗费不同的时间,因此优化递归非常重要,即用空间换时间策略。

2、空间复杂度

空间复杂度是一个程序在运行时所占用内存空间的量度,记作:

S(n)=O(f(n))

2.1 常见问题

空间复杂度是考虑程序(可执行文件)占有的空间吗?

不是的,空间复杂度是考虑程序运行时占用内存的大小,而不是可执行文件的大小

空间复杂度表示的就是程序运行占用的内存吗?

不是的,程序运行时占用的内存受很多因素影响,空间复杂度只是在程序运行之前对占用内存的一个预估。

总结

本篇文章只是对时间和空间复杂度进行了一个分析,具体的熟悉掌握还需要不断的具体实例的练习。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Zero3_msh

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

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

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

打赏作者

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

抵扣说明:

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

余额充值