说明
要形式化一个算法来正式应用 Big-O 并不容易,它是一个数学概念,不容易转化为算法。通常,您会根据输入的大小测量执行操作所需的“计算步骤”数量。
- 所以
f 是衡量算法执行多少计算步骤的函数。
-
n 是输入的大小,例如5 表示[4, 2, 9, 8, 2] 之类的列表。
-
g 是您衡量的函数,因此如果您检查 O(n^2),则为 g = n^2。
-
c 和 k 在很大程度上取决于具体的算法以及 f 的样子。
示例
将算法形式化的最大问题是您无法真正准确地知道执行了多少计算步骤。假设我们有以下 Java 代码:
public static void printAllEven(int n) {
for (int i = 0; i < n; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
}
它执行多少步?我们应该走多深? for (int i = 0; i < count; i++) 呢?这些是在循环期间执行的多个语句。 i % 2 呢?我们可以假设这是一个“单一操作”吗?在哪个级别,一个 CPU 周期?一条装配线? println(i) 呢,它需要多少计算步骤,1 或 5 或 200?
这是不切实际的。我们不知道确切的数量,我们必须抽象并说它是一个常数A、B和C的步数,这没关系,因为它运行在常数时间。
在简化分析之后,我们可以说我们实际上只关心println(i) 被调用的频率。
这导致我们将其精确称为n / 2 次的观察结果(因为在0 和n 之间有很多偶数。
f 使用上述常量的精确公式会产生类似
n * A + n * B + n/2 * C
但由于常量并没有真正发挥任何作用(它们在 c 中消失了),我们也可以忽略这一点并简化。
例如,现在您需要证明n / 2 在O(n^2) 中。通过这样做,您还将获得c 和k 的具体数字。示例:
n / 2 <= n <= 1 * n^2 // for all n >= 0
因此,通过选择 c = 1 和 k = 0,您已经证明了该声明。 c 和 k 的其他值也可以,例如:
n / 2 <= 100 * n <= 5 * n^2 // for all n >= 20
这里我们选择了c = 5和k = 20。
你也可以用完整的公式玩同样的游戏,得到类似的东西
n * A + n * B + n/2 * C
<= n * (A + B + C)
= D * n
<= D * n^2 // for all n > 0
c = D 和 k = 0。
如您所见,它实际上并没有起到任何作用,常量只是在 c 中消失。