Dynamic Programming in TheAlgorithms/Python: Memoization vs Tabulation Explained

Memoization caches recursive subproblem results on first computation while tabulation iteratively fills a DP table from base cases upward, with both patterns implemented throughout the repository.

TheAlgorithms/Python is a comprehensive collection of algorithmic implementations showcasing classic optimization techniques. Understanding how this codebase utilizes memoization versus tabulation provides concrete examples of when to apply each dynamic programming approach to problems like rod cutting, knapsack optimization, and sequence generation.

Memoization: Top-Down Recursive Caching

Memoization (top-down) solves subproblems recursively and stores each result immediately after computation. Subsequent calls retrieve the cached value instead of recalculating.

In dynamic_programming/rod_cutting.py, the top_down_cut_rod function demonstrates this pattern by initializing a max_rev array with negative infinity as a sentinel value. The helper function _top_down_cut_rod_recursive checks if max_rev[n] >= 0 to determine whether to return the cached result or compute it.

def top_down_cut_rod(n: int, prices: list):
    _enforce_args(n, prices)
    max_rev = [float("-inf") for _ in range(n + 1)]
    return _top_down_cut_rod_recursive(n, prices, max_rev)

def _top_down_cut_rod_recursive(n: int, prices: list, max_rev: list):
    if max_rev[n] >= 0:                # ← cached value

        return max_rev[n]
    if n == 0:
        return 0
    best = float("-inf")
    for i in range(1, n + 1):
        best = max(best,
                   prices[i - 1] + _top_down_cut_rod_recursive(n - i,
                                                               prices,
                                                               max_rev))
    max_rev[n] = best                  # ← store for future calls

    return best

Source: [rod_cutting.py lines 56-71](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/rod_cutting.py#L56-L71)

The mf_knapsack function in dynamic_programming/knapsack.py implements a similar global caching strategy. It uses a 2D table f initialized with -1 to indicate uncomputed states, recursively filling entries only when f[i][j] < 0.

def mf_knapsack(i, wt, val, j):
    global f
    if f[i][j] < 0:                     # not computed yet

        if j < wt[i - 1]:
            val = mf_knapsack(i - 1, wt, val, j)
        else:
            val = max(mf_knapsack(i - 1, wt, val, j),
                      mf_knapsack(i - 1, wt, val, j - wt[i - 1]) + val[i - 1])
        f[i][j] = val                   # cache result

    return f[i][j]

Source: [knapsack.py lines 10-26](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/knapsack.py#L10-L26)

Tabulation: Bottom-Up Iterative Filling

Tabulation (bottom-up) eliminates recursion by iteratively filling a table from the smallest subproblems upward. This guarantees each entry is computed exactly once with better cache locality.

The bottom_up_cut_rod function in dynamic_programming/rod_cutting.py initializes max_rev[0] = 0 as the base case. Nested loops then compute each length i from previously solved smaller lengths i-j.

def bottom_up_cut_rod(n: int, prices: list):
    _enforce_args(n, prices)
    max_rev = [float("-inf")] * (n + 1)
    max_rev[0] = 0                      # base case

    for i in range(1, n + 1):
        best = max_rev[i]               # initially −inf

        for j in range(1, i + 1):
            best = max(best,
                        prices[j - 1] + max_rev[i - j])
        max_rev[i] = best
    return max_rev[n]

Source: [rod_cutting.py lines 33-44](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/rod_cutting.py#L33-L44)

For the 0/1 knapsack problem, the knapsack function creates a full 2D DP table and populates it row-by-row. This avoids the overhead of recursive calls and stack frames entirely.

def knapsack(w, wt, val, n):
    dp = [[0] * (w + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for cap in range(1, w + 1):
            if wt[i - 1] <= cap:
                dp[i][cap] = max(val[i - 1] + dp[i - 1][cap - wt[i - 1]],
                                 dp[i - 1][cap])
            else:
                dp[i][cap] = dp[i - 1][cap]
    return dp[n][w], dp

Source: [knapsack.py lines 29-39](https://github.com/TheAlgorithms/Python/blob/master/dynamic_programming/knapsack.py#L29-L39)

The Fibonacci class in dynamic_programming/fibonacci.py demonstrates pure tabulation by maintaining an internal list self.sequence and extending it iteratively, computing each new term from the two preceding values.

Choosing Between Memoization and Tabulation

Memoization suits problems with naturally recursive definitions and sparse state spaces where many theoretical subproblems remain unsolved. The repository demonstrates this in mf_knapsack where the global cache avoids recomputation only for visited states.

Tabulation performs better when the subproblem space is dense, requiring every state to be computed anyway. The bottom-up rod cutting and iterative knapsack implementations leverage contiguous memory access patterns that improve CPU cache performance over recursive pointer chasing.

TheAlgorithms/Python provides both implementations for the same problems side-by-side, enabling direct performance and readability comparisons across rod_cutting.py and knapsack.py.

Summary

  • Memoization uses recursion with sentinel-checked caches (e.g., max_rev[n] >= 0 or f[i][j] < 0) to store results on first computation
  • Tabulation iteratively fills DP tables from base cases, eliminating recursion overhead and improving cache locality
  • The repository implements both approaches for classic problems like rod cutting and knapsack, allowing developers to compare trade-offs
  • Choose memoization for sparse state spaces with natural recursive formulations; prefer tabulation for dense subproblem domains requiring complete table population

Frequently Asked Questions

What is the primary difference between memoization and tabulation?

Memoization employs a top-down recursive approach that caches results as they are computed during the descent, while tabulation uses a bottom-up iterative approach that fills a table starting from known base cases. TheAlgorithms/Python demonstrates memoization in top_down_cut_rod with its recursive helper checking cached values, versus the iterative loops of bottom_up_cut_rod.

When should I use memoization over tabulation in Python?

Use memoization when the problem has a naturally recursive structure and the state space is sparse, meaning many theoretical subproblems never need computation. The mf_knapsack function illustrates this by only populating cache entries for reachable item-capacity combinations rather than the full Cartesian product.

Does tabulation always provide better performance than memoization?

Tabulation generally offers superior performance for dense subproblem spaces due to eliminated recursion overhead and better CPU cache locality, as seen in the iterative knapsack implementation. However, for sparse problems where few states are visited, memoization avoids computing unnecessary table entries, potentially saving both time and memory.

How does TheAlgorithms/Python handle base cases in dynamic programming?

The repository consistently initializes base cases explicitly before computation begins. In tabulation implementations like bottom_up_cut_rod, max_rev[0] = 0 establishes the foundation before loops fill subsequent entries. For memoization, base cases typically return constants immediately before any cache checks occur, ensuring termination of the recursive descent.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →