算法复杂度和大 O 表示法

1,838 阅读2分钟

概念

SICP
算法复杂度是算法分析里的概念(对应到SICP里的增长阶),是衡量计算资源消耗数量(例如计算时间,存储器使用等)的指标。

算法的复杂度在理论上表示为一个函数:其定义域是输入数据的长度(通常考虑任意大的输入,没有上界),值域通常是执行步骤数量(时间复杂度)或者存储器位置数量(空间复杂度)。

这个函数形如:

R(n) = Θ(f(n)) 亦可记做 O(f(n))

f(n) 就是被度量的算法主体;算法的复杂度标记就是大O(Θ读做theta),这种记法称为大O表示法

常见复杂度级别

  • Θ(1) 常数级别
  • Θ(log(n)) 对数级别
  • Θ(n) 线性级别
  • Θ(n*log(n)) 线性对数级别
  • Θ(n^2) 平方级别
  • Θ(2^n) 指数级别

直观感受

基于规模n 各级别算法 与 复杂度 的关系:

Big-O_Complexity_Chart

各种排序算法的复杂度:

Complexity of Array Sorting Algorithms

例子

求幂算法 b^n(b的n次方),如果使用递归的形式来表达,有:
b^n = b * b^(n-1)
b^0 = 1

我们用 Racket 将它翻译为如下过程:

Scheme
; 线性递归算幂
(define (expt b n)
    (if (= n 0)
        1
        (* b (expt b (- n 1)))))

可以看出,对于任意的输入规模 n,它都是线性的递归计算(线性递归),时间和空间复杂皆为 Θ(n)

我们知道,有限递归算法都可以优化成线性迭代形式,于是就有了:

Scheme
; 线性迭代算幂
(define (linear-exp m n)
  (define (linear-exp-iter m n acc)
    (cond ((= n 0) 1)
          ((= n 1) (* acc m))
          (else (linear-exp-iter m (- n 1) (* acc m)))
          ))
  (linear-exp-iter m n 1)
)

不难发现,这个算法的时间复杂度还是线性的,变换成迭代形式后,优化的是空间复杂度(Lisp是应用序求值,即调用函数时会先求值参数而后应用;结合递归算法,就有一个展开的空间开销),降至了 Θ(1)

还有优化的余地,因为,幂函数可以表示为:

b^n = (b^(n/2))^2 当n是偶数时
b^n = b * b^(n-1) 当n是奇数时

所以,算法可以通过迅速降幂,优化成对数级别

Scheme
; 对数递归算幂
(define (fast-expt b n)
  (define (square x) (* x x))
  (cond ((= n 0) 1)
        ((even? n) (square (fast-expt  b (/ n 2))))
        (else (* b (fast-expt b (- n 1))))))

时间和空间上复杂度皆为 Θ(log(n))

所以,递归就一定比迭代算法慢吗?未必~

引用

  1. bigocheatsheet.com/
  2. mitpress.mit.edu/sicp/full-t…