复杂度
复杂度是我们衡量一个算法好坏的重要的标准。在算法竞赛中,我们通常关注于算法的时间复杂和空间复杂度。
一般的来说,复杂度是一个关于输入长度的一个函数。对于某些算法来说,相同的输入的不同输入依然会造成算法的运行时间/空间的不同,因此我们通常使用算法的最坏时间复杂度,记为 。对于一些特殊的情况,我们可能会关心它的平均情况复杂复杂度(特别是对于随机算法 (randomized algorithm)),这个时候我们通过使用随机分析 (probabilistic analysis) 来得到期望的复杂度。
渐进符号
我们通常使用渐进符号来描述一个算法的复杂度。
符号
对于给定的一个函数 , 函数集合 定义为
也就是说,如果函数 属于 ,那么我们能找到两个正常数 使得 被 和 夹在中间。因为 是一个函数集合,我们可以用 表达 属于 ,但是我们通常使用 。
符号
符号同时给了我们一个函数的上下界,如果我们只有一个函数的渐进上界的时候,我们使用 符号。对于一个给定的函数 , 我们把它记作 。
符号
同样的,我们使用 符号来描述一个函数的渐进下界。
常见性质
- 任何对数函数无论底数为何,都具有相同的增长率。
主定理 (Master Theorem)
我们可以使用 Master Theorem 来快速的求得关于递归算法的复杂度。 假设我们有递推关系式
那么
均摊复杂度
算法往往是会对内存中的数据进行修改的,而同一个算法的多次执行,就会通过对数据的修改而互相影响。
例如快速排序中的“按大小分类”操作,单次执行的最坏时间复杂度,看似是 的。 但是由于快排的分治过程,先前的“分类”操作每次都减小了数组长度,所以实际的总复杂度 ,分摊在每一次“分类”操作上,是 。
多次操作的总复杂度除以操作次数,就是这种操作的 均摊复杂度 。
势能分析
势能分析,是一种求均摊复杂度下界的方法。 求均摊复杂度,关键是表达出先前操作对当前操作的影响。势能分析用一个函数来表达此种影响。
定义“状态” :即某一时刻的所有数据。在快排的例子中,一个“状态”就是当前过程需要排序的下标区间
定义“初始状态” :即未进行任何操作时的状态。在快排的例子中,“初始状态”就是整个数组
假设存在从状态到数的函数 ,且对于任何状态 , ,则有以下推论:
设 为从 开始连续做 次操作所得的状态序列, 为第 次操作的时间开销。
记 ,则 次操作的总时间花销为
(正负相消,证明显然)
又因为 ,所以有
因此,若 ,则 是均摊复杂度的一个下界。
势能分析使用中有很多技巧,案例在此不题。
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用