Tightening algorithmic time bounds based on execution sequences
Introduction
Motivation
Analyzing an algorithmβs worst-case time complexity can be too pessimistic. Perhaps an algorithm A is usually cheap, but every n executions, it is expensive. Should A be categorized as an expensive algorithm?
Definition
The amortized time cost for A is the average time cost per execution across any sequence of executions of length n for some determined n.
n=1 is worst-case analysis, but larger n can offer more optimistic analysis
Examples
Binary Counter
Problem
We want to store a big binary counter in an array A initialized to 0.
The cost model is every bit flip.
The worst-case time complexity is O(logn) since at worst n becomes a power of 2, and we have to flip all of its significant bits.
Amortized Analysis
Theorem
The amortized time cost is at most 2.
Proof
Consider n (for large n) executions beginning at any state. A0β flips every execution. A1β flips every other execution. A2β flips every 4 executions. Akβ flips every 2k executions.
The total cost is the sum of these flips.
n+2nβ+4nβ+β¦β€2n
Thus, the average per execution is at most 2βO(1).
The βbeginning at any stateβ is important. Otherwise, our analysis is not generalizable.
Expensive Binary Counter
Problem
We want to store a big binary counter in an array A initialized to 0.
It costs 2k to flip bit Akβ.
The worst-case time complexity is O(n) since at worst n becomes a power of 2, and we have to flip all of its significant bits (1+2+4+β¦+2nβ+nβ€2n).
Amortized Analysis
Theorem
The amortized time cost is at most log2β(n)+1.
Proof
Consider n (for large n) executions beginning at any state. A0β flips every execution for a cost of 1. A1β flips every other execution for a cost of 2. A2β flips every 4 executions for a cost of 4. Akβ flips every 2k executions for a cost of 2k.
Thus, the average per execution is βlog2βn+1ββO(logn).
Unbounded Array
Problem
We want to store a linear stream of data. We start with an Array A of memory space of size 1. Every new append inserts into A. If an append is attempted when A is completely filled, we must reallocate memory at double the size and re-insert previous data before inserting the new data.
The worst-case time complexity is O(n) with respect to the data size since at worst, we are in a reallocation step where we must re-insert all old data.
Amortized Analysis
Theorem
The amortized time cost is at most 3.
Proof
Consider n (for large n) executions beginning at any state. Without including the cost of inserting old data, every append costs exactly 1. In total, this has cost n.
Inserting old data costs the size of the old array. In total, this costs at most n+2nβ+β¦β€2n.
Thus, the total cost is at most n+2n=3n and the average cost per execution is 3.
Potentials
Potentials offer a more rigorous approach to amortized analysis. What weβve been doing is still entirely correct, but might be problematically difficult with more complex algorithms.
Intuition: Bankerβs Method
The intuitive way to think about potentials is with a bank. Every time A is executed, we earn some amount of coins k(S) (function on data-structure state). If the current A operation is cheap, we can split our earnings between this operation and saving it in the bank. If the current A operation is expensive, we can use our funds from the bank to support it.
k(S) is an amoritized time bound on A if we will always have enough coins to pay for an operation at any given time.
Definition
The potential function Ξ¦ is a function mapping data-structure states to R. It represents the βpotentialβ of that state (the coin cost).
Let aciβ represent the amortized cost over i algorithm executions. Let ciβ be the cost of the ith execution, and let Siβ being the resulting state.
aciβ=ciβ+Ξ¦(Siβ)βΞ¦(Siβ1β)
Analogically, amortized cost=actual cost+change in potential.