Code Katas
Focused, repeatable coding exercises across every paradigm from this module. Complete each kata multiple times until it is automatic.
Format for every kata:
- Time limit: as stated per kata
- Polya comments: in your code, include four comments:
# Understand,# Plan,# Invariant/Recurrence,# Reflect - Tests: write at least three test cases before coding, including one edge case and (where possible) one stress-test comparison with a brute-force oracle
Kata 1: Loop Invariants in Practice (insertion sort)
Time limit: 20 minutes
Goal: Implement insertion sort from scratch with a written loop invariant and three tests.
Setup: Do not look at reference code.
Implementation plus at the top of the function a comment stating the outer-loop invariant. Tests: empty, single element, reverse-sorted, already-sorted.
Repeat until: you can write it and the invariant in under 10 minutes without reference.
Kata 2: Binary Search Variants
Time limit: 25 minutes
Goal: Implement three binary-search variants in one session.
Setup: A sorted list of integers, possibly with duplicates.
Write:
find(A, x): return any indexiwithA[i] == x, or-1.lower_bound(A, x): return the smallestiwithA[i] >= x(len(A)if none).upper_bound(A, x): return the smallestiwithA[i] > x.
Each must use explicit lo, hi with a single, stated invariant. Include at least two tests that stress boundary behavior (duplicates, target absent, target smaller/larger than every element).
Repeat until: no off-by-one bugs and all three variants correct on first run.
Kata 3: Divide-and-Conquer Mergesort and Inversion Count
Time limit: 30 minutes
Goal: Implement mergesort, extend it to count inversions, and prove the recurrence.
Setup: A list of integers.
Implement merge_sort(A) returning a sorted copy, then modify to also return the number of inversions (pairs i < j with A[i] > A[j]). Include a comment stating the recurrence T(n) = 2 T(n/2) + n and its solution.
Tests: empty list, all-equal list, reverse-sorted (maximum inversions), random list compared to brute force O(n^2) counter.
Repeat until: you write mergesort from scratch in under 10 minutes and the inversion extension in under 10 more.
Kata 4: Greedy Activity Selection with Oracle
Time limit: 20 minutes
Goal: Implement the earliest-finish-time greedy and validate against a brute-force oracle.
Setup: A list of (start, finish) activities.
Implement:
greedy_select(activities): returns the maximum-size mutually compatible subset by earliest-finish-time.brute_select(activities): enumerates all2^nsubsets, returns the largest compatible one.- Stress test both on 200 random inputs with
n <= 12.
Repeat until: zero mismatches on 500 rounds and you can explain the exchange argument from memory.
Kata 5: Backtracking N-Queens
Time limit: 25 minutes
Goal: Solve N-queens by backtracking with explicit extend / check / undo structure.
Setup: Integer n, return one valid placement (or all placements).
Use a cols array to track column choices per row. Attack test should consider column and both diagonals. Include # Invariant comment: "cols[0..r-1] is a valid partial placement".
Tests: n=1 (1 solution), n=2 and n=3 (0 solutions), n=4 (2 solutions), n=8 (92 solutions).
Repeat until: correct count for n=8 and clean recursion with no state leakage.
Kata 6: Dynamic Programming Minimum Coin Change
Time limit: 25 minutes
Goal: Implement coin change both top-down (memoization) and bottom-up (tabulation).
Setup: A list of denominations C and target T.
Include both:
min_coins_topdown(C, T)usingfunctools.lru_cachemin_coins_bottomup(C, T)using adparray
State the recurrence dp[t] = 1 + min(dp[t - c] for c in C if c <= t) with base dp[0] = 0.
Tests: T = 0, unreachable target (should return -1 or inf), simple cases ([1,5,10,25], 30), adversarial case [1,3,4], 6 where greedy-by-largest fails.
Repeat until: both implementations agree on 300 random small inputs.
Kata 7: Amortized Dynamic Array
Time limit: 20 minutes
Goal: Implement a dynamic array with doubling, and verify amortized O(1) cost empirically.
Setup: A custom DynArray class in Python.
Implement append, __getitem__, and internal _resize. Record the number of element writes (actual writes plus copies) for n = 1, 2, 4, ..., 2^20 appends. Plot or print total_writes / n; it should stay below a small constant.
Tests: verify len behaviour, element preservation across resizes, and that the write count matches the aggregate analysis bound.
Repeat until: you can explain the accounting-method credit assignment (bank 2 per append) from memory.
Kata 8: Paradigm Triage Timed Sprint
Time limit: 30 minutes total
Goal: Pick a paradigm and sketch an approach for eight unfamiliar problems in under 4 minutes each.
Setup: Any set of eight problems you have not solved before (take them from exercises.md).
For each problem write:
- task type (decision / optimization / counting / construction / enumeration)
- paradigm choice
- one-sentence justification
- expected runtime
Do not implement anything. The goal is triage speed, not finishing.
Repeat until: you complete eight triages in under 30 minutes with reasonable paradigm choices.
Completion Standard
- All eight katas completed at least once
- Katas 1, 2, 4, 5, 6 completed three or more times, within time limit, from scratch
- Stress tests pass for all katas that include a brute-force oracle
- Polya comments present in every final solution
- You can recite the core pattern (invariant / recurrence / exchange / state) for every kata without reference
Additional Fluency Katas
Kata 9: Big-O Repair Sheet
Implement ten tiny functions that match the common loop shapes from the complexity workshop. Beside each function, write the exact count for a small input, the asymptotic bound, and one sentence saying whether the algorithm is practical for n = 10^6.
Kata 10: Paradigm Explainer
For the same problem, write four approaches: brute force, greedy if plausible, divide-and-conquer if plausible, and dynamic programming if plausible. You may use a small scheduling, coin-change, or path problem. Mark invalid approaches explicitly and include a counterexample for each invalid greedy claim.