Bottom Up Dynamic Programming vs Top Down Dynamic Programming

What are the difference between bottom up dynamic programming and top down dynamic programming? In bottom up Dynamic Programming algorithm, for example with Fibonacci:

  1. Memoize
  2. Go from smaller to larger value of n.

We always go from smaller to larger value for Bottom Up DP and larger to smaller values for Top Down DP. With top down recursion is that we go from bigger values to smaller values till we hit the base case. Let’s consider both approaches for finding fibonacci.

Top Down Dynamic Programming

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
F = []
FIBONACCI(n)
  if F[n] == null
    if n==0 //base case
      F[n] = 0
    else if n==1 //base case
      F[n] = 1
    else
      F[n] = FIBONACCI(n-1) + FIBONACCI(n-2)
  return F[n]

Now in this algorithm you can see that we have made an blank array, and make recursive calls till we reach 0 or 1, when we reach 0 and 1 we fill these values and backfilling starts. This is how top down dynamic programming and recursion works.

Language Agnostic Coding Drills

The code is a dynamic programming approach to solve the Fibonacci series. We can break it down into smallest units of learning:

  1. Understanding Variables and Arrays: The algorithm uses an array F to store the computed Fibonacci values. This is to avoid repeated calculations.

    • Drill: Write a program that declares an array, adds values to it, and retrieves values from it.
  2. Understanding Conditional Statements (if-else): The code uses if-else statements to check for base cases (when n is 0 or 1) and to decide when to calculate the Fibonacci number.

    • Drill: Write a program that uses conditional statements to check and perform operations based on certain conditions.
  3. Understanding Function Definitions and Calls: The Fibonacci calculation is wrapped in a function, which is recursively called to calculate Fibonacci numbers.

    • Drill: Write a simple function and learn how to call it from your program.
  4. Understanding Recursion: The code calculates Fibonacci numbers using a recursive approach, which means the function calls itself until it reaches the base case.

    • Drill: Write a recursive function, such as one that calculates the factorial of a number.
  5. Understanding Base Cases in Recursion: In any recursive algorithm, base cases that stop the recursion are crucial. In this case, the base cases are when n is 0 or 1.

    • Drill: Modify your factorial function to handle the base case (factorial of 0 or 1).
  6. Understanding Dynamic Programming: The algorithm uses a technique called memoization, a key element of dynamic programming, to store the results of expensive function calls and reuse them when needed instead of recomputing.

    • Drill: Modify your factorial function to use memoization. Store the results of each calculation in an array and before performing a calculation, check if the result is already in the array.
  7. Implementing the Fibonacci Algorithm: The final step is to bring all these concepts together to write a function that calculates Fibonacci numbers using recursion and dynamic programming.

    • Drill: Write the Fibonacci function using dynamic programming.

These drills start from basic programming concepts like variables, arrays, and conditional statements, and gradually introduce more advanced topics like recursion and dynamic programming, finally culminating in the implementation of a well-known algorithm.

Solution for Coding Drills in Python

The breakdown and drills provided in the last response are about the Fibonacci series algorithm, not the N-Queens problem. Here’s how you would code the drills in Python for Fibonacci:

  1. Understanding Variables and Arrays:
    • Declaring, adding to, and retrieving from an array:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Declare an array
F = []

# Add values to array
F.append(1)
F.append(2)

# Retrieve values from array
print(F[0])  # Output: 1
print(F[1])  # Output: 2
  1. Understanding Conditional Statements (if-else):
    • Check and perform operations based on certain conditions:
1
2
3
4
5
6
7
8
# If-else conditions
n = 5
if n > 10:
    print("n is greater than 10")
elif n < 10:
    print("n is less than 10")
else:
    print("n is 10")
  1. Understanding Function Definitions and Calls:
    • Define and call a simple function:
1
2
3
4
5
# Function definition and call
def greet(name):
    print("Hello, " + name)

greet("Alice")  # Output: Hello, Alice
  1. Understanding Recursion:
    • Recursive function to calculate the factorial of a number:
1
2
3
4
5
6
7
8
# Recursive factorial function
def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n-1)

print(factorial(5))  # Output: 120
  1. Understanding Base Cases in Recursion:
    • Factorial function with base cases:
1
2
3
4
5
6
7
8
9
# Recursive factorial function with base case
def factorial(n):
    if n == 0 or n == 1:
        return 1
    else:
        return n * factorial(n-1)

print(factorial(0))  # Output: 1
print(factorial(1))  # Output: 1
  1. Understanding Dynamic Programming:
    • Factorial function with memoization:
1
2
3
4
5
6
7
8
9
# Recursive factorial function with memoization
def factorial(n, memo = {}):
    if n == 0 or n == 1:
        return 1
    if n not in memo:
        memo[n] = n * factorial(n-1)
    return memo[n]

print(factorial(5))  # Output: 120
  1. Implementing the Fibonacci Algorithm:
    • Fibonacci function with dynamic programming:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Initialize memoization array
F = {}

def fibonacci(n):
    if n in F:
        return F[n]
    if n <= 1:
        F[n] = n
    else:
        F[n] = fibonacci(n-1) + fibonacci(n-2)
    return F[n]

print(fibonacci(10))  # Output: 55

Bottom Up Dynamic Programming

1
2
3
4
5
6
7
8
9
F = []
FIBONACCI(n)
  F[0] = 0 //Initial values
  F[1] = 1 //Initial values

  for i in 2 to n
    F[i] = F[i-1] + F[i-2]

  return F[n]

Here you can see that we store initial values first in the array and use then for calculating further values, i.e from smaller values, we calculate bigger values, this is how bottom up dynamic programming works.

And you can keep this as rule that we always go from smaller to larger value for Bottom Up Dynamic Programming and larger values to smaller values for Top Down Dynamic Programming.

Language Agnostic Coding Drills

This algorithm is a bottom-up dynamic programming solution to calculate the Fibonacci series. Here are the smallest units of learning and corresponding drills:

  1. Understanding Variables and Arrays: The algorithm uses an array F to store the computed Fibonacci values. This is to avoid repeated calculations.

    • Drill: Write a program that declares an array, adds values to it, and retrieves values from it.
  2. Understanding Looping Constructs (for loop): The for loop is used to iterate over a range of numbers from 2 to n and calculate the Fibonacci series.

    • Drill: Write a program that uses a for loop to iterate over a range of numbers and perform some operation.
  3. Understanding Array Indexing: The Fibonacci sequence is stored in an array, and each number is retrieved by indexing into the array.

    • Drill: Write a program that stores a sequence of numbers in an array and retrieves them by indexing into the array.
  4. Understanding Base Case Initialization: The first two numbers of the Fibonacci sequence are defined as base cases and initialized at the start.

    • Drill: Write a program that initializes an array with a certain base case and then builds on it.
  5. Implementing the Fibonacci Algorithm: The final step is to bring all these concepts together to write a function that calculates Fibonacci numbers using iterative dynamic programming.

    • Drill: Write the Fibonacci function using iterative dynamic programming.

These drills start from basic programming concepts like variables, arrays, and looping constructs, and gradually introduce more advanced topics like array indexing and dynamic programming, finally culminating in the implementation of a well-known algorithm.

Solution for Coding Drills in Python

Here are the codeing drills in Python:

  1. Understanding Variables and Arrays:
    • Declaring, adding to, and retrieving from an array:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Declare an array
F = []

# Add values to array
F.append(1)
F.append(2)

# Retrieve values from array
print(F[0])  # Output: 1
print(F[1])  # Output: 2
  1. Understanding Looping Constructs (for loop):
    • Using a for loop to iterate over a range of numbers and perform some operation:
1
2
3
# For loop
for i in range(5):
    print(i)  # Output: 0, 1, 2, 3, 4
  1. Understanding Array Indexing:
    • Storing a sequence of numbers in an array and retrieving them by indexing into the array:
1
2
3
4
# Array indexing
numbers = [5, 10, 15, 20, 25]
for i in range(len(numbers)):
    print(numbers[i])  # Output: 5, 10, 15, 20, 25
  1. Understanding Base Case Initialization:
    • Initializing an array with a certain base case and then building on it:
1
2
3
# Base case initialization
F = [0, 1]
print(F)  # Output: [0, 1]
  1. Implementing the Fibonacci Algorithm:
    • Writing the Fibonacci function using iterative dynamic programming:
1
2
3
4
5
6
7
def fibonacci(n):
    F = [0, 1] + [0] * (n - 1)
    for i in range(2, n + 1):
        F[i] = F[i - 1] + F[i - 2]
    return F[n]

print(fibonacci(10))  # Output: 55

Advantages of Bottom Up Dynamic Programming

The advantages of bottom up dynamic programming over top-down are:

  • Bottom-up approach is easy to debug.
  • Bottom-up approach is difficult to implement but it is usually faster.
  • Bottom up implementations have a better performance, and top-down approach often results in worse complexities.
  • We can in certain cases avoid recursion by using iterative bottom up dynamic programming, and using bottom up approach, using certain specific data structures we can improve performance and we can get a better and optimal complexity.
  • So performance wise top-down is generally worse in case of complexity.