请问有人能够用通俗易懂的语言解释摊销复杂度吗?我在网上找了很久也没有找到确切的定义,也不知道它与算法分析有什么关系。如果有任何有用的信息,即使是外部引用的,都将不胜感激。
请问有人能够用通俗易懂的语言解释摊销复杂度吗?我在网上找了很久也没有找到确切的定义,也不知道它与算法分析有什么关系。如果有任何有用的信息,即使是外部引用的,都将不胜感激。
与非摊销复杂度一样,用于摊销复杂度的大O符号忽略了固定的初始开销和常数性能因素。因此,在评估大O摊销性能时,您通常可以假设任何一系列摊销操作都足够“长”,以便分摊固定的启动费用。具体而言,在std::vector<>
示例中,这就是为什么您不需要担心是否实际遇到N
个额外操作的原因:渐近分析的性质已经假定您会遇到。
除了任意长度,摊销分析不对您正在测量其成本的操作序列进行任何假设--它是对任何可能的操作序列的最坏情况保证。无论操作选择得多么糟糕(比如由恶意对手选择!),摊销分析必须保证一段足够长的操作序列的成本不会持续高于其摊销成本之和。这就是为什么(除非明确说明为限定条件)“概率”和“平均情况”与摊销分析无关--就像它们与普通的最坏情况大O分析一样!
put(x): Push x on the right-hand stack.
y=get(): If the left-hand stack is empty:
Pop each element off the right-hand stack and
push it onto the left-hand stack. This effectively
reverses the right-hand stack onto the left-hand stack.
Pop and return the top element of the left-hand stack.
put
和get
的摊还成本为O(1)
。分析很简单:我总是将put
放在右手堆栈上,并从左手堆栈中获取get
。因此,除了If
子句之外,每个put
都是一个push
,每个get
都是一个pop
,两者都是O(1)
。我不知道我会执行多少次If
子句——它取决于put
和get
的模式——但我知道每个元素都恰好从右手堆栈移动到左手堆栈一次。因此,在n个put
和n个get
的整个序列中,总成本为:n个push
,n个pop
和n个move
,其中move
是pop
后跟一个push
:换句话说,2n个操作(n个put
和n个get
)导致2n个push
和2n个pop
。因此,单个put
或get
的摊销成本是一个push
和一个pop
。2^n
次 - 因为平衡树相当复杂,但每 n 次插入只会发生一次(例如在第 256 次插入时,然后在第 512 次、1024 次等)。在所有其他插入中,复杂度为 O(1) - 是的,每 n 次插入需要 O(n),但只有 1/n
的概率 - 所以我们将 O(n) 乘以 1/n,得到 O(1)。因此,这被称为"摊销复杂度为 O(1)" - 因为随着添加更多元素,重新平衡树所需的时间是最小的。"它有点类似于将算法中不同分支的最坏情况复杂度与执行该分支的概率相乘,并将结果相加。因此,如果某个分支很少被执行,它对复杂度的贡献就会更少。