📚 L8: Amortized Analysis
Lecture Goal
Understand how to analyze the total cost of a sequence of operations, rather than individual operation costs, using three methods: aggregate, accounting, and potential.
The Big Picture
Some data structures have operations that are usually cheap but occasionally expensive. Worst-case analysis would overestimate the total cost. Amortized analysis gives a more realistic picture by averaging costs over a sequence — showing that occasional expensive operations don’t doom overall performance.
1. What is Amortized Analysis?
🤔 The Core Problem
Some data structures have operations with varying costs:
- Most operations are cheap
- Occasionally, an operation is very expensive
- What’s the true cost over many operations?
💡 The Amortized Concept
Amortized Analysis asks: “What is the average cost per operation over a sequence of operations?”
flowchart LR subgraph Operations ["Sequence of n Operations"] O1["Op 1<br/>Cost: 1"] O2["Op 2<br/>Cost: 1"] O3["Op 3<br/>Cost: n"] O4["Op 4<br/>Cost: 1"] On["..."] end Total["Total: O(n) operations<br/>Some expensive, most cheap"] Amortized["Amortized Cost<br/>= Total/n<br/>= O(1)"] Operations --> Total Total --> Amortized style O3 fill:#ff9999 style Amortized fill:#c8e6c9
The Detective Analogy 🕵️
Imagine you’re a detective solving cases. Most cases are easy (P problems) — you follow the clues and solve them quickly. But some cases require checking every possible suspect (expensive operations). However, if you solve 100 cases and only 1 was hard, your average case-solving time is still pretty good. That’s amortized analysis!
📖 Key Terminology
| Term | Definition |
|---|---|
| Worst-case analysis | Considers the most expensive single operation |
| Amortized analysis | Considers average cost per operation over a sequence |
| Average-case analysis | Probabilistic expectation over random inputs |
| Total cost | Sum of costs over all operations |
💡 Warm-Up Example: Dynamic Array
A dynamic array (like Python list or Java ArrayList) doubles in size when full:
flowchart LR subgraph Cheap ["Most Insertions: O(1)"] C1["Insert<br/>Cost: 1"] C2["Insert<br/>Cost: 1"] end subgraph Expensive ["Occasional: O(n)"] E1["Array full!<br/>Double size<br/>Copy all elements"] end Cheap -->|"n operations"| Expensive style Cheap fill:#c8e6c9 style Expensive fill:#ff9999
- Worst-case per insertion: (when array doubles)
- Amortized cost: (expensive operations are rare)
⚠️ Common Pitfalls
Confusing amortized with average-case
Amortized analysis is not probabilistic. It’s a worst-case bound on the average over any sequence of operations. Average-case assumes random inputs; amortized makes no such assumption.
Thinking "worst case × n" is correct
For operations with worst-case each, you might think total is . But the expensive case is rare! The amortized analysis shows total is actually .
❓ Mock Exam Questions
Q1: Amortized vs Worst-Case
If an operation has worst-case cost , what does amortized analysis tell us?
Answer
Answer: Amortized analysis might show average cost per operation is much lower
Because the expensive operation occurs rarely. For dynamic array, worst-case is but amortized is .
2. Aggregate Method
🧠 Core Concept
The Aggregate Method computes the total cost of all operations and divides by .
flowchart TD subgraph Method ["Aggregate Method"] Step1["1. Compute total cost of n operations"] Step2["2. Divide by n"] Step3["3. Result: amortized cost per operation"] end Step1 --> Step2 --> Step3 style Step1 fill:#e1f5fe style Step3 fill:#c8e6c9
Key Idea: No need to track individual costs — just sum everything!
📖 Example 1: Dynamic Array
Starting with an empty array of capacity 1, do insertions:
| Operation | Array Size | Capacity | Cost | Notes |
|---|---|---|---|---|
| 1 | 1 | 1 | 1 | Insert, no resize |
| 2 | 2 | 2 | 2 | Copy 1 + insert 1, capacity doubled |
| 3 | 3 | 4 | 3 | Copy 2 + insert 1 |
| 4 | 4 | 4 | 1 | Insert, no resize |
| 5 | 5 | 8 | 5 | Copy 4 + insert 1 |
Analysis:
flowchart LR subgraph Costs ["Cost Breakdown"] Insert["Insertions: n × 1 = n"] Copy["Copies: 1+2+4+... "] end Total["Total < 3n"] Insert --> Total Copy --> Total style Total fill:#c8e6c9
- Insertions: operations, each costs 1 → Total:
- Copying: Occurs at capacities 1, 2, 4, 8, … → Total: where
- This is a geometric series:
Total cost:
Amortized cost: ✅
📖 Example 2: Binary Counter
A -bit binary counter, incremented times starting from 0.
Key Observation:
| Bit | Flips Every… | Total Flips |
|---|---|---|
| Bit 0 (LSB) | 1 increment | |
| Bit 1 | 2 increments | |
| Bit 2 | 4 increments | |
| Bit | increments |
Total bit flips:
Amortized cost per increment: ✅
⚠️ Common Pitfalls
Forgetting geometric series formula
Sum of powers of 2 is , not . Remember:
Counting each bit flip equally
Higher-order bits flip exponentially less often! Bit flips every operations, not every operation.
❓ Mock Exam Questions
Q2: Binary Counter Bit Flips
In a binary counter incremented times, how many times does bit 3 flip?
Answer
Answer: times
Bit flips every increments. Bit 3 flips every increments, so it flips times.
Q3: Dynamic Array Copy Cost
After insertions into a dynamic array that doubles when full, what is the total copying cost?
Answer
Answer:
Copies occur at sizes 1, 2, 4, 8, … up to first power of 2 ≥ . Total: .
3. Accounting Method
🧠 Core Concept
The Accounting Method assigns an amortized cost to each operation and uses “credits” to pay for expensive operations.
flowchart TD subgraph Cheap ["Cheap Operations"] Charge["Charge amortized cost"] Save["Save surplus as 'credit'"] end subgraph Expensive ["Expensive Operations"] Use["Use saved credits"] Pay["Pay the actual cost"] end Cheap --> Expensive style Cheap fill:#c8e6c9 style Expensive fill:#fff9c4
Key Idea:
- Charge each operation an amortized cost (may differ from actual cost)
- Store surplus as credit for future expensive operations
- Credit must never go negative!
The Savings Account Analogy 💰
Think of it like a savings account. You deposit money during good times (cheap operations), and withdraw during expensive times (expensive operations). The goal is to always have enough saved up — credit never goes negative!
📖 Example 1: Dynamic Array
Strategy: Charge amortized cost of 3 for each insertion.
| Operation | Actual Cost | Amortized Cost | Credit |
|---|---|---|---|
| Insert (no resize) | 1 | 3 | +2 |
| Insert (with resize) | 3 | varies |
Why does this work?
When array is full and needs expansion from size to :
- We need to copy elements → cost =
- We have saved credits from previous insertions (2 credits per element)
- Credits saved ≥ copy cost ✅
flowchart LR subgraph Saving ["Each Insertion"] Charge["Charge: 3"] Use1["Use: 1 (for insert)"] Save["Save: 2 (credit)"] end subgraph Resize ["When Array Full"] Need["Need: k (to copy)"] Have["Have: 2k credits"] end Saving --> Resize style Saving fill:#c8e6c9 style Resize fill:#fff9c4
Result: Total amortized cost = for operations → per operation ✅
📖 Example 2: Binary Counter
Strategy: Charge amortized cost of 2 for each increment.
| What | Cost | Credit |
|---|---|---|
| Set bit 0→1 | 1 | +1 (from the 2 charged) |
| Reset bit 1→0 | 1 | Use saved credit |
Accounting:
- When bit flips 0→1: costs 1, charge 2, save 1 credit
- When bit flips 1→0: costs 1, use the saved credit from when it was set
- Every 1→0 flip is “prepaid” by a previous 0→1 flip
flowchart LR subgraph Bits ["Bit Life Cycle"] Zero["Bit = 0"] One["Bit = 1"] Back["Bit = 0"] end Zero -->|"Set: cost 1, charge 2, save 1"| One One -->|"Reset: cost 1, use saved credit"| Back style Zero fill:#e1f5fe style One fill:#c8e6c9
Result: Amortized cost per increment ≤ 2 → ✅
⚠️ Common Pitfalls
Letting credit go negative
You must prove credit at all times. If credit ever goes negative, your amortized bound is wrong.
Not accounting for all costs
Every actual cost must be paid — either by the amortized charge or by credits. Don’t forget any operations.
❓ Mock Exam Questions
Q4: Accounting Method Credit
For dynamic array with amortized cost 3, how much credit is saved per non-resize insertion?
Answer
Answer: 2 credits
Charge 3, actual cost is 1, so save 2 credits for future resize.
4. Potential Method
🧠 Core Concept
The Potential Method uses a mathematical potential function on the data structure state.
Where:
- = amortized cost of operation
- = actual cost of operation
- = potential after operation
flowchart TD subgraph Formula ["Potential Method Formula"] AC["Amortized Cost"] Actual["Actual Cost c_i"] Delta["Potential Change<br/>Φ(D_i) - Φ(D_{i-1})"] end AC -->|"="| Actual AC -->|"+"| Delta style AC fill:#c8e6c9 style Actual fill:#e1f5fe style Delta fill:#fff9c4
Requirements:
- (initial potential is zero)
- for all (potential never negative)
The Physics Analogy ⚡
Think of potential energy. A ball at the top of a hill has high potential energy — it can “pay” for the descent later. Similarly, the potential function stores “energy” in the data structure for future expensive operations!
📖 Example 1: Dynamic Array
Potential Function:
Case 1: Insert without resize
| Quantity | Before | After |
|---|---|---|
| Size | ||
| Capacity | ||
| Potential |
- Actual cost: 1
- Potential change:
- Amortized cost: ✅
Case 2: Insert with resize (array full)
| Quantity | Before | After |
|---|---|---|
| Size | ||
| Capacity | ||
| Potential |
- Actual cost: (copy) + (insert) =
- Potential change:
- Amortized cost: ✅
📖 Example 2: Binary Counter
Potential Function:
Analysis:
When we increment and flip bits:
- Suppose we flip trailing 1s to 0, and one 0 to 1
- Actual cost: bit flips
- Potential change: lose ones, gain 1 one → change =
- Amortized cost: ✅
flowchart LR subgraph Increment ["Increment Operation"] Before["Before: ...0111"] After["After: ...1000"] end subgraph Analysis ["Analysis"] Flips["Flip t 1s to 0<br/>Flip one 0 to 1"] Potential["Potential: -t + 1"] Amortized["Amortized: t+1 + (-t+1) = 2"] end Increment --> Analysis style Amortized fill:#c8e6c9
⚠️ Common Pitfalls
Choosing a potential that goes negative
You must prove for all states. If potential ever goes negative, the bound is invalid.
Forgetting the initial condition
must hold for the initial state. This ensures the total amortized cost bounds the total actual cost.
❓ Mock Exam Questions
Q5: Potential Method Formula
In the potential method, what is the formula for amortized cost?
Answer
Answer:
Amortized cost = actual cost + potential change.
Q6: Potential Change Interpretation
If actual cost is 10 and potential decreases by 3, what is the amortized cost?
Answer
Answer:
Amortized cost = . When potential decreases, we “spend” saved energy, reducing the amortized cost.
5. Comparison of Methods
🧠 Core Concept
| Method | Approach | Best For |
|---|---|---|
| Aggregate | Sum all costs, divide by | Simple sequences with predictable patterns |
| Accounting | Assign amortized costs, track credit | Intuitive understanding, teaching |
| Potential | Mathematical potential function | Rigorous proofs, complex data structures |
flowchart TD subgraph Methods ["Three Methods"] Agg["Aggregate Method<br/>Sum and divide"] Acc["Accounting Method<br/>Credits and charges"] Pot["Potential Method<br/>Mathematical function"] end Result["All give same<br/>asymptotic bound"] Agg --> Result Acc --> Result Pot --> Result style Result fill:#c8e6c9
📖 When to Use Each Method
| Scenario | Recommended Method | Why |
|---|---|---|
| Simple, uniform operations | Aggregate | Easy to calculate total |
| Need intuitive explanation | Accounting | Credit concept is easy to explain |
| Complex data structures | Potential | Mathematical rigor |
| Proving bounds for publications | Potential | Most rigorous method |
| Teaching amortized analysis | Accounting | Most intuitive |
The Budgeting Analogy 📊
- Aggregate is like calculating your total yearly expenses and dividing by 12 to get monthly average
- Accounting is like budgeting with envelopes — setting aside money each month for known future expenses
- Potential is like physics equations — tracking energy levels precisely
⚠️ Common Pitfalls
Using the wrong method for the problem
Some problems are easier with one method. Practice all three to know which to use.
Inconsistent bounds between methods
All three methods should give the same asymptotic bound. If not, check your analysis.
6. Classic Examples Summary
📊 Dynamic Array (Resizable Array)
| Method | Analysis | Result |
|---|---|---|
| Aggregate | Total = | |
| Accounting | Charge 3, save 2 for resize | |
| Potential |
📊 Binary Counter
| Method | Analysis | Result |
|---|---|---|
| Aggregate | Total flips < | |
| Accounting | Charge 2, save 1 per 1-bit | |
| Potential | = number of 1-bits |
🔗 Connections
| 📚 Lecture | 🔗 Connection |
|---|---|
| L9 | Amortized analysis used in analyzing complex data structures |
The Key Insight
Amortized analysis reveals that occasional expensive operations don’t doom overall performance. By averaging over a sequence, we see the true cost — like understanding that a gym membership’s value comes from consistent use, not just one intense session! 🏋️
7. Mock Exam Questions
📝 Multiple Choice
Q1: Amortized vs Worst-Case
A dynamic array has worst-case insertion cost . What is the amortized insertion cost?
Answer
Answer: (b)
Even though some insertions cost when the array resizes, the amortized cost over operations is because resizing happens rarely (logarithmically many times).
Q2: Aggregate Method
In the aggregate method, how do we compute amortized cost?
Answer
Answer: (b) Sum total cost and divide by
The aggregate method computes the total cost of all operations and divides by to get average cost per operation.
Q3: Binary Counter
A binary counter incremented times has how many total bit flips?
Answer
Answer: (b)
Bit flips times. Total: .
✍️ Open-Ended Practice Problems
P1: Stack with Multipop
A stack supports push (cost 1) and multipop() (cost where is stack size). Using any method, show that a sequence of operations has amortized cost per operation.
Answer
Solution (Accounting Method):
- Charge amortized cost 2 for push, 0 for multipop
- Each push saves 1 credit on the element
- Each pop uses 1 credit from the element
- Total pushes , so total credits total pops
- Credit never goes negative
- Amortized cost:
- Per operation: ✅
Solution (Potential Method):
Define size of stack
- Push: cost 1, , amortized =
- Multipop(): cost , , amortized =
Both operations have constant amortized cost ✅
P2: Potential Method for Dynamic Array
(a) Define a potential function for a dynamic array and prove that insert without resize has amortized cost .
(b) Using the same potential function, prove that insert with resize also has amortized cost .
Answer
Potential Function:
Initial state: (empty array) ✅
Non-negative: Since size capacity, ✅
(a) Insert without resize:
- Actual cost: 1
- Size increases by 1, capacity unchanged
- Amortized cost: ✅
(b) Insert with resize (array was full):
- Before: size = , capacity =
- After: size = , capacity =
- Actual cost: (copy) + (insert) =
- Amortized cost: ✅
P3: Comparing Methods
Explain why all three methods (aggregate, accounting, potential) give the same asymptotic bound for the dynamic array.
Answer
All three methods bound the same quantity: total actual cost.
- Aggregate: Directly counts total cost <
- Accounting: Total amortized cost = , and credit means total actual total amortized
- Potential: Total amortized = Total actual + , and means total actual total amortized
All methods show total cost = , so per-operation = .