liveBook · Manning

Dynamic programming

As explained in Machine Learning Algorithms in Depth

Dynamic programming solves problems with overlapping subproblems by storing and reusing solutions to smaller subproblems.

Dynamic programming (DP) solves problems by dividing them into smaller, overlapping subproblems, storing each solution, and reading the final result from a table. Unlike divide and conquer, its subproblems are not independent. section 1.5.2 section 4.2.4

Apply DP by making the subproblem structure explicit:

  • Define the problem states represented by entries in the DP table. section 4.2.4
  • Specify base cases and the recursive relationships or transitions between states. section 4.2.4
  • Populate the table top-down with memoization or bottom-up from the smallest subproblems. section 4.2.4
  • Reuse stored results instead of recomputing previously encountered subproblems. section 4.2.4

Binomial coefficients

For C(n,k), overlapping recursive calls can be memoized top-down or filled iteratively bottom-up. Both approaches compute C(5,2) in O(nk) time and O(nk) space; top-down stores only needed subproblems, while bottom-up fills the table from base cases. section 4.2.4

Figure 4.2 Binomial coefficient C(5,2) recursion
figure

Trace the overlapping recursion for the binomial coefficient C(5,2). section 4.2.4

Listing 4.4 Binomial coefficients
def binomial_coeffs1(n, k):
    #top down DP
    if (k == 0 or k == n):
        return 1
    if (memo[n][k] != -1):
        return memo[n][k]
    memo[n][k] = binomial_coeffs1(n-1, k-1) + binomial_coeffs1(n-1, k)
    return memo[n][k]

def binomial_coeffs2(n, k):
    #bottom up DP
    for i in range(n+1):
        for j in range(min(i,k)+1):
            if (j == 0 or j == i):
                memo[i][j] = 1
            else:
                memo[i][j] = memo[i-1][j-1] + memo[i-1][j]
            #end if
        #end for
    #end for
    return memo[n][k]
def print_array(memo):
    for i in range(len(memo)):
        print('\t'.join([str(x) for x in memo[i]]))

if __name__ == "__main__":
    n = 5
    k = 2
    print("top down DP")
    memo = [[-1 for i in range(6)] for j in range(6)]
    nCk = binomial_coeffs1(n, k)
    print_array(memo)
    print("C(n={}, k={}) = {}".format(n,k,nCk))

    print("bottom up DP")
    memo = [[-1 for i in range(6)] for j in range(6)]
    nCk = binomial_coeffs2(n, k)
    print_array(memo)
    print("C(n={}, k={}) = {}".format(n,k,nCk))

Inspect from-scratch Python implementations of top-down memoization and bottom-up table construction. section 4.2.4

How Machine Learning Algorithms in Depth covers it

How deep it goes
Derives states, transitions, base cases, traversal strategies, complexity, and implementations, stopping short of broader algorithmic applications.
Examples
Uses binomial coefficients, the C(5,2) recursion tree, Python table implementations, and tabular reinforcement-learning value computation.
What it assumes
Assumes familiarity with basic algorithmic paradigms and software implementation, while defining DP mechanics directly.
What you can do after
Build it from scratch by designing DP states, transitions, base cases, memoized recursion, and bottom-up tables.
How it connects
Connects overlapping subproblems and memoization with recursion, Bellman equations, Q-functions, reinforcement learning, and algorithmic paradigms.

Also explained in

Compare how 5 books explain dynamic programming →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Machine Learning Algorithms in Depth. The section links point to the parts of the book it draws on.