Dynamic Programming Algorithm Explained

Kamal Weheliye
3 min readJun 27, 2024

--

Dynamic programming (DP) is an algorithmic technique used for solving complex problems by breaking them down into simpler subproblems. It is particularly effective for optimization problems where the solution can be constructed from solutions to overlapping subproblems. DP leverages the principle of storing the results of already solved subproblems (memoization) to avoid redundant calculations, thus improving efficiency.

Key Principles of Dynamic Programming

  • Overlapping Subproblems: Problems that can be broken down into subproblems, which are reused several times. For instance, calculating Fibonacci numbers involves repeatedly calculating the same smaller Fibonacci numbers.
  • Optimal Substructure: The optimal solution of the problem can be derived from the optimal solutions of its subproblems. For example, in the shortest path problem, the shortest path to the destination can be found by combining the shortest paths to intermediate points.

Dynamic Programming Techniques

  • Memoization: A top-down approach where you solve the problem recursively and store the results of subproblems to avoid recalculating them. It uses a cache or dictionary to store results.
  • Tabulation: A bottom-up approach where you solve the problem iteratively, starting from the smallest subproblems and building up solutions to larger subproblems. It uses a table (usually an array) to store results.

Example in Java: Fibonacci Sequence

The Fibonacci sequence is a series where each number is the sum of the two preceding ones, typically starting with 0 and 1. The nth Fibonacci number can be defined as:

F(n)= F(n−1) + F(n−2)

Fibonacci Using Memoization (Top-Down Approach)

public class FibonacciMemoization {
private Map<Integer, Long> memo = new HashMap<>();

public long fib(int n) {
if (n <= 1) return n;
if (memo.containsKey(n)) return memo.get(n);

long result = fib(n - 1) + fib(n - 2);
memo.put(n, result);
return result;
}
}

In this implementation:

  • We use a HashMap to store already computed Fibonacci numbers to avoid redundant calculations.
  • The fib method checks if the result for n is already computed; if so, it returns the stored result, otherwise, it calculates F(n-1) + F(n-2) recursively.

Fibonacci Using Tabulation (Bottom-Up Approach)

public class FibonacciTabulation {
public long fib(int n) {
if (n <= 1) return n;

long[] dp = new long[n + 1];
dp[0] = 0;
dp[1] = 1;

for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}

return dp[n];
}
}

In the tabulation approach:

  • We use an array dp to store Fibonacci numbers from 0 to n.
  • We iterate from 2 to n and calculate each Fibonacci number iteratively based on previously computed values.

When to Use Dynamic Programming

Dynamic programming is particularly useful in the following scenarios:

  1. Optimization Problems: Problems where you need to find the best solution among many possible solutions. DP helps in efficiently finding the optimal solution by breaking down the problem into smaller subproblems.
  2. Problems with Overlapping Subproblems: Problems where the same subproblems are solved multiple times. DP stores the results of subproblems to avoid redundant calculations, thus saving time and computational resources.
  3. Problems with Optimal Substructure: Problems where the optimal solution to the problem can be constructed from the optimal solutions of its subproblems.

Benefits of Using Dynamic Programming:

  • Efficiency: By storing the results of subproblems, DP avoids redundant calculations, making the algorithm more efficient.
  • Optimal Solutions: DP guarantees finding the optimal solution if the problem has an optimal substructure.
  • Simplicity: Once the subproblems are defined, the implementation of DP can be straightforward and easy to understand.

Conclusion

Dynamic programming is a fundamental technique for solving complex optimization problems efficiently. By breaking down problems into smaller subproblems and storing solutions, dynamic programming provides an elegant approach to solving a wide range of problems, including the Fibonacci sequence example demonstrated here. Understanding its principles and when to apply it can significantly enhance your ability to tackle challenging problems in computer science and beyond.

--

--

No responses yet