You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -6647,7 +6649,7 @@ <h1 id="introduction-to-dynamic-programming">Introduction to Dynamic Programming
6647
6649
<h2id="speeding-up-fibonacci-with-dynamic-programming-memoization">Speeding up Fibonacci with Dynamic Programming (Memoization)<aclass="headerlink" href="#speeding-up-fibonacci-with-dynamic-programming-memoization" title="Permanent link">¶</a></h2>
6648
6650
<p>Our recursive function currently solves fibonacci in exponential time. This means that we can only handle small input values before the problem becomes too difficult. For instance, <spanclass="arithmatex">$f(29)$</span> results in <em>over 1 million</em> function calls!</p>
6649
6651
<p>To increase the speed, we recognize that the number of subproblems is only <spanclass="arithmatex">$O(n)$</span>. That is, in order to calculate <spanclass="arithmatex">$f(n)$</span> we only need to know <spanclass="arithmatex">$f(n-1),f(n-2), \dots ,f(0)$</span>. Therefore, instead of recalculating these subproblems, we solve them once and then save the result in a lookup table. Subsequent calls will use this lookup table and immediately return a result, thus eliminating exponential work! </p>
6650
-
<p>Each recursive call will check against a lookup table to see if the value has been calculated. This is done in <spanclass="arithmatex">$O(1)$</span> time. If we have previously calcuated it, return the result, otherwise, we calculate the function normally. The overall runtime is <spanclass="arithmatex">$O(n)$</span>! This is an enormous improvement over our previous exponential time algorithm!</p>
6652
+
<p>Each recursive call will check against a lookup table to see if the value has been calculated. This is done in <spanclass="arithmatex">$O(1)$</span> time. If we have previously calcuated it, return the result, otherwise, we calculate the function normally. The overall runtime is <spanclass="arithmatex">$O(n)$</span>. This is an enormous improvement over our previous exponential time algorithm!</p>
@@ -6662,7 +6664,7 @@ <h2 id="speeding-up-fibonacci-with-dynamic-programming-memoization">Speeding up
6662
6664
<spanclass="p">}</span>
6663
6665
</code></pre></div>
6664
6666
<p>With our new memoized recursive function, <spanclass="arithmatex">$f(29)$</span>, which used to result in <em>over 1 million calls</em>, now results in <em>only 57</em> calls, nearly <em>20,000 times</em> fewer function calls! Ironically, we are now limited by our data type. <spanclass="arithmatex">$f(46)$</span> is the last fibonacci number that can fit into a signed 32-bit integer.</p>
6665
-
<p>Typically, we try to save states in arrays,if possible, since the lookup time is <spanclass="arithmatex">$O(1)$</span> with minimal overhead. However, more generically, we can save states anyway we like. Other examples include maps (binary search trees) or unordered_maps (hash tables).</p>
6667
+
<p>Typically, we try to save states in arrays,if possible, since the lookup time is <spanclass="arithmatex">$O(1)$</span> with minimal overhead. However, more generically, we can save states any way we like. Other examples include binary search trees (<code>map</code> in C++) or hash tables (<code>unordered_map</code> in C++).</p>
@@ -6691,7 +6693,7 @@ <h2 id="speeding-up-fibonacci-with-dynamic-programming-memoization">Speeding up
6691
6693
<p>This approach is called top-down, as we can call the function with a query value and the calculation starts going from the top (queried value) down to the bottom (base cases of the recursion), and makes shortcuts via memoization on the way.</p>
<p>Until now you've only seen top-down dynamic programming with memoization. However, we can also solve problems with bottom-up dynamic programming.
6694
-
Bottomup is exactly the opposite of top-down, you start at the bottom (base cases of the recursion), and extend it to more and more values.</p>
6696
+
Bottom-up is exactly the opposite of top-down, you start at the bottom (base cases of the recursion), and extend it to more and more values.</p>
6695
6697
<p>To create a bottom-up approach for fibonacci numbers, we initilize the base cases in an array. Then, we simply use the recursive definition on array:</p>
<p>Of course, as written, this is a bit silly for two reasons:
6708
6710
Firstly, we do repeated work if we call the function more than once.
6709
6711
Secondly, we only need to use the two previous values to calculate the current element. Therefore, we can reduce our memory from <spanclass="arithmatex">$O(n)$</span> to <spanclass="arithmatex">$O(1)$</span>. </p>
6710
-
<p>An example of a bottomup dynamic programming solution for fibonacci which uses <spanclass="arithmatex">$O(1)$</span> might be:</p>
6712
+
<p>An example of a bottom-up dynamic programming solution for fibonacci which uses <spanclass="arithmatex">$O(1)$</span> might be:</p>
<p>Note that we've changed the constant from <code>MAXN</code> TO <code>MAX_SAVE</code>. This is because the total number of elements we need to have access to is only 3. It no longer scales with the size of input and is, by definition, <spanclass="arithmatex">$O(1)$</span> memory. Additionally, we use a common trick (using the modulo operator) only maintaining the values we need.</p>
6724
-
<p>That's it. That's the basics of dynamic programming: Don't repeat work you've done before.</p>
6725
+
<p>Note that we've changed the constant from <code>MAXN</code> TO <code>MAX_SAVE</code>. This is because the total number of elements we need to access is only 3. It no longer scales with the size of input and is, by definition, <spanclass="arithmatex">$O(1)$</span> memory. Additionally, we use a common trick (using the modulo operator) only maintaining the values we need.</p>
6726
+
<p>That's it. That's the basics of dynamic programming: Don't repeat the work you've done before.</p>
6725
6727
<p>One of the tricks to getting better at dynamic programming is to study some of the classic examples.</p>
<td>Given <spanclass="arithmatex">$W$</span>, <spanclass="arithmatex">$N$</span>, and <spanclass="arithmatex">$N$</span> items with weights <spanclass="arithmatex">$w_i$</span> and values <spanclass="arithmatex">$v_i$</span>, what is the maximum <spanclass="arithmatex">$\sum_{i=1}^{k} v_i$</span> for each subset of items of size <spanclass="arithmatex">$k$</span> (<spanclass="arithmatex">$1 \le k \le N$</span>) while ensuring <spanclass="arithmatex">$\sum_{i=1}^{k} w_i \le W$</span>?</td>
6738
6740
</tr>
6739
6741
<tr>
6740
6742
<td>Subset Sum</td>
6741
6743
<td>Given <spanclass="arithmatex">$N$</span> integers and <spanclass="arithmatex">$T$</span>, determine whether there exists a subset of the given set whose elements sum up to the <spanclass="arithmatex">$T$</span>.</td>
6742
6744
</tr>
6743
6745
<tr>
6744
-
<td>Longest Increasing Subsequence</td>
6746
+
<td>Longest Increasing Subsequence (LIS)</td>
6745
6747
<td>You are given an array containing <spanclass="arithmatex">$N$</span> integers. Your task is to determine the LIS in the array, i.e., a subsequence where every element is larger than the previous one.</td>
6746
6748
</tr>
6747
6749
<tr>
6748
-
<td>Counting all possible paths in a matrix.</td>
6750
+
<td>Counting Paths in a 2D Array</td>
6749
6751
<td>Given <spanclass="arithmatex">$N$</span> and <spanclass="arithmatex">$M$</span>, count all possible distinct paths from <spanclass="arithmatex">$(1,1)$</span> to <spanclass="arithmatex">$(N, M)$</span>, where each step is either from <spanclass="arithmatex">$(i,j)$</span> to <spanclass="arithmatex">$(i+1,j)$</span> or <spanclass="arithmatex">$(i,j+1)$</span>.</td>
0 commit comments