惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Azure Blog
Microsoft Azure Blog
量子位
小众软件
小众软件
C
Cybersecurity and Infrastructure Security Agency CISA
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tenable Blog
V
Vulnerabilities – Threatpost
Know Your Adversary
Know Your Adversary
T
Threat Research - Cisco Blogs
Latest news
Latest news
Spread Privacy
Spread Privacy
C
Cyber Attacks, Cyber Crime and Cyber Security
NISL@THU
NISL@THU
T
Tor Project blog
Hacker News: Ask HN
Hacker News: Ask HN
V2EX - 技术
V2EX - 技术
T
The Exploit Database - CXSecurity.com
博客园 - 三生石上(FineUI控件)
K
Kaspersky official blog
Cyberwarzone
Cyberwarzone
博客园 - 叶小钗
博客园 - 聂微东
Last Week in AI
Last Week in AI
爱范儿
爱范儿
腾讯CDC
博客园 - Franky
美团技术团队
J
Java Code Geeks
O
OpenAI News
L
Lohrmann on Cybersecurity
Simon Willison's Weblog
Simon Willison's Weblog
有赞技术团队
有赞技术团队
T
Threatpost
G
GRAHAM CLULEY
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
宝玉的分享
宝玉的分享
I
Intezer
N
News and Events Feed by Topic
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
The Blog of Author Tim Ferriss
S
Security @ Cisco Blogs
Forbes - Security
Forbes - Security
N
News | PayPal Newsroom
Stack Overflow Blog
Stack Overflow Blog
Scott Helme
Scott Helme
H
Hacker News: Front Page
Cloudbric
Cloudbric

博客园 - 立体风

当AI构建自身 nana gui库的回调函数 Nana c++ gui库的对话框函数 -- 理解 nana 的运行机制 介绍 MSVC 的编译命令/ Zc CMake 变量 ${CMAKE_CURRENT_BINARY_DIR} 学习 nana c++ 库 (一) AutoHotkey v2 的源码学习路线 Visual Studio 中行号右边的黄色竖条 allocate 中为什么有两个L vs的cl.exe 编译器 c++20 基本配置 IT高频词 address Python Install Manager (PIM,官方安装管理器)简介 debian 中 contrib 软件仓库 debian 版本号及 apt 命令对软件的区分 sysctl的历史及常用命令 极限运算的法则 TeX 的 ctex 宏包的基本用法 XeLaTeX 的基本用法 XeLaTeX 介绍 为什么梯度向量 ∇f 指向函数值增长最快的方向 三角函数的余弦定理的证明 向量点积的几何定义的证明 方向导数公式的证明 仿射函数的定义及用途 凸集的定义及证明 在 LaTeX 公式中处理英文单词 梯度下降法 (Gradient Descent)的数学原理。 梯度下降法简介 向量形式表达最小二乘法 线性回归模型的核心数学表达式 risidual 音标和本义 coefficients的音标及本义(线性模型)
大O表示法推导
立体风 · 2026-06-15 · via 博客园 - 立体风

概述

在计算算法运行时间的问题中,对于输入元素个数为N的算法,可以考虑其运行所需最大时间T(N),也称输入元素个数为N时的最坏情况下运行时间。为了进一步简化问题,不寻求T(N)的精确值,而主要考察T(N)在N较大时的特点,我们要找的是函数T(N)形式比较简单的上界函数,比如对于某个常量c和任意大的N,可以有T(N)<= cN。对于这种情况,可以肯定“在最坏情况下,运行时间也可以与输入元素个数成正比”,当算法在不同计算机上运行时,取不同的常数c,因此还可以进一步简化表达式,使其不再含有常数c,用大O表示法,前面的关系式写成T(N)=O(N),通常,如果存在某个函数f使得T(N)<=cf(N),那么对于某个常量c和任意大的N,我们定义T(N)=O(f(N))

详细解读推导

这段话其实是计算机科学中算法复杂度分析(大O表示法)的核心逻辑。它解释了为什么程序员在聊代码快慢时,不聊“几分几秒”,而是聊 \(O(N)\)\(O(N^2)\)


第一步:为什么要看“最坏情况” \(T(N)\)

原文: 对于输入元素个数为N的算法,可以考虑其运行所需最大时间T(N),也称输入元素个数为N时的最坏情况下运行时间。

通俗解读:
假设你要在一堆乱序的扑克牌(共 \(N\) 张)里找一张红桃A。

  • 最好情况: 运气爆棚,第一张就是它(只找了1次)。
  • 平均情况: 运气一般,牌在中间(找了 \(N/2\) 次)。
  • 最坏情况: 运气差到家,红桃A在最后一张,或者根本不在里面(找了 \(N\) 次)。

在实际算算法时间时,最好情况毫无意义(谁能保证每次运气都好?),平均情况计算极其复杂(需要数学概率论)。只有最坏情况 \(T(N)\) 最好用,它相当于给算法兜底:“无论输入多极端,这个算法最慢也就是 \(T(N)\) 这么长,绝不会更慢了。” 这种确定性让工程师最安心。


第二步:为什么要看“大 \(N\) 时的特点”?

原文: 为了进一步简化问题,不寻求T(N)的精确值,而主要考察T(N)在N较大时的特点,我们要找的是函数T(N)形式比较简单的上界函数……

通俗解读:
假设我们精准测量了某个算法在电脑上的运行步数,得到精确函数是 \(T(N) = 3N^2 + 5N + 10\)

\(N\) 变得非常大时(比如处理海量用户数据,\(N = 1,000,000\)):

  • \(3N^2 = 3 \times 1,000,000^2 = 3,000,000,000,000\) (三万亿)
  • \(5N = 5 \times 1,000,000 = 5,000,000\) (五百万)
  • \(10 = 10\) (十)

瞧见了吗?在大数据面前,后两项 \(5N\)\(10\) 连零头都算不上!真正决定这个算法运行时间的,是那个最高次项 \(N^2\)

因此,科学家决定抓大放小。既然精确值这么难算,不如找一个形式简单的“天花板”(上界)。只要在 \(N\) 很大时,这个天花板能罩住 \(T(N)\) 就行了。


第三步:为什么要干掉常数 \(c\)?(大O表示法的诞生)

原文: 当算法在不同计算机上运行时,取不同的常数c,因此还可以进一步简化表达式,使其不再含有常数c,用大O表示法……

通俗解读:
同一段找扑克牌的代码:

  • 超级计算机上运行,一下看5张牌,耗时 \(T(N) = 0.2N\) 秒。
  • 老旧笔记本上运行,半天看一张,耗时 \(T(N) = 5N\) 秒。

如果把电脑硬件的差异算进去,我们就没法纯粹地比较“算法本身的好坏”了。

为了让算法评级脱离硬件限制,我们引入一个常数 \(c\)
不管是 \(0.2N\) 还是 \(5N\),它们都满足一个规律:运行时间和输入个数 \(N\) 成正比。我们完全可以找一个常数 \(c\)(在老电脑上 \(c\) 取 6,在超算上 \(c\) 取 1),使得 \(T(N) \le c \times N\) 恒成立。

既然 \(c\) 只是为了照顾硬件差异而存在的,那在研究算法理论时,干脆把 \(c\) 也省去!
这就诞生了大O表示法
我们不关心具体是 \(5N\) 还是 \(0.2N\),我们只关心它的增长趋势和 \(N\) 是一样的,直接写成:

\[T(N) = O(N) \]


终极总结与数学定义

原文最后一句: 通常,如果存在某个函数f使得T(N)<=cf(N),那么对于某个常量c和任意大的N,我们定义T(N)=O(f(N)) (注:原文结尾的cN应为笔误或排版残留)

大白话推导闭环:

  1. 现实情况: 算法的真实最坏时间 \(T(N)\) 复杂且受机器影响。
  2. 寻找天花板: 我们找到一个形状简单的标准数学函数 \(f(N)\)(比如 \(N\)\(N^2\)\(\log N\))。
  3. 放大包容: 我们乘以一个足够大的常数 \(c\),让 \(c \cdot f(N)\) 成为一条高高在上的曲线。
  4. 最终定义: 只要当 \(N\) 足够大之后,真实的 \(T(N)\) 永远被压在 \(c \cdot f(N)\) 的下方(即 \(T(N) \le c \cdot f(N)\)),那么我们就可以拍板:这个算法的时间复杂度就是 \(O(f(N))\)

举个例子:
如果一个算法最坏情况要跑 \(T(N) = 2N + 10\) 步。
\(N > 10\) 时,我们可以肯定 \(2N + 10 < 3N\)
这里的 \(c = 3\)\(f(N) = N\)。因为 \(T(N) \le 3N\),所以这个算法的复杂度就是 \(O(N)\)(线性阶)。