最优化方法—— §1-1 算法复杂性

专题1 算法分析技术

§1-1 算法复杂性

一、算法(Algorithm)

  在正式介绍算法的时间复杂度之前,首先抛出一个问题:算法是什么
  顾名思义,算法就是计算的方法。更严谨地说,算法应该是求解问题的途径。 现实生活中,往往会利用计算机求解各种问题,而这些问题无外乎两个要素:问题描述+输入、输出。算法就是计算机求解问题的具体过程,它包含了若干计算机能够理解的指令,对于问题的每一个输入,总是给出正确的输出,即问题的解。
  下图给出了算法与问题的关系,以便于理解:在这里插入图片描述

  Pascal语言之父Nicklaus Wirth用一句非常经典的话,概括了算法在程序设计中的核心地位。希望大家能以这句话作为开端,在今后学习更多有趣的算法:

          程序=算法+数据结构     --Nicklaus Wirth

二、复杂度

  上面提到,算法的目的是为了求解问题。然而,仅仅是设计出算法是远远不够的。举一个非常简单的例子:


问题  幂运算 x n x^n xn.
输入  正整数 x , n x,n x,n.
输出   x x x n n n次幂.


  非常自然地,我们会想到用一个变量 a n s ans ans 存储结果,初始赋值为1,用简单的循环运算,不停使 a n s = a n s ⋅ x ans=ans\cdot x ans=ansx就行了。代码如下:

ans=1
for i in range(n):
    ans*=x
print(ans)

  这种算法的复杂度是与输入的 n n n 有关的,而在 n n n 很大时,这种算法会运行得很慢。相比之下,快速幂算法就会比直接模拟快一些:

tmp, ans = x, 1
while n > 0:
    if n % 2 == 1:
        ans *= tmp
    n = n // 2
    tmp *= tmp
print(ans)

  通过简单的检验发现,这两种算法处理上述问题会给出完全相同的结果,而后者的每次循环会将 n n n 减半,这意味着问题的规模增加时,使用快速幂的操作次数增幅更小。
  即使算法的效率再高,也不可避免地与问题给出的 n n n 产生关联。上述比较带给我们的直观感受是快速幂算法“好”,模拟算法“差”。如何对算法的优劣给出直观定义呢?这就要引出复杂度的概念:

Definition 1.1-1:时间复杂度
  仅考虑主要的算法操作,将总操作数与问题规模间的函数关系称作时间复杂度,记作 T ( n ) T(n) T(n) ;所需存储空间与问题规模间的函数关系称作空间复杂度,记作 S ( n ) S(n) S(n) .

  多采用时间复杂度对算法进行衡量,复杂度量级越小,处理大规模问题的性能越优。

三、复杂度估计

  为了强调算法效率随着问题规模的变化趋势,往往重点关注复杂度的量级。上例中,模拟算法所需的操作与 n n n 大致成正比关系,量级应该为 n ( n 1 ) n(n^1) n(n1) 。而快速幂算法的每一步将 n n n 减半,即双倍的 n n n 会使算法多进行一步运算,量级为 log ⁡ n \log n logn 。(由数学知识, log ⁡ 2 n \log_2 n log2n ln ⁡ n \ln n lnn 间仅相差常数因子 ln ⁡ 2 \ln 2 ln2 ,因此通常忽略底数 2 2 2
  接下来将给“量级”一个严谨的定义,这也是常用的渐近估计技术

Definition 1-1.2:复杂度上界
  对给定的函数 f ( n ) , ∃ C ∈ R , n 0 ∈ N ∗ , ∀ n ≥ n 0 , T ( n ) ≤ C ⋅ f ( n ) f(n) ,\exists C \in \mathbb R,n_0\in \mathbb N^*,\forall n \geq n_0,T(n)\leq C\cdot f(n) f(n),CR,n0N,nn0,T(n)Cf(n) 恒成立,则称算法具有量级为 f ( n ) f(n) f(n) 的上界,即 T ( n ) = O ( f ( n ) ) T(n)=O(f(n)) T(n)=O(f(n)).

  用该上界 O ( f ( n ) ) O(f(n)) O(f(n)) 表示算法复杂度就称作大O表示法 f ( n ) f(n) f(n)的增长趋势越缓,意味着算法的性能越好。下面将给出使用大O表示法估计复杂度的具体方法。
  考虑一个具体问题:


问题  方阵乘积 A ⋅ B A\cdot B AB . 方阵 A , B A,B AB 的大小均为 n × n n \times n n×n.
输入  两个方阵 A , B A , B A,B.
输出  方阵乘积 C = A ⋅ B C=A\cdot B C=AB.


  由高等代数知识,矩阵 C C C i i i j j j 列由下列公式给出:
c i j = ∑ k = 1 n a i k ⋅ b k j c_{ij}=\sum_{k=1}^{n} a_{ik}\cdot b_{kj} cij=k=1naikbkj
  其中的循环变量 i , j , k i,j,k i,j,k 均在 1~ n n n 间变化。若设单次运算需要 k k k 次基本操作( k k k 为常数),那么这三重嵌套循环总共需要的操作数为 k ⋅ n 3 k \cdot n^3 kn3。由上述分析,该算法的时间复杂度为 O ( n 3 ) O(n^3) O(n3)
  同样,回顾最初的幂运算例子,可以得出模拟算法的时间复杂度为 O ( n ) O(n) O(n) ,快速幂算法的时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn)
  最后,让我们对估计过程中的一些操作给出一般原则:

  1. 基本操作:单次四则运算,读,写,赋值操作的时间复杂度为 O ( 1 ) O(1) O(1)
  2. 判断语句:if-else 语句的复杂度为两分支的复杂度之和。
  3. 循环语句:for 循环的复杂度为 单次循环复杂度 × \times ×循环次数。
  4. 多算法串行:仅计算所有算法中复杂度的最高量级,忽略常数因子。

  相信学过OI的都有被 1s,128MB 支配的恐惧,没错,设计更高效的算法总是非常有挑战性的过程。一起为设计更高效的算法而奋斗!

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值