计算复杂度

提示:计算复杂度的简单理解(第一次写博客)

计算复杂度

计算复杂度

我们以Vicinity Vision Transformer论文中的图为例。
在这里插入图片描述图注:标准自注意力(左)和线性化自注意力(右)的图示。 N N N表示输入图像的 p a t c h patch patch数, d d d是特征维度。使 N ≫ d N\gg d Nd,线性化自注意力的计算复杂度相对于输入长度线性增长,而标准自注意力的计算复杂度是二次的。

从输入到输出可以这样计算:
( N × d ) × ( d × N ) = N × N × ( d × N ) × ( N × N ) = d × N (N\times d)\times (d\times N)=N\times N\times (d\times N)\times (N\times N)=d\times N (N×d)×(d×N)=N×N×(d×N)×(N×N)=d×N
( d × N ) × ( N × d ) = d × d × ( d × d ) × ( d × N ) = d × N (d\times N)\times (N\times d)=d\times d\times (d\times d)\times (d\times N)=d\times N (d×N)×(N×d)=d×d×(d×d)×(d×N)=d×N

关于计算复杂度:其实可以认为是乘法次数。我们给出最直观的解释。

假设有两个矩阵做乘法,如下:
[ 1 2 3 4 5 6 ] × [ 1 2 3 4 5 6 ] = [ 1 2 3 4 5 6 7 8 9 ] \left[\begin{matrix}1&2\\3&4\\5&6\\\end{matrix}\right]\times\left[\begin{matrix}1&2&3\\4&5&6\\\end{matrix}\right]=\left[\begin{matrix}1&2&3\\4&5&6\\7&8&9\\\end{matrix}\right] 135246×[142536]=147258369,其中行数为 N N N,列数为 d d d

( 3 × 2 ) × ( 2 × 3 ) = ( 3 × 3 ) × ( N × d ) × ( d × N ) = ( N × N ) (3\times 2)\times (2\times 3)=(3\times 3)\times (N\times d)\times (d\times N)=(N\times N) (3×2)×(2×3)=(3×3)×(N×d)×(d×N)=(N×N)

3 × 3 3\times 3 3×3矩阵第一个元素涉及的乘法次数: 1 × 1 + 2 × 4 = 9 1\times 1+2\times 4=9 1×1+2×4=9 共2次乘法;其它元素是一样的。最后可以得到 2 × 9 = 2 × 3 × 3 = d × N × N = N 2 d 2\times 9=2\times 3\times 3=d\times N\times N=N^{2}d 2×9=2×3×3=d×N×N=N2d.

假设又有两个矩阵做乘法,如下:
[ 1 2 3 4 5 6 ] × [ 1 2 3 4 5 6 ] = [ 1 2 3 4 ] \left[\begin{matrix}1&2&3\\4&5&6\\\end{matrix}\right]\times\left[\begin{matrix}1&2\\3&4\\5&6\\\end{matrix}\right]=\left[\begin{matrix}1&2\\3&4\\\end{matrix}\right] [142536]×135246=[1324],其中行数为 d d d,列数为 N N N

( 2 × 3 ) × ( 3 × 2 ) = ( 2 × 2 ) × ( d × N ) × ( N × d ) = ( d × d ) (2\times 3)\times (3\times 2)=(2\times 2)\times (d\times N)\times (N\times d)=(d\times d) (2×3)×(3×2)=(2×2)×(d×N)×(N×d)=(d×d)

2 × 2 2\times 2 2×2矩阵第一个元素涉及的乘法次数: 1 × 1 + 2 × 3 + 2 × 5 = 17 1\times 1+2\times 3+2\times 5=17 1×1+2×3+2×5=17 共3次乘法;其它元素是一样的。最后可以得到 3 × 4 = 3 × 2 × 2 = N × d × d = N d 2 3\times 4=3\times 2\times 2=N\times d\times d=Nd^2 3×4=3×2×2=N×d×d=Nd2 .

为什么会有这种情况呢?以第二个例子为例,可以观察到,所得结果的一个元素的乘法数量和消失的维度大小有关,也就是列数 N N N,或者说,列数 N N N就是所得结果一个元素的乘法次数。那么多少个元素呢?元素个数就要看你是如何进行的乘法操作,其实就是矩阵大小。比如 ( 2 × 3 ) × ( 3 × 2 ) = ( 2 × 2 ) × ( d × N ) × ( N × d ) = ( d × d ) (2\times 3)\times (3\times 2)=(2\times 2)\times (d\times N)\times (N\times d)=(d\times d) (2×3)×(3×2)=(2×2)×(d×N)×(N×d)=(d×d),那么就是 d 2 d^2 d2个元素,最后乘法次数就是 N d 2 Nd^2 Nd2

乘法次数=消失的维度 × 所得矩阵大小

那么计算复杂度呢?我们不要去管 O ( ∙ ) O(\bullet) O()具体代表什么,这不重要。
以第一个图为例,乘法次数1: ( N × d ) × ( d × N ) = N 2 d (N\times d)\times (d\times N)=N^{2}d (N×d)×(d×N)=N2d;乘法次数 2 2 2 ( N × d ) × ( d × N ) = N 2 d (N\times d)\times (d\times N)=N^{2}d (N×d)×(d×N)=N2d O ( N 2 d + N 2 d ) = O ( N 2 ) O(N^{2}d+N^{2}d)=O(N^2) O(N2d+N2d)=O(N2)。因为 N ≫ d N\gg d Nd,所以 d d d(还有常数 2 2 2)被省略了,即 O ( N 2 ) O(N^2) O(N2)
以第二个图为例,乘法次数1: ( d × N ) × ( N × d ) = N d 2 (d\times N)\times (N\times d)=Nd^2 (d×N)×(N×d)=Nd2;乘法次数2: ( d × d ) × ( d × N ) = N d 2 (d\times d)\times (d\times N)=Nd^2 (d×d)×(d×N)=Nd2 O ( N d 2 + N d 2 ) = O ( N ) O(Nd^2+Nd^2)=O(N) O(Nd2+Nd2)=O(N)。因为 N ≫ d N\gg d Nd,所以 d d d(还有常数2)被省略了,即 O ( N ) O(N) O(N)

事实告诉我们,我们两个的结果一样,但是我们可以通过控制中间过程减少计算复杂度。

<think>好的,用户问的是时间复杂度计算方法。我之前已经给他推荐过数据结构的学习路线,其中基础准备阶段提到了时间复杂度计算,现在他可能想深入了解这部分内容。首先,我需要回顾一下之前提到的内容,确保这次回答的一致性和连贯性。 用户可能是刚开始学习数据结构,对时间复杂度的概念还不太清楚。他可能需要一个从基础到进阶的详细解释,包括如何计算和实际例子。我需要确保用简单易懂的语言,避免使用过于专业的术语,或者如果使用的话,要加以解释。 可能用户对时间复杂度计算步骤不太清楚,或者在实际应用中遇到困难,比如如何分析循环结构或递归算法的时间复杂度。需要分步骤讲解,每个步骤配以例子,帮助他理解。 另外,用户可能对常见的时间复杂度类型(如O(1), O(n), O(n²)等)感兴趣,需要列举这些类型,并给出相应的例子,这样他可以对比自己的代码情况。同时,最好能指出常见的误区,比如忽略系数或低阶项,这样他在实际计算时不会犯错。 还需要考虑用户的学习阶段,可能他需要知道为什么时间复杂度重要,以及如何应用在实际编程或算法优化中。这部分可以简要提及,强调时间复杂度在评估算法效率中的作用。 最后,要确保回答结构清晰,按照逻辑顺序展开,比如先定义,再计算步骤,然后常见类型,最后注意事项和误区。这样用户能够逐步理解和应用。同时,使用中文口语化的表达,避免生硬,让内容更易于接受。 总结下来,回答的结构应该是: 1. 时间复杂度的定义和作用 2. 计算步骤,分点说明,每个步骤配例子 3. 常见的时间复杂度类型及其例子 4. 注意事项和常见误区 5. 总结应用场景 需要检查是否有遗漏的重要点,比如递归算法的时间复杂度计算可能需要特别说明,比如主定理或者递归树方法,但考虑到用户可能处于初级阶段,可以简要提及或引导进一步学习资源。确保内容准确无误,比如Big O的定义,常见复杂度类型的正确性。例子要典型,比如循环、嵌套循环、递归等,帮助用户举一反三。 另外,用户可能不知道如何将实际代码转化为数学表达式来计算时间复杂度,所以需要详细说明如何分析循环次数、变量变化等。例如,单层循环、双层嵌套循环、循环变量递增方式(如i *= 2)等不同情况下的分析方法。 最后,要确保回答符合之前的系统级指令,使用正确的LaTeX格式,行内公式用$...$,独立公式用$$...$$,并且结构清晰,分点明确,语言简洁。避免使用复杂术语,必要时用中文解释,保持口语化,但不过于随意,保持专业性。</think>以下是时间复杂度的详细计算方法和应用指南,通过具体案例帮助理解: --- ### 一、核心定义 **时间复杂度**描述算法执行时间随数据规模$n$增长的**渐进趋势**,用大O符号$O(·)$表示。 关键原则:**忽略低阶项和常数系数**,例如$3n^2 + 5n + 100$简化为$O(n^2)$ --- ### 二、计算步骤(附案例) #### 步骤1:找出基本操作 选择算法中执行次数最多的操作(如循环内的核心语句) ```python # 案例:数组求和 sum = 0 # 执行1次 for i in range(n): # i从0到n-1循环 sum += arr[i] # 执行n次 ← 基本操作 ``` #### 步骤2:建立数学表达式 统计基本操作次数与$n$的关系 - 单层循环:$n$次 → $O(n)$ $$ \text{总次数} T(n) = n $$ - 双层嵌套循环:$n \times n$次 → $O(n^2)$ $$ T(n) = n^2 $$ ```python for i in range(n): for j in range(n): # 执行n²次 print(i,j) ``` #### 步骤3:分析特殊结构 **情况1:循环变量指数增长** ```python i = 1 while i < n: # 循环次数log₂n i *= 2 # 每次i翻倍 ``` 数学推导: 设循环执行$k$次后结束,则$2^k = n$ → $k = \log_2 n$ 时间复杂度:$O(\log n)$ **情况2:递归算法** 以斐波那契数列递归实现为例: ```python def fib(n): if n <= 1: return n return fib(n-1) + fib(n-2) # 每次分裂为两个子问题 ``` 递归树分析: 每层节点数呈2倍增长 → 总次数约$2^n$ 时间复杂度:$O(2^n)$ --- ### 三、常见时间复杂度类型 | 类型 | 示例场景 | 计算公式 | |------------------|----------------------------|-------------------| | $O(1)$ | 数组随机访问、哈希表查询 | 无循环 | | $O(\log n)$ | 二分查找、平衡树操作 | 循环变量指数增长 | | $O(n)$ | 遍历数组/链表 | 单层循环 | | $O(n \log n)$ | 快速排序、归并排序 | 分治算法 | | $O(n^2)$ | 冒泡排序、暴力搜索二维数组 | 双层嵌套循环 | | $O(2^n)$ | 斐波那契递归、子集枚举 | 指数级递归 | --- ### 四、关键注意事项 1. **最坏情况优先原则** 快速排序默认按$O(n \log n)$分析,但最坏情况(完全逆序)是$O(n^2)$ 2. **均摊时间复杂度** 动态数组扩容:单次插入可能触发$O(n)$扩容,但均摊到每次操作仍为$O(1)$ $$ \text{均摊成本} = \frac{O(n)}{n} = O(1) $$ 3. **递归复杂度计算工具** 主定理(Master Theorem)可快速计算分治算法复杂度: $$ T(n) = aT(n/b) + f(n) $$ --- ### 五、实战训练建议 1. 用时间复杂度反推算法优化方向 - 若题目要求$O(n)$解法,当前算法是$O(n^2)$ → 考虑哈希表或双指针 2. LeetCode练习题: - 简单:LC217(存在重复元素,$O(n)$哈希表解法) - 中等:LC15(三数之和,$O(n^2)$双指针优化) - 困难:LC239(滑动窗口最大值,$O(n)$单调队列) --- 通过这种「理论公式+代码对照+场景应用」的三维学习方法,可以快速掌握时间复杂度分析的底层逻辑。建议在刷题时先自行分析复杂度,再对比LeetCode官方题解的标答,逐步提升分析准确度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值