跳转至

绪论:数据、模型与算法

数据与数学模型

数据是客观事物的符号表示,也是信息的载体。计算机围绕数据完成采集、传输、存储和处理;算法则从数据中得到所需结果。

数学模型是针对特定目的,对现实对象作取舍和抽象后得到的数学结构。建模时没有“越复杂越好”这一说,模型只需保留与当前问题有关的因素。例如人口增长可以先用指数模型

\[ x(t)=x_0e^{rt}, \]

但它没有考虑资源上限。加入承载量 \(x_m\) 后,可改用 Logistic 模型

\[ \frac{\mathrm dx}{\mathrm dt}=rx\left(1-\frac{x}{x_m}\right), \qquad x(t)=\frac{x_m}{1+\left(\frac{x_m}{x_0}-1\right)e^{-rt}}. \]

同一问题往往有一串由简到繁的模型。先明确假设、输入、输出和适用范围,再谈计算方法。

算法及其评价

算法是解决特定问题的有限指令序列,具有五个基本特性:

  1. 有穷性:有限步后结束,每一步也能在有限时间内完成;
  2. 确定性:每条指令含义明确,不依赖含糊解释;
  3. 可行性:每一步都能由已有的基本操作在有限次执行后完成;
  4. 输入:有零个或多个输入;
  5. 输出:至少有一个输出。

评价算法不能只看“样例能跑”。通常还要看:

  • 正确性:对所有合法输入都给出正确结果;
  • 可读性:结构清楚,便于检查、维护和复用;
  • 健壮性:能妥善处理非法输入和边界情况;
  • 效率:合理使用时间和空间。

直接计时属于事后统计,结果会混入语言、编译器、硬件和运行负载的影响。复杂度分析则选取会反复执行的基本操作,在实现前估计其次数随问题规模 \(n\) 的增长规律。

渐进复杂度

若存在正常数 \(C,n_0\),使得 \(n\ge n_0\)

\[ 0\le T(n)\le C f(n), \]

就记作 \(T(n)=O(f(n))\)。Big-O 描述渐进上界,常数因子和低阶项在这里被忽略。常见增长量级为

\[ O(1)<O(\log n)<O(n)<O(n\log n)<O(n^2)<O(n^3)<O(2^n)<O(n!). \]

分析循环时,顺序语句的代价相加,嵌套循环的迭代次数相乘;循环变量按倍数增长通常对应对数复杂度。例如

for (int i = 1; i < n; i *= 2) {
    // O(1)
}

只执行 \(\lceil\log_2 n\rceil\) 次,因此为 \(O(\log n)\)

几种课件中的典型计数可以放在一起看:

程序结构 基本操作次数 复杂度
三层各循环 \(n\) \(n^3\) \(O(n^3)\)
内层从当前 \(i\) 走到 \(n\) \(n+(n-1)+\cdots+1\) \(O(n^2)\)
外层 \(i=1\ldots n\),内层变量倍增 \(\sum_i O(\log i)=O(\log n!)\) \(O(n\log n)\)
外层变量倍增,内层固定执行 10 次 \(10\lceil\log_2n\rceil\) \(O(\log n)\)

同一算法还要区分最好、最坏和平均情况。以顺序查找为例,成功位置等可能时平均比较次数为

\[ \frac{1}{n}\sum_{i=1}^{n}i=\frac{n+1}{2}. \]

空间复杂度包括指令、数据以及函数调用所需的环境空间。通常只统计除输入本身以外的额外空间;若额外空间为常数,称为原地算法。

评论