Dynamic Programming: Md. Bakhtiar Hasan
Dynamic Programming: Md. Bakhtiar Hasan
Part I
Lecturer
Department of Computer Science and Engineering
Islamic University of Technology
Motivation
Fibonacci Series
F1 = 1
F2 = 1
Fn = Fn−1 + Fn−2
Fibonacci Series
F1 = 1
F2 = 1
Fn = Fn−1 + Fn−2
fib(n):
fib(n):
if n <= 2: f = 1
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Translates the formula into code → Correct algorithm
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Translates the formula into code → Correct algorithm
Complexity?
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Translates the formula into code → Correct algorithm
Complexity?
T (n)
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Translates the formula into code → Correct algorithm
Complexity?
T (n) = T (n − 1) + T (n − 2) + Θ(1)
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Translates the formula into code → Correct algorithm
Complexity?
√
1+ 5
T (n) = T (n − 1) + T (n − 2) + Θ(1)→ ϕn , where ϕ= 2
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Translates the formula into code → Correct algorithm
Complexity?
√
1+ 5
T (n) = T (n − 1) + T (n − 2) + Θ(1)→ ϕn , where ϕ= 2
T (n) ≥ 2 × T (n − 2)
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Translates the formula into code → Correct algorithm
Complexity?
√
1+ 5
T (n) = T (n − 1) + T (n − 2) + Θ(1)→ ϕn , where ϕ= 2
T (n) ≥ 2 × T (n − 2)
T (n) ≥ 2 × 2 × T (n − 4)
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Translates the formula into code → Correct algorithm
Complexity?
√
1+ 5
T (n) = T (n − 1) + T (n − 2) + Θ(1)→ ϕn , where ϕ= 2
T (n) ≥ 2 × T (n − 2)
T (n) ≥ 2 × 2 × T (n − 4)
...
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Translates the formula into code → Correct algorithm
Complexity?
√
1+ 5
T (n) = T (n − 1) + T (n − 2) + Θ(1)→ ϕn , where ϕ= 2
T (n) ≥ 2 × T (n − 2)
T (n) ≥ 2 × 2 × T (n − 4)
...
(How many times can I subtract 2 from n?)
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Translates the formula into code → Correct algorithm
Complexity?
√
1+ 5
T (n) = T (n − 1) + T (n − 2) + Θ(1)→ ϕn , where ϕ= 2
T (n) ≥ 2 × T (n − 2)
T (n) ≥ 2 × 2 × T (n − 4)
...
(How many times can I subtract 2 from n?)
n
T (n) ≥ 2 2
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Translates the formula into code → Correct algorithm
Complexity?
√
1+ 5
T (n) = T (n − 1) + T (n − 2) + Θ(1)→ ϕn , where ϕ= 2
T (n) ≥ 2 × T (n − 2)
T (n) ≥ 2 × 2 × T (n − 4)
...
(How many times can I subtract 2 from n?)
n
T (n) ≥ 2 2
n
T (n) = Ω(2 2 )
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Translates the formula into code → Correct algorithm
Complexity?
√
1+ 5
T (n) = T (n − 1) + T (n − 2) + Θ(1)→ ϕn , where ϕ= 2
T (n) ≥ 2 × T (n − 2)
T (n) ≥ 2 × 2 × T (n − 4)
...
(How many times can I subtract 2 from n?)
n
T (n) ≥ 2 2
n
T (n) = Ω(2 2 )→ Exponential!
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
fib(n):
if n <= 2: f = 1
else: f = fib(n-1) + fib(n - 2)
return f
Fn
Observation
Observation
• The problem can be divided into subproblems
Observation
• The problem can be divided into subproblems
• The subproblems overlap
Observation
• The problem can be divided into subproblems
• The subproblems overlap
• Calculate once and store
Observation
• The problem can be divided into subproblems
• The subproblems overlap
• Calculate once and store
• No cost of recalculation
memo = {}
memo = {}
fib(n):
memo = {}
fib(n):
if n in memo: return memo[n]
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
else: f = fib(n-1) + fib(n-2)
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
else: f = fib(n-1) + fib(n-2)
memo[n] = f
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
else: f = fib(n-1) + fib(n-2)
memo[n] = f
return f
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
else: f = fib(n-1) + fib(n-2)
memo[n] = f
return f
fib(k) only recurses the first time it’s called, ∀k
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
else: f = fib(n-1) + fib(n-2)
memo[n] = f
return f
fib(k) only recurses the first time it’s called, ∀k
Memoized calls cost Θ(1)
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
else: f = fib(n-1) + fib(n-2)
memo[n] = f
return f
fib(k) only recurses the first time it’s called, ∀k
Memoized calls cost Θ(1) → Free
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
else: f = fib(n-1) + fib(n-2)
memo[n] = f
return f
fib(k) only recurses the first time it’s called, ∀k
Memoized calls cost Θ(1) → Free
# of non-memoized calls
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
else: f = fib(n-1) + fib(n-2)
memo[n] = f
return f
fib(k) only recurses the first time it’s called, ∀k
Memoized calls cost Θ(1) → Free
# of non-memoized calls → n
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
else: f = fib(n-1) + fib(n-2)
memo[n] = f
return f
fib(k) only recurses the first time it’s called, ∀k
Memoized calls cost Θ(1) → Free
# of non-memoized calls → n
Cost of each non-memoized calls
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
else: f = fib(n-1) + fib(n-2)
memo[n] = f
return f
fib(k) only recurses the first time it’s called, ∀k
Memoized calls cost Θ(1) → Free
# of non-memoized calls → n
Cost of each non-memoized calls → Θ(1)
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
else: f = fib(n-1) + fib(n-2)
memo[n] = f
return f
fib(k) only recurses the first time it’s called, ∀k
Memoized calls cost Θ(1) → Free
# of non-memoized calls → n
Cost of each non-memoized calls → Θ(1)
Running Time: # of non-memoized calls × Cost of each call
memo = {}
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 1
else: f = fib(n-1) + fib(n-2)
memo[n] = f
return f
fib(k) only recurses the first time it’s called, ∀k
Memoized calls cost Θ(1) → Free
# of non-memoized calls → n
Cost of each non-memoized calls → Θ(1)
Running Time: # of non-memoized calls × Cost of each call→ Θ(n)
F1 F2 F3 Fn−2 Fn−1 Fn
F1 F2 F3 Fn−2 Fn−1 Fn
fib = {}
F1 F2 F3 Fn−2 Fn−1 Fn
fib = {}
for k in range(1, n + 1):
F1 F2 F3 Fn−2 Fn−1 Fn
fib = {}
for k in range(1, n + 1):
if k <= 2: f = 1
F1 F2 F3 Fn−2 Fn−1 Fn
fib = {}
for k in range(1, n + 1):
if k <= 2: f = 1
else: f = fib[k-1] + fib[k-2]
F1 F2 F3 Fn−2 Fn−1 Fn
fib = {}
for k in range(1, n + 1):
if k <= 2: f = 1
else: f = fib[k-1] + fib[k-2]
fib[k] = f
F1 F2 F3 Fn−2 Fn−1 Fn
fib = {}
for k in range(1, n + 1):
if k <= 2: f = 1
else: f = fib[k-1] + fib[k-2]
fib[k] = f
return fib[n]
F1 F2 F3 Fn−2 Fn−1 Fn
fib = {}
for k in range(1, n + 1):
if k <= 2: f = 1
else: f = fib[k-1] + fib[k-2]
fib[k] = f
return fib[n]
Observation
• Exactly the same computation as the memoized version
F1 F2 F3 Fn−2 Fn−1 Fn
fib = {}
for k in range(1, n + 1):
if k <= 2: f = 1
else: f = fib[k-1] + fib[k-2]
fib[k] = f
return fib[n]
Observation
• Exactly the same computation as the memoized version
• Topological sort of subproblem dependency DAG
F1 F2 F3 Fn−2 Fn−1 Fn
fib = {}
for k in range(1, n + 1):
if k <= 2: f = 1
else: f = fib[k-1] + fib[k-2]
fib[k] = f
return fib[n]
Observation
• Exactly the same computation as the memoized version
• Topological sort of subproblem dependency DAG
• Can save space
F1 F2 F3 Fn−2 Fn−1 Fn
fib = {}
for k in range(1, n + 1):
if k <= 2: f = 1
else: f = fib[k-1] + fib[k-2]
fib[k] = f
return fib[n]
Observation
• Exactly the same computation as the memoized version
• Topological sort of subproblem dependency DAG
• Can save space → Only the last two values are required
F1 F2 F3 Fn−2 Fn−1 Fn
fib = {}
for k in range(1, n + 1):
if k <= 2: f = 1
else: f = fib[k-1] + fib[k-2]
fib[k] = f
return fib[n]
Observation
• Exactly the same computation as the memoized version
• Topological sort of subproblem dependency DAG
• Can save space → Only the last two values are required
• Obvious running time
F1 F2 F3 Fn−2 Fn−1 Fn
fib = {}
for k in range(1, n + 1):
if k <= 2: f = 1
else: f = fib[k-1] + fib[k-2]
fib[k] = f
return fib[n]
Observation
• Exactly the same computation as the memoized version
• Topological sort of subproblem dependency DAG
• Can save space → Only the last two values are required
• Obvious running time → One for loop, constant work inside
s v
s v
s v
s u v
s u v
s u v
s u v
s u v
s u v
s u v
s u v
s u v
s u v
s u v
s u v
# of subproblems × Time/subproblem
# of subproblems × Time/subproblem
Subproblems
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
Time for subproblem δ(s, v )
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
Time for subproblem δ(s, v )
• indegree(v )
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
Time for subproblem δ(s, v )
• indegree(v ) → Depends on V
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
Time for subproblem δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
Time for subproblem δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
Time for subproblem δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
Time for subproblem δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
Time for subproblem δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
P
Total Time (indegree(v ) + 1)
v ∈V
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
Time for subproblem δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
P
Total Time (indegree(v ) + 1)
P v ∈V P
= indegree(v ) + 1
v ∈V v ∈V
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
Time for subproblem δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
P
Total Time (indegree(v ) + 1)
P v ∈V P
= indegree(v ) + 1
v ∈V v ∈V
= Θ(V + E )
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
Time for subproblem δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
P
Total Time (indegree(v ) + 1)
P v ∈V P
= indegree(v ) + 1
v ∈V v ∈V
= Θ(V + E )
But that’s TopSort + One Pass Relaxation!
# of subproblems × Time/subproblem
Subproblems→ δ(s, v )
# of subproblems→ V
Time for subproblem δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
P
Total Time (indegree(v ) + 1)
P v ∈V P
= indegree(v ) + 1
v ∈V v ∈V
= Θ(V + E )
But that’s TopSort + One Pass Relaxation! → Doing the same thing
here.
s v
s v
s a t
s a t
s0 a0 t0
s a t s1 a1 t1
s2 a2 t2
s0 a0 t0
s a t s1 a1 t1
time
s2 a2 t2
s0 a0 t0
s a t s1 a1 t1
time
s2 a2 t2
s0 a0 t0
s a t s1 a1 t1
time
s2 a2 t2
s0 a0 t0
s a t s1 a1 t1
time
s2 a2 t2
# of subproblems × Time/subproblem
# of subproblems × Time/subproblem
Subproblems
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems
• Value of k → [0, V − 1]
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems
• Value of k → [0, V − 1]
• For each k → v ∈ V
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems
• Value of k → [0, V − 1]
• For each k → v ∈ V
• Total: V 2
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems
• Value of k → [0, V − 1]
• For each k → v ∈ V
• Total: V 2
For each k, time for subproblem, δ(s, v )
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems
• Value of k → [0, V − 1]
• For each k → v ∈ V
• Total: V 2
For each k, time for subproblem, δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems
• Value of k → [0, V − 1]
• For each k → v ∈ V
• Total: V 2
For each k, time for subproblem, δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
Total time for each k → Θ(V + E )
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems
• Value of k → [0, V − 1]
• For each k → v ∈ V
• Total: V 2
For each k, time for subproblem, δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
Total time for each k → Θ(V + E )
How many ks?
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems
• Value of k → [0, V − 1]
• For each k → v ∈ V
• Total: V 2
For each k, time for subproblem, δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
Total time for each k → Θ(V + E )
How many ks? → [0, |V | − 1] → |V |
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems
• Value of k → [0, V − 1]
• For each k → v ∈ V
• Total: V 2
For each k, time for subproblem, δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
Total time for each k → Θ(V + E )
How many ks? → [0, |V | − 1] → |V |
• Total: Θ(V · E )
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems
• Value of k → [0, V − 1]
• For each k → v ∈ V
• Total: V 2
For each k, time for subproblem, δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
Total time for each k → Θ(V + E )
How many ks? → [0, |V | − 1] → |V |
• Total: Θ(V · E )
But that’s Bellman-Ford!
MBH (CSE, IUT) Dynamic Programming 15 / 16
Complexity
# of subproblems × Time/subproblem
Subproblems → δk (s, v )
# of subproblems
• Value of k → [0, V − 1]
• For each k → v ∈ V
• Total: V 2
For each k, time for subproblem, δ(s, v )
• indegree(v ) → Depends on V
• Cannot take direct product here → Sum over all subproblems
• Calculate min each time → Θ(1)
• Total: indegree(v ) + 1
Total time for each k → Θ(V + E )
How many ks? → [0, |V | − 1] → |V |
• Total: Θ(V · E )
But that’s Bellman-Ford!→ Yes, exactly!
MBH (CSE, IUT) Dynamic Programming 15 / 16
Reading