SICP example Counting change, cannot understand
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
The SICP "counting change" example demonstrates recursive problem decomposition, not just coin counting. It computes how many different combinations of coins can make a target amount, using a recursive split: include current coin denomination or skip it. Many learners get stuck because they expect the function to return one best combination rather than the number of valid combinations.
Core Sections
Core recursive idea
At each step with amount n and k coin types:
- count ways excluding coin
k, - count ways including coin
kat least once, - sum the two counts.
Base cases:
- amount
0-> one valid way, - amount
< 0-> no way, - no coin types left -> no way.
Python equivalent of SICP pattern
This mirrors the SICP structure.
Why it works
The two recursive branches partition solution space without overlap:
- branch A never uses coin
k, - branch B uses coin
kat least once.
No combination belongs to both branches.
Time complexity and optimization
Naive recursion has repeated subproblems. Memoization or dynamic programming improves efficiency.
Pedagogical purpose
SICP uses this example to teach abstraction and recursive process modeling, not production-grade performance.
Common Pitfalls
- Interpreting output as minimum coins instead of number of combinations.
- Missing base cases and causing infinite recursion or wrong counts.
- Assuming branches overlap and double-count solutions.
- Getting confused by denomination indexing direction.
- Ignoring repeated-subproblem cost in naive recursion.
Implementation Playbook
When studying recursive combinatorics problems, write the decision partition in plain language before coding. This reduces confusion between "count ways" and "find best" formulations. Add tiny test cases (amount=0, small values with known answers) to verify base-case behavior early.
After conceptual correctness, instrument function call counts to observe growth and motivate memoization. Translating the same recurrence to bottom-up DP is a useful next step because it reinforces that recursion and DP solve identical subproblems with different execution order. Keep both implementations for learning and benchmark comparisons.
Operational Readiness
Converting a technically correct implementation into a reliable production behavior requires explicit operational guardrails. Begin by defining success criteria in measurable terms: expected output shape, acceptable latency range, and acceptable failure rate under normal load. Then build a minimal verification harness that exercises the same code path with deterministic fixtures so behavioral drift is detected early when dependencies or runtime versions change. This harness should run quickly enough to execute on every change and should fail loudly when assumptions break.
Next, establish observability that captures both correctness and health. Structured logs should include correlation identifiers, key decision branches, and error classifications. Metrics should track throughput, latency percentiles, and error categories relevant to this workflow. If external integrations are involved, include dependency status and timeout counters so incident triage can isolate whether failures originate locally or downstream. Avoid relying on manual spot checks because intermittent regressions are often timing-sensitive and disappear outside repeatable test conditions.
Finally, define a controlled rollout and rollback process. Deploy incrementally, compare live metrics against baseline, and keep rollback criteria explicit before release starts. Store configuration assumptions in a short runbook so future maintainers can reproduce intended behavior quickly. A disciplined rollout model dramatically reduces recovery time when unexpected behavior appears after infrastructure, network, or platform changes.
Summary
SICP counting-change counts combinations through recursive decomposition of include/exclude decisions. The main conceptual win is understanding subproblem structure; performance optimization comes later with memoization or DP.
Related reading
- Sieve of Eratosthenes algorithm in JavaScript running endless for large number
- Sieve optimization
- Similar String algorithm
- similarity between two vectors representing star graphs
- Simple algorithm tutorials?
- Simple Popularity Algorithm
- Simple ranking algorithm
- Simple way to find if two different lists contain exactly the same elements?

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.