数据结构-算法分析


用时间来度量一个算法的优劣。

实验研究

如果算法已经实现,
在不同的输入下,执行算法并记录每一次执行所花费的时间。

我们可以利用time模块中的time()函数来记录时间。

存在的问题

  1. 很难直接比较两个算法的运行时间,需要满足:
    a. 实验在相同的硬件和软件环境中执行;
    b. 相同的CPU活动情况。
  2. 实验只有在有限的一组测试输入下才能完成。忽略了输入数据的规模(超多数据);忽略了输入数据的质量(最坏情况)。
  3. 算法必须完全实现。在算法设计初期,我们并不知道算法的质量。

进一步实验分析

寻找新的评估时间的方法,它需要满足以下要求:

  1. 在软硬件环境独立的情况下,在某种程度上允许我们评价任意两个算法的相对效率;
  2. 通过研究不需要实现的高层次算法描述来执行算法;
  3. 考虑所有可能的输入。
计算原子操作

原子操作有:

  1. 给对象指定一个标识符;
  2. 确定与这个标识符相关联的对象;
  3. 执行算术运算;
  4. 比较两个数的大小;
  5. 通过索引访问列表的一个元素;
  6. 调用函数(不包括函数内的操作执行);
  7. 从函数返回。
    执行时间是常数时间。
    度量原子操作数量。
    原子操作数与真实运行时间成正比。
最坏情况输入的研究

对于不同的输入,倘若计划用平均运行时间进行分析的话,是具有挑战性的。这要求定义输入的概率分布,从而计算数学期望。

因此,我们分析最坏情况分析。最坏情况分析的设计使得算法更加健壮。

7种常用函数

将算法的时间复杂度与函数联系起来。
我们把原子操作数量描述为输入大小为n的函数 f ( n ) f(n) f(n)

常数函数

f ( n ) = c f(n)=c f(n)=c
描述了在计算机上需要做的基本操作步数。

对数函数

x = log ⁡ b n x = \log_bn x=logbn
许多算法中的常见操作是反复把一个输入分成两半。

线性函数

f ( n ) = n f(n)=n f(n)=n
这个函数出现在我们必须对n各元素做基本操作的算法分析的任何时间。

nlogn函数

f ( n ) = n log ⁡ n f(n)=n\log n f(n)=nlogn

二次函数

f ( n ) = n 2 f(n)=n^2 f(n)=n2
常出现在嵌套当中。

三次函数和其他多项式

f ( n ) = n 3 f(n)=n^3 f(n)=n3
常出现在嵌套当中。
f ( n ) = a 0 + . . . + a d n d f(n)=a_0+...+a_dn^d f(n)=a0+...+adnd
常出现在等差数列求和问题当中。

指数函数

f ( n ) = b n f(n)=b^n f(n)=bn
常出现在等比数列求和问题当中。

比较增长率

由大到小依次为:
指数函数,三次函数,二次函数,nlogn,线性函数,对数函数,常数函数。

渐进分析

用数学符号来分析算法。

大O符号(上限)

f ( n ) f(n) f(n) g ( n ) g(n) g(n)作为正整数映射到正实数的函数。如果存在实数常量 c > 0 c>0 c>0和整型常量 n 0 ≥ 1 n_0 \ge 1 n01满足 f ( n ) ≤ c g ( n ) , w h i l e   n ≥ n 0 f(n)\le cg(n),while~n\ge n_0 f(n)cg(n),while nn0
我们就说 f ( n )   i s   O ( g ( n ) ) f(n)~is~O(g(n)) f(n) is O(g(n))

大O符号蕴含一种小于等于的意思。

Ω \Omega Ω符号(下限)

f ( n ) f(n) f(n) g ( n ) g(n) g(n)作为正整数映射到正实数的函数。如果存在实数常量 c > 0 c>0 c>0和整型常量 n 0 ≥ 1 n_0 \ge 1 n01满足 f ( n ) ≥ c g ( n ) , w h i l e   n ≥ n 0 f(n)\ge cg(n),while~n\ge n_0 f(n)cg(n),while nn0
我们就说 f ( n )   i s   Ω ( g ( n ) ) f(n)~is~\Omega(g(n)) f(n) is Ω(g(n))

Θ \Theta Θ符号

f ( n ) f(n) f(n) g ( n ) g(n) g(n)作为正整数映射到正实数的函数。如果存在实数常量 c ‘ > 0 , c ′ ′ > 0 c‘>0,c''>0 c>0,c>0和整型常量 n 0 ≥ 1 n_0 \ge 1 n01满足 c ′ g ( n ) ≤ f ( n ) ≤ c ′ ′ g ( n ) , w h i l e   n ≥ n 0 c'g(n) \le f(n)\le c''g(n),while~n\ge n_0 cg(n)f(n)cg(n),while nn0
我们就说 f ( n )   i s   Θ ( g ( n ) ) f(n)~is~\Theta(g(n)) f(n) is Θ(g(n))

比较分析

一、
我们使用大O符号依据渐进增长率来为函数排序。
高增长率也说明了在n很大时, f ( n ) f(n) f(n)也是巨大的。

二、
我们需要注意被符号隐藏的常数因子和低阶项。

比如:
f ( n ) = 1 0 100 n f(n)=10^{100}n f(n)=10100n f ( n ) = n 2 f(n)=n^2 f(n)=n2,我们更倾向于选择后者。

因为 1 0 100 10^{100} 10100被认为是一个天文数字。

三、
我们一般认为 O ( n l o g n ) O(nlogn) O(nlogn)就是高效的; O ( n 2 ) O(n^2) O(n2)在n很小时也被认为是高效的。

区分多项式 O ( n c ) O(n^c) O(nc)是否高效的关键在于c与1的关系;
区分指数式 O ( b n ) O(b^n) O(bn)是否高效的关键在于b与1的关系;

算法分析示例

常量时间操作

len(arr) : O(1)
data[I] : O(1)

找最大值算法

对于随机序列,第j个元素比前j个元素大的概率是 1 j \frac{1}{j} j1,因此更新最大值的的数学期望为 H n = ∑ j = 1 n 1 j H_n=\sum_{j=1}^n\frac{1}{j} Hn=j=1nj1(n调和数)。
于是,最大值被更新的预期次数/时间为 O ( l o g n ) O(logn) O(logn)

前缀平均数

有这样一个特殊的序列,它的元素是另一个序列的前缀平均数。
在这里插入图片描述

二次时间算法

在这里插入图片描述
O ( n 2 ) O(n^2) O(n2)

线性时间算法

在这里插入图片描述
O ( n ) O(n) O(n)

初始化列表的时间是 O ( n ) O(n) O(n)

求和项随时更新。

三集不相交
原始方案

在这里插入图片描述
O ( n 3 ) O(n^3) O(n3)

改进方案

在这里插入图片描述
O ( n 2 ) O(n^2) O(n2)

最坏情况下,
(a,b)判断,用时 O ( n 2 ) O(n^2) O(n2);
(a,c)判断,用时 O ( n 2 ) O(n^2) O(n2)

判别项拆分。

元素唯一性
原始方案

在这里插入图片描述
O ( n 2 ) O(n^2) O(n2)

改进方案

在这里插入图片描述
O ( n l o g n ) O(nlogn) O(nlogn)

先排序,后操作。

证明技术

  1. 反证法;
  2. 数学归纳法;
  3. 循环不变量。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

右边是我女神

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

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

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

打赏作者

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

抵扣说明:

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

余额充值