Big-O 表示法 O(n)
和 Little-O 表示法 o(n)
有什么区别?
f ∈ O(g) 说,本质上
对于常数 k > 0 的至少一个选择,您可以找到一个常数 a,使得不等式 0 <= f(x) <= kg(x) 对于所有 x > a 都成立。
请注意,O(g) 是该条件成立的所有函数的集合。
f ∈ o(g) 说,本质上
对于常数 k > 0 的每一个选择,您都可以找到一个常数 a,使得不等式 0 <= f(x) < kg(x) 对于所有 x > a 都成立。
再次注意,o(g) 是一个集合。
在 Big-O 中,您只需要找到一个特定的乘数 k,对于该乘数 k,不等式在某个最小值 x 之外仍然成立。
在 Little-o 中,必须存在一个最小值 x,在该最小值之后,无论您使 k 多么小,只要它不是负数或零,不等式都成立。
这些都描述了上限,虽然有点违反直觉,但 Little-o 是更强的陈述。如果 f ∈ o(g) 与 f ∈ O(g) 相比,f 和 g 的增长率之间的差距要大得多。
差异的一个例子是:f ∈ O(f) 为真,但 f ∈ o(f) 为假。因此,Big-O 可以理解为“f ∈ O(g) 意味着 f 的渐近增长不快于 g's”,而“f ∈ o(g) 意味着 f 的渐近增长严格慢于 g's”。这就像 <=
与 <
。
更具体地说,如果 g(x) 的值是 f(x) 值的常数倍,则 f ∈ O(g) 为真。这就是为什么在使用 big-O 表示法时可以删除常量的原因。
但是,要使 f ∈ o(g) 为真,那么 g 必须在其公式中包含更高的 x 幂,因此 f(x) 和 g(x) 之间的相对分离实际上必须随着 x 变大而变大。
要使用纯数学示例(而不是指算法):
以下对于 Big-O 是正确的,但如果您使用 little-o,则不正确:
x² ∈ O(x²)
x² ∈ O(x² + x)
x² ∈ O(200 * x²)
以下情况适用于 little-o:
x² ∈ o(x³)
x² ∈ o(x!)
ln(x) ∈ o(x)
请注意,如果 f ∈ o(g),这意味着 f ∈ O(g)。例如 x² ∈ o(x³) 所以 x² ∈ O(x³) 也是正确的,(再次,将 O 视为 <=
,将 o 视为 <
)
Big-O 对 little-o 就像 ≤
对 <
。 Big-O 是一个包容性的上界,而 little-o 是一个严格的上界。
例如,函数 f(n) = 3n
是:
在 O(n²)、o(n²) 和 O(n) 中
不在 O(lg n)、o(lg n) 或 o(n) 中
类似地,数字 1
是:
≤ 2、< 2 和 ≤ 1
不 ≤ 0、< 0 或 < 1
这是一张表格,显示了总体思路:
https://i.stack.imgur.com/SZ7jy.png
(注意:该表是一个很好的指南,但它的限制定义应该根据 superior limit 而不是正常限制。例如,3 + (n mod 2)
永远在 3 和 4 之间振荡。它在 O(1)
中,尽管没有正常限制,因为它仍然有 lim sup
: 4。)
我建议记住 Big-O 表示法如何转换为渐近比较。比较更容易记住,但不太灵活,因为你不能说像 nO(1) = P 这样的东西。
我发现当我在概念上无法掌握某件事时,思考为什么要使用 X 有助于理解 X。(不是说你没有尝试过,我只是在设置阶段。)
你知道的东西:对算法进行分类的一种常见方法是通过运行时,并且通过引用算法的大复杂度,你可以很好地估计哪个是“更好”的——无论哪个具有“最小”功能哦!即使在现实世界中,O(N) 也比 O(N²) “更好”,除非是像超大质量常数等愚蠢的东西。
假设有一些算法在 O(N) 中运行。很不错吧?但是假设你(你聪明的人,你)想出了一个在 O(N⁄loglogloglogN) 中运行的算法。耶!它更快!但是当你写论文的时候,你会觉得一遍又一遍地写这个很傻。所以你写一次,你可以说“在这篇论文中,我已经证明了算法 X,以前可以在 O(N) 时间内计算,实际上可以在 o(n) 时间内计算。”
因此,每个人都知道你的算法更快——多少不清楚,但他们知道它更快。理论上。 :)
一般来说
您可以将渐近符号理解为:缩小时函数如何比较?(一个很好的测试方法是使用像 Desmos 这样的工具并使用鼠标滚轮)。尤其是:
f(n) ∈ o(n) 意味着:在某些时候,你越缩小,f(n) 将被 n 支配的越多(它会逐渐偏离它)。
g(n) ∈ Θ(n) 意味着:在某些时候,缩小不会改变 g(n) 与 n 的比较方式(如果我们从轴上删除刻度,您将无法判断缩放级别)。
最后 h(n) ∈ O(n)
表示函数 h
可以属于这两个类别中的任何一个。当 n
增加时,它可能看起来很像 n
,也可能比 n
越来越小。基本上,f(n)
和 g(n)
都在 O(n)
中。
我认为这个维恩图(来自 this course)可能会有所帮助:
https://i.stack.imgur.com/v2eH3.png
在计算机科学
在计算机科学中,人们通常会证明给定算法同时接受上限 O
和下限 𝛺
。当两个界限都满足时,这意味着我们找到了一种渐近最优算法来解决该特定问题Θ
。
例如,如果我们证明算法的复杂度在 O(n)
和 𝛺(n)
中,则意味着它的复杂度在 Θ(n)
中。 (这就是 Θ
的定义,它或多或少地转化为“渐近相等”。)这也意味着没有算法可以解决 o(n)
中的给定问题。再次粗略地说“这个问题不能(严格)少于 n
步解决”。
通常在下限证明中使用 o
来显示矛盾。例如:
假设算法 A 可以在 o(n) 步中找到大小为 n 的数组中的最小值。由于 A ∈ o(n) 它不能从输入中看到所有项目。换句话说,至少有一个项目 x A 从未见过。算法 A 无法区分只有 x 值发生变化的两个相似输入实例之间的差异。如果 x 是其中一个实例中的最小值而不是另一个实例中的最小值,则 A 将无法在(至少)这些实例之一中找到最小值。换句话说,在 𝛺(n) 中找到数组中的最小值(o(n) 中没有算法可以解决这个问题)。
有关下限/上限含义的详细信息
O(n)
的上限仅表示即使在最坏的情况下,算法也将在最多 n
步中终止(忽略所有常数因子,包括乘法和加法)。 𝛺(n)
的下限是关于问题本身的陈述,它表示我们构建了一些示例,其中给定问题任何算法都无法在不到 n
步内解决(忽略乘法和加法常数)。步数最多为 n
且至少为 n
,因此这个问题的复杂性是“正好 n
”。而不是每次我们只写 Θ(n)
时都说“忽略常数乘法/加法因子”。
big-O 表示法有一个称为 small-o 表示法的伴侣。大 O 表示法表示一个函数是渐近的 no more than
另一个。要说一个函数渐近 less than
另一个,我们使用 small-o 表示法。 big-O 和 small-o 符号之间的区别类似于 <=(小于等于)和 < 之间的区别。 (少于)。
a
有一个k
:...”,而是“对于每个k
有一个a
:...”