时间复杂度--概念

目录

基本操作执行的次数T(n)

渐近时间复杂度( asymptotic time complexity)


算法执行时间是我们改进算法的重要依据。算法执行时间需通过依据该算法编制的程序在计算机上运行时所消耗的时间来度量,一般有两种方法:

1、计算机运行 + 计时

2、运算前分析

 显然,同一个算法用不同的语言实现,或者用不同的编译程序进行编译,或者在不同的计算机上运行时,效率均不同。这表明使用绝对的时间单位衡量算法的效率是不合适的。撇开这些与计算机硬件、软件相关的因素,可以认为一个特定算法运行工作量的大小,只依赖于问题的规模(通常用整数n表示),或者说,它是问题规模的函数。

一个算法是由控制结构(顺序、分之和循环3种)和原操作(指固有数据类型的操作)构成的,则算法取决于两者的综合效果。为了便于比较同一问题的不同算法,从算法中选取一种对于所研究的问题来说是基本操作的原操作,以该基本操作重复执行的次数作为算法的时间度量。

基本操作执行的次数T(n)

T(n)描述了一个算法中的基本操作执行的次数。我们可以举几个例子来具体感受T(n)的计算方法。

情景1: 线性

假设我们有 一条长10寸的面包,每3天吃掉1寸,那么吃掉整个面包需要几天?答案是 3 X 10 = 30天。如果面包的长度是 n 寸呢?此时吃掉整个面包,需要 3 X n = 3n 天。如果用一个函数来表达这个相对时间,可以记作 T(n) = 3n。

情景2: 对数

一条长16寸的面包,每5天吃掉面包剩余长度的一半, 第一次吃掉8寸,第二次吃掉4寸,第三次吃掉2寸......那么把面包吃得只剩下1寸,需要多少天呢?

这个问题翻译一下,就是数字16不断地除以2,除几次以后的结果等于1?这里要涉及到数学当中的对数,以2位底,16的对数,可以简写为log16。因此,把面包吃得只剩下1寸,需要 5 X log16 = 5 X 4 = 20 天。如果面包的长度是 N 寸呢?需要 5 X logn = 5logn天,记作 T(n) = 5logn。

情景3: 常量

 一条长10寸的面包和一个鸡腿,每2天吃掉一个鸡腿。那么吃掉整个鸡腿需要多少天呢?答案自然是2天。因为只说是吃掉鸡腿,和10寸的面包没有关系 。如果面包的长度是 N 寸呢?无论面包有多长,吃掉鸡腿的时间仍然是2天,记作 T(n) = 2。

情景4:多项式

一条长10寸的面包,吃掉第一个一寸需要1天时间,吃掉第二个一寸需要2天时间,吃掉第三个一寸需要3天时间.....每多吃一寸,所花的时间也多一天。那么吃掉整个面包需要多少天呢?答案是从1累加到10的总和,也就是55天。如果面包的长度是 N 寸呢?此时吃掉整个面包,需要 1+2+3+......+ n-1 + n = (1+n)*n/2 = 0.5n^2 + 0.5n。记作 T(n) = 0.5n^2 + 0.5n。

上面的这些情景,暂时没有讨论控制结构,只考虑了源操作,其中“吃”就是原操作,面包的长度n就是问题的规模。随着n的改变,T(n)也随之变化,如果无法事先知道不同算法n的区别,我们也无法使用时间频度 T(n)来判断算法的复杂程度。这是就引入了渐近时间复杂度的概念。

渐近时间复杂度( asymptotic time complexity)

定义:若存在函数 f(n),使得当n趋近于无穷大时,T(n)/ f(n)的极限值为不等于零的常数,则称 f(n)是T(n)的同数量级函数。记作 T(n)= O( f(n) ),称O( f(n) )为算法的渐近时间复杂度,简称时间复杂度。渐进时间复杂度用大写O来表示,所以也被称为大O表示法。

    其实就跟高数中的同阶无穷小的定义类似,渐近时间复杂度旨在描述在n-->无穷时,时间频度的增长速度。时间复杂度O( f(n) )将原先具体的时间度量T(n)简化成了一个“数量级”,也就是增加速度的量化。需要注意的是, f(n)函数并不需要特别详细的表达式,因为我们往往只需要定性的知道计算成本增长速度的趋势就足以判断算法的时间复杂度

    所以如何计算出时间复杂度,首先有几点原则:

    1) 如果运行时间是常数量级,用常数1表示

    2) 只保留时间函数中的最高阶项

    3) 如果最高阶项存在,则省去最高阶项前面的系数

我们再举几个例子来动手实际地算一算。大O记号满足\exists\; c>0,n>>2,T(n)<c\cdot f(n)

假设我们已经得到了多项式时间的T(n),则T(n) = \sqrt{5n\cdot \left [ 3n\cdot (n+2)+4 \right ]+6}       2 --> n得

                                                                               <\sqrt{5n\cdot (6n^{2} + 4)+6}                    4 --> n^2得

                                                                               <\sqrt{35n^{3}+6}                                    6 --> n^3得

                                                                               <6\cdot n^{1.5}                                            忽略常系数得

                                                                               =O(n^{1.5})

邓俊辉老师在视频中提到了两个概念:“长远”、“主流”。前者表示我们的眼光需要放在n趋向于无穷的情况,因为现在问题的规模通常比较大。在上面的情景中,如果n=3, 那么2^n < n^2,然而当n增加,指数级增加是无法忍受的。后者是指要关注使得计算复杂度上升的主要原因。有时候T(n)中有多项式也有常数项,相比之下常数项就可以忽略。

由上面的图我们也可以看出大O记号的实际意义:在最坏情况下,计算复杂度随着问题规模n增加而增长的速度。为什么要称之为“渐近”?因为正如上文所说“f(n)函数并不需要特别详细的表达式”,然而也不能无限制的放大(这里的放大是指类似多项式次数的参数,而不是前面的系数T(n) < c · f(n)中的c。具体的差别这里不讨论)。

举个例子:打车去车站,问师傅:“还有多久到啊?”。 师傅说:“堵车啦,再过个60分钟吧”。然而事实是就算堵车也只要10分钟就能到。这里10分钟就比60分钟更加精准(贴近事实)地描述了真相。如T(n) = 4n^3 + 3^n2,他的渐近时间复杂度可以是O(n^4)甚至是O(n^5),然而还是O(n^3)最贴切

其中还有一类比较特殊的复杂度:指数复杂度O(2^n)。这一类算法的计算成本增长极快,通常是不可忍受的。从多项式复杂度O(n^c)到指数复杂度O(2^n),是有效算法到无效算法的分水岭。

例如,给定集合S包含n个正整数,这n个数的和为2m。问是否存在子集T,满足T中所有数的和为m?我们第一想法是穷举所有子集,计算子集中所有元素的和,知道找出和为m的子集。然而这种方法的时间复杂度为O(2^n),且这个子集划分问题本身就是NPC问题。

下图,显示出了当模型规模n趋向正无穷是,各种时间复杂度增长的趋势:

可以看出在算法规模越来越大,不同时间复杂度的增长速度有着质的差别。

写在最后

这篇博客记录一下时间复杂度的概念,并不是以前简单理解的“计算次数”。后面会结合具体的算法复杂度分析来进一步理解时间复杂度对算法的重要意义。

 

参考博客:

一图搞懂时间复杂度

《数据结构与算法Python语言实现》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值