<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Miguel Vilá on Medium]]></title>
        <description><![CDATA[Stories by Miguel Vilá on Medium]]></description>
        <link>https://medium.com/@miguelvila?source=rss-de26d616f08------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Stories by Miguel Vilá on Medium</title>
            <link>https://medium.com/@miguelvila?source=rss-de26d616f08------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 02:59:12 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@miguelvila/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Solving Dynamic Programming problems using Functional Programming (Part 4)]]></title>
            <link>https://medium.com/@miguelvila/solving-dynamic-programming-problems-using-functional-programming-part-4-4df1cedf9127?source=rss-de26d616f08------2</link>
            <guid isPermaLink="false">https://medium.com/p/4df1cedf9127</guid>
            <category><![CDATA[scala]]></category>
            <category><![CDATA[functional-programming]]></category>
            <category><![CDATA[dynamic-programming]]></category>
            <category><![CDATA[bamtech]]></category>
            <dc:creator><![CDATA[Miguel Vilá]]></dc:creator>
            <pubDate>Mon, 20 Nov 2017 08:00:00 GMT</pubDate>
            <atom:updated>2018-05-24T13:16:07.480Z</atom:updated>
            <content:encoded><![CDATA[<p>n the <a href="https://medium.com/bamtechmedia/solving-dynamic-programming-problems-using-functional-programming-part-3-f9365ee9d3f1">previous posts</a>, we saw an approach to solving <a href="https://en.wikipedia.org/wiki/Dynamic_programming">Dynamic Programming</a> problems in a bottom-up fashion and trying to be functional. In this last post we will see a functional approach when solving them top-down.</p><p>When following a top-down approach we memoize answers and have explicit recursion. Usually this is done with a mutable look-up table. For instance the knapsack problem can be solved this way:</p><pre>import scala.collection.mutable</pre><pre>def knapsack(maxWeight: Int, value: Vector[Int], weight: Vector[Int]): Int = {<br>    val n = value.length<br>    val solutions = mutable.HashMap.empty[(Int,Int), Int]<br>    def f(i: Int, j: Int): Int = {<br>        if(!solutions.contains((i,j))) {<br>            val solution = <br>                if( i == 0 || j == 0 ) {<br>                    0<br>                } else if( j - weight(i-1) &gt;= 0 ) {<br>                    Math.max( <br>                        f(i-1,j) , <br>                        f(i-1, j - weight(i-1)) + value(i-1) <br>                    )<br>                } else {<br>                    f(i-1, j)<br>                }<br>            solutions.put((i,j), solution)<br>        }<br>        solutions((i,j))<br>    }<br>    f(n, maxWeight)<br>}</pre><p>What if we could do the same thing with a data structure that’s defined recursively, just as the solutions? Can this be done with a normal map? Well there’s the method <a href="http://www.scala-lang.org/files/archive/api/2.12.2/scala/collection/immutable/Vector$.html#tabulate[A](n1:Int,n2:Int)(f:(Int,Int)=%3EA):CC[CC[A]]">tabulate</a> that can be used to build a Vector using a function that describes every entry by it’s indexes. Let’s try something with it:</p><pre>def knapsack2(maxWeight: Int, value: Vector[Int], weight: Vector[Int]): Int = {<br>    val n = value.length<br>    lazy val solutions: Vector[Vector[Int]] = <br>        Vector.tabulate(n + 1, maxWeight + 1) { (i,j) =&gt;<br>            if( i == 0 || j == 0 ) {<br>                0<br>            } else if( j - weight(i-1) &gt;= 0 ) {<br>                Math.max( <br>                    solutions(i-1)(j) , <br>                    solutions(i-1)(j - weight(i-1)) + value(i-1) <br>                )<br>            } else {<br>                solutions(i-1)(j)<br>            }<br>        }<br>    solutions(n)(maxWeight)<br>}</pre><p>Well, this even compiles but fails at runtime with a Stackoverflow error. This is because when we use the solutions matrix inside the tabulate function we demand for the solutions value to be already completely built. It’s kind of a chicken and egg problem. If we could relax this condition and make it possible to have some entries defined and others not…</p><p>Let’s define a LazyVector allowing precisely that!</p><pre>// not threadsclass LazyVector[A](thunks: Vector[() =&gt; A]) { // (1)<br>    <br>    private val values: Array[Option[A]] = Array.fill(thunks.length)              (None) // (2)<br>    <br>    def apply(i: Int): A = { // (3)<br>        if(!values(i).isDefined) {<br>            values(i) = Some( thunks(i)() )<br>        }<br>        values(i).get<br>    }   <br>}</pre><p>At (1) we declare that to build a LazyVector we need a Vector describing how to compute each entry. This is just a Vector of thunks (a <a href="https://en.wikipedia.org/wiki/Thunk#Functional_programming">thunk</a> is a zero argument function). At (2) we see where we will save the computed values. Under the hood we will memoize the values using a mutable array. Yes, mutable! Don’t worry, our intentions are pure (get it?). At (3) we declare how we are going to retrieve values for this data structure: if it’s already computed just return it, otherwise compute it and save it.</p><p>Let’s also add some constructor functions in the companion object:</p><pre>object LazyVector {<br>    <br>    def tabulate[A](n: Int)(f: Int =&gt; A): LazyVector[A] = {<br>        new LazyVector( Vector.tabulate(n)( i =&gt; () =&gt; f(i) ) )<br>    }</pre><pre>    def tabulate[A](m: Int, n: Int)(f: (Int,Int) =&gt; A): LazyVector[LazyVector[A]] = {<br>        tabulate(m)( i =&gt; tabulate(n)( j =&gt; f(i,j) ) )<br>    }</pre><pre>}</pre><p>Let’s try the same thing we did with Vectors:</p><pre>def knapsack(maxWeight: Int, value: Vector[Int], weight: Vector[Int]): Int = {<br>    val n = value.length<br>    lazy val solutions: LazyVector[LazyVector[Int]] = <br>        LazyVector.tabulate(n + 1, maxWeight + 1) { (i,j) =&gt;<br>            if( i == 0 || j == 0 ) {<br>                0<br>            } else if( j - weight(i-1) &gt;= 0 ) {<br>                Math.max( <br>                    solutions(i-1)(j) , <br>                    solutions(i-1)(j - weight(i-1)) + value(i-1) <br>                )<br>            } else {<br>                solutions(i-1)(j)<br>            }<br>        }<br>    solutions(n)(maxWeight)<br>}</pre><p>And this works! No stack overflow!</p><p>This has the same time complexity as we had before with the bottom-up approach. The space complexity, unfortunately, is <em>O</em>(<em>nW</em>) which is not the best we can do if we are only interested in computing the maximum total value.</p><p>There is an important advantage with this approach, though. The code is way more declarative. It doesn’t say how to iterate over the matrix of solutions in such a way that dependencies are pre-computed. It just says: here’s what the solution looks like, let the data structure’s laziness avoid re-computations.</p><h3>Returning the concrete solution</h3><p>If we also want to know <em>which</em> items conform the maximum total value then we have to record all the decisions we make, so the space complexity will be <em>O</em>(<em>nW</em>) anyway. This just implies returning more information when we are looking for the maximum:</p><pre>def max[A](a: (Int, A), b: (Int, A)): (Int, A) =<br>    if(a._1 &gt; b._1)<br>        a<br>    else<br>        b</pre><pre>def knapsackWithItems(<br>    maxWeight: Int, value: Vector[Int], weight: Vector[Int]): List[Int] = {<br>    val n = value.length<br>    lazy val solutions: LazyVector[LazyVector[(Int, List[Int])]] = <br>        LazyVector.tabulate(n + 1, maxWeight + 1) { (i,j) =&gt;<br>            if( i == 0 || j == 0 ) {<br>                (0, List.empty)<br>            } else if( j - weight(i-1) &gt;= 0 ) {<br>                val (including, itemsIds) = solutions(i-1)(j - weight(i-1))<br>                max( <br>                    solutions(i-1)(j) ,<br>                    (including + value(i-1), (i-1) :: itemsIds )<br>                )<br>            } else {<br>                solutions(i-1)(j)<br>            }<br>        }<br>    val (_,items) = solutions(n)(maxWeight)<br>    items<br>}</pre><p>Similarly we can solve <a href="https://projecteuler.net/problem=81">Project Euler’s 81-th problem</a> like this:</p><pre>sealed trait Instruction<br>case object GoRight extends Instruction<br>case object GoDown extends Instruction</pre><pre>def minPath(values: Vector[Vector[Int]]): List[Instruction] = {<br>    val m = values.length<br>    val n = values(0).length<br>    lazy val solutions: LazyVector[LazyVector[(Int, List[Instruction])]] = <br>        LazyVector.tabulate(m, n) { <br>            case (0,0) =&gt; (values(0)(0), List.empty)<br>            case (0,j) =&gt; <br>                val (leftVal, leftInsts) = solutions(0)(j-1)<br>                (values(0)(j) + leftVal, leftInsts ++ List(GoRight))<br>            case (i,0) =&gt; <br>                val (upVal, upInsts) = solutions(i-1)(0)<br>                (values(i)(0) + upVal, upInsts ++ List(GoDown))<br>            case (i,j) =&gt;<br>                val (leftVal, leftInsts) = solutions(i)(j-1)<br>                val (upVal  , upInsts  ) = solutions(i-1)(j)<br>                if(leftVal &lt; upVal) {<br>                    (leftVal + values(i)(j), leftInsts ++ List(GoRight))<br>                } else {<br>                    (upVal + values(i)(j)  , upInsts ++ List(GoDown))                    <br>                }<br>        }<br>    val (_,insts) = solutions(m-1)(n-1)<br>    insts<br>}</pre><h3>Conclusions</h3><p>This top-down approach has the disadvantage that it may consume more memory than we need (if we are just interested in the maximum or minimum value). But for the kind of problems where we <em>need</em> to have all the decisions then it’s worth it anyway. It produces very clear and declarative code. The only disadvantage I can think of is that it may produce stack-overflows for large inputs.</p><p><strong>— Miguel Vilá</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4df1cedf9127" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Solving Dynamic Programming problems using Functional Programming (Part 3)]]></title>
            <link>https://medium.com/@miguelvila/solving-dynamic-programming-problems-using-functional-programming-part-3-f9365ee9d3f1?source=rss-de26d616f08------2</link>
            <guid isPermaLink="false">https://medium.com/p/f9365ee9d3f1</guid>
            <category><![CDATA[functional-programming]]></category>
            <category><![CDATA[bamtech]]></category>
            <category><![CDATA[dynamic-programming]]></category>
            <dc:creator><![CDATA[Miguel Vilá]]></dc:creator>
            <pubDate>Thu, 12 Oct 2017 07:00:00 GMT</pubDate>
            <atom:updated>2018-05-24T13:17:11.017Z</atom:updated>
            <content:encoded><![CDATA[<p>In the <a href="https://medium.com/bamtechmedia/solving-dynamic-programming-problems-using-functional-programming-part-2-4bf24941da2a">previous post </a>we saw a way to functionally solve the knapsack problem. Can we do the same when the dependencies between solutions have another shape?</p><p>For instance in <a href="https://projecteuler.net/problem=81">this</a> project Euler problem the solutions depend on each other differently. Once again, let’s start with a problem definition that allows us to break the problem into subproblems:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/541/0*jmenJqDlfVMZjCY0.png" /></figure><p>Let’s consider some position (i,j). To get to that position we only have two possibilities: we either get to that position by coming from the upper row and going down from there or we come from the previous column and go right from there. These two possibilities are mutually exclusive thus we can compute the minimum between them to compute the optimal solution for (i,j) :</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/750/0*Hqagf6382qDXOLSN.png" /></figure><p>Here v(i,j) is the value stored at row <em>i</em> and column <em>j</em>.</p><p>And, once again, let’s draw a diagram showing the dependencies:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/271/0*ij4JjGHsq0BegpKt.png" /></figure><p>There is an important difference with respect to the base cases here. The minimal path for each value in the first row is simply the path that starts at (0,0) and goes right from there (this is the formula R(i,j), which is the accumulative <em>row </em>sum). Similarly, the minimal path for each value in the first column is the part that starts at and goes down from there (this is the formula C(i,j) , which is the accumulative <em>column</em> sum).</p><p>Now, unlike the <em>0/1 knapsack problem</em>, the solution for (i,j) not only depends on a value at the previous row. It also depends on the previous value in the same row.</p><p>Let’s start with the base cases: how to compute the accumulative sum of an array of numbers? fold can be used to combine all the values using a reducer function. But the intermediate results of that function aren’t kept, which is what we want: we don’t want to have the total sum. We want to get the accumulative sum. scan does this. It has the same parameters as fold but instead of returning just one value it’s going to return a vector of all the intermediate values:</p><pre>def accumulativeSum(nums: Vector[Int]): Vector[Int] =<br>    nums.scanLeft(0)(_ + _).drop(1)</pre><p>The drop(1) is so we don’t include the entry with 0 at the beginning.</p><p>So the first row and the first column can be computed like this:</p><pre>def minPath(values: Vector[Vector[Int]]): Int = {<br>    val m = values.length<br>    val n = values(0).length<br>    val firstRow = accumulativeSum(values(0))<br>    val firstColumn = accumulativeSum( (0 to m-1).map(values(_)(0)) )<br>    ???<br>}</pre><p>Let’s use the same strategy as before. We’ll compute every new row from the bottom up, starting with the first row as the initial state:</p><pre>def minPath(values: Vector[Vector[Int]]): Int = {<br>    val m = values.length<br>    val n = values(0).length<br>    val firstRow = accumulativeSum(values(0))<br>    val firstColumn = accumulativeSum( (0 to m-1).map(values(_)(0)) )<br>    (1 to m-1).foldLeft(firstRow) { (upperRow, i) =&gt;<br>        ??? // How to compute the new row?<br>    }.last<br>}</pre><p>Now to compute say M(i,1) we’ll need to use two things: M(i — 1, 1)</p><p>which is in the upper row and M(i, 0) which is a value in the very first column, at the left of M(i,1). Then to compute M(i,2) we will need to use M(i — 1,2) and M(i,1) which is the value that we previously computed. The function fold can help us with using the last value to compute the next. But we also need to return all the list of intermediate values, not just the last one, so scan is the one we want. Putting it all together:</p><pre>def minPath(values: Vector[Vector[Int]]): Int = {<br>    val m = values.length<br>    val n = values(0).length<br>    val firstRow = accumulativeSum(values(0))<br>    val firstColumn = accumulativeSum( (0 to m-1).map(values(_)(0)) )<br>    (1 to m-1).foldLeft(firstRow) { (upperRow, i) =&gt;<br>        val leftmostSolution = firstColumn(i)<br>        (1 to n-1).scanLeft(leftmostSolution) { (leftSolution,j) =&gt;<br>            val upperSolution = upperRow(j)<br>            Math.min(leftSolution, upperSolution) + values(i)(j)<br>        }.toVector<br>    }.last<br>}</pre><p>That’s it! No mutation whatsoever!</p><h3>Conclusions</h3><p>Even for a class of algorithms that looks inherently imperative there are ways to avoid mutation and to end up with the very same algorithm using immutable data structures and higher-order functions like fold or scan. You could just translate the mutations into State monad values but that’s just too complex and unnecessary. There are simpler ways to functionally express the same thing.</p><p>Is this worth it? Maybe not if you are used to the imperative approach, which already yields a purely functional solution up to observable mutation.</p><p>In the next post we will see how to do the same with a top-down approach that’s both functional and expressive.</p><p><strong>— Miguel Vilá</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f9365ee9d3f1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Solving Dynamic Programming problems using Functional Programming (Part 2)]]></title>
            <link>https://medium.com/@miguelvila/solving-dynamic-programming-problems-using-functional-programming-part-2-4bf24941da2a?source=rss-de26d616f08------2</link>
            <guid isPermaLink="false">https://medium.com/p/4bf24941da2a</guid>
            <category><![CDATA[functional-programming]]></category>
            <category><![CDATA[bamtech]]></category>
            <category><![CDATA[dynamic-programming]]></category>
            <dc:creator><![CDATA[Miguel Vilá]]></dc:creator>
            <pubDate>Wed, 04 Oct 2017 07:00:00 GMT</pubDate>
            <atom:updated>2018-05-24T13:17:45.660Z</atom:updated>
            <content:encoded><![CDATA[<p>In the <a href="https://medium.com/bamtechmedia/solving-dynamic-programming-problems-using-functional-programming-part-1-988528c00908">last post</a> we saw an imperative solution to the knapsack problem. In this post we will see an initial approach for doing the same thing functionally.</p><h3>Translating every mutation to a state modification</h3><p>In functional programming one way to handle state is the State monad. Then we could mindlessly replace the modifications by creating State values that represent those modifications. Using the State monad implementation in <a href="https://github.com/typelevel/cats">Cats</a> and an immutable data structure like Vector we can reimplement the O(nW) space version like this:</p><p>First some imports:</p><pre>import cats._, cats.instances.all._, cats.syntax.traverse._, cats.syntax.foldable._<br>import cats.data.State</pre><p>This function will prove useful when updating the state:</p><pre>def setSolution(i: Int, j: Int, newVal: Int)<br>               (solutions: Vector[Vector[Int]]): Vector[Vector[Int]] =<br>    solutions.updated(i, solutions(i).updated(j, newVal))</pre><p>and the whole thing:</p><pre>def knapsack(maxWeight: Int, value: Vector[Int], weight: Vector[Int]): Int = {<br>    val n = value.length<br>    val initialState: Vector[Vector[Int]] = Vector.fill(n+1, maxWeight + 1)( 0 )<br>    val st: State[Vector[Vector[Int]], Unit] = ( 1 to n ).toList.traverseU_ { i =&gt;<br>        ( 1 to maxWeight ).toList.traverseU_ { j =&gt;<br>            for {<br>                solutions &lt;- State.get[Vector[Vector[Int]]]<br>                newVal = if( j - weight(i-1) &gt;= 0 ) {<br>                    Math.max(<br>                        solutions(i-1)(j) , <br>                        solutions(i-1)(j - weight(i-1)) + value(i-1) <br>                    )<br>                } else {<br>                    solutions(i-1)(j)<br>                }<br>                _ &lt;- State.modify(setSolution(i,j,newVal))<br>            } yield ()<br>        }<br>    }<br>    val solution = st.runS(initialState).value<br>    solution(n)(maxWeight)<br>}</pre><p>Not too interesting. This even looks too much like the original code. In a similar way we can do the same for the O(W)space version:</p><pre><strong>def</strong> <strong>knapsack</strong>(maxWeight: Int, value: Vector[Int], weight: Vector[Int]): Int = {<br>    val n = value.length<br>    val initialState: Vector[Int] = Vector.fill(maxWeight + 1)( 0 )<br>    val st: State[Vector[Int], Unit] = ( 1 to n ).toList.traverseU_ { i =&gt;<br>        <strong>for</strong> {<br>            solutions &lt;- State.get[Vector[Int]]<br>            newSolutions = 0 +: ( 1 to maxWeight ).map { j: Int =&gt;<br>                <strong>if</strong>( j - weight(i-1) &gt;= 0 ) {<br>                    Math.max(<br>                        solutions(j) , <br>                        solutions(j - weight(i-1)) + value(i-1) <br>                    )<br>                } <strong>else</strong> {<br>                    solutions(j)<br>                }<br>            }.toVector<br>            _ &lt;- State.set(newSolutions)<br>        } <strong>yield</strong> ()<br>    }<br>    val solution = st.runS(initialState).value<br>    solution(maxWeight)<br>}</pre><p>Have we learned anything from this? Not much, really. And this is boring precisely because the state monad can be used to implement the very same imperative logic. This solves the problem in a functional way but it’s kind of an over-engineered approach. Is there a simpler, more elegant and functional way to do this?</p><h3>Just use fold!</h3><p>In the imperative version at each iteration we are computing a new row based on the last one. And we are interested in the last row because it holds the final answer. The first row is full of zeros, that’s our “base case”. And a perfect function for building incremental state from an initial state is `fold`! `fold` has two parameters: the initial state and a function that computes a new state based on the previous one and on the current element of the structure we are folding over. This fits us perfectly because to compute each new row we must have two ingredients: the previous row and which row number is that we are processing. We could start by folding over a list of the row numbers starting with a row full of zeros:</p><pre>val firstRow = Vector.fill(maxWeight + 1)( 0 )<br>val lastRow = (1 to n).foldLeft(firstRow) { (upperRow, i) =&gt;<br>    ??? // How to compute each new row?<br>}<br>val answer = lastRow.last</pre><p>To compute the new row we must do the same that we were doing before. Well, kind of: instead of modifying the input we are going to return a new row. To do this we can compute each value based on the column number, the row number and the values of the previous row:</p><pre>val firstRow = Vector.fill(maxWeight + 1)( 0 )<br>val lastRow = (1 to n).foldLeft(firstRow) { (upperRow, i) =&gt;<br>    0 +: (1 to maxWeight).map { j =&gt; <br>        if( j - weight(i-1) &gt;= 0 ) {<br>            Math.max( <br>                upperRow(j) ,<br>                upperRow(j - weight(i-1)) + value(i-1)<br>            )<br>        } else {<br>            upperRow(j)<br>        }<br>    }.toVector<br>}<br>val answer = lastRow.last</pre><p>And that’s pretty much it! There’s no mutation and we accomplish the same thing with very similar code! This even has O(W) space complexity.</p><h3>What we have seen so far</h3><p>In this post we saw two functional solutions to a dynamic programming problem. The first approach is very equivalent to the imperative solution and the second one uses a higher order function. In the next post we will see what happens when the dependencies between the solutions have another shape.</p><p><strong>— Miguel Vilá</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4bf24941da2a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Solving Dynamic Programming problems using Functional Programming (Part 1)]]></title>
            <link>https://medium.com/@miguelvila/solving-dynamic-programming-problems-using-functional-programming-part-1-988528c00908?source=rss-de26d616f08------2</link>
            <guid isPermaLink="false">https://medium.com/p/988528c00908</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[bamtech]]></category>
            <category><![CDATA[dynamic-programming]]></category>
            <category><![CDATA[functional-programming]]></category>
            <dc:creator><![CDATA[Miguel Vilá]]></dc:creator>
            <pubDate>Mon, 11 Sep 2017 07:00:00 GMT</pubDate>
            <atom:updated>2022-01-26T13:39:11.858Z</atom:updated>
            <content:encoded><![CDATA[<p>Dynamic programming (DP) is a technique used to solve innately inefficient recursive problems by storing solutions to pre-computed subproblems. The idea is to break a problem into smaller subproblems and save the result of each subproblem so that it is only calculated once. Dynamic programming involves two parts: restating the problem in terms of overlapping subproblems and saving the solution of the subproblems so that we can build the solution to the bigger problem.</p><p>Overlapping subproblems are subproblems that may have the same dependencies. DP helps us here because we want to compute these common dependencies just once. There are two usual approaches:</p><ul><li>The top-down approach starts trying to solve the bigger problem and uses memoization.</li><li>The bottom-up approach starts with the subproblems to build up the solution to the bigger problem and uses some data structure to save the subproblems solutions.</li></ul><p>If you want to learn more about dynamic programming you can look into some of the links at the end of this post.</p><p>In my opinion the best way to understand Dynamic Programming is by looking at multiple examples. The first usual example is the Fibonacci sequence but I think that’s a bad instance of the general idea because nobody would call Dynamic Programming the “technique” of storing the two last solutions in two variables. But in more complex examples you will be able to see the “pattern”.</p><p>This post is not so much an introduction to DP. As you will see it’s more about how to implement DP algorithms in a functional way when taking the bottom-up approach.</p><h3>The imperative perspective</h3><p>Dynamic programming problems, when solved in a bottom-up fashion, usually rely in some mutable data structure which holds the solutions for smaller subproblems. For example let’s explore the <a href="https://en.wikipedia.org/wiki/Knapsack_problem#0.2F1_knapsack_problem">0/1 knapsack problem</a>. <a href="https://www.youtube.com/watch?v=8LusJS5-AGo">This</a> and <a href="https://www.youtube.com/watch?v=xOlhR_2QCXY">this</a> are good videos about it and describe a solution. Here’s my summary of the problem:</p><p>Given n items indexed 1, 2, ⋯, n with values Vi and weights Wi find a subset of them with maximum total value that doesn’t exceed the input weight W. Each item may be included just once.</p><p>For example, for this list of items with their respective value:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/316/0*5-e-uf1Xj517DcAY.png" /></figure><p>and maximum target weight W=9.</p><p>You can select the items 3, 4 and 5 for a total weight of 9 and a total value of 15. This is good but you could do even better by selecting items 1, 2, 4 and 5. In this case we would get a total value of 16 which is in fact the maximum you can get without exceeding the maximum weight of 9.</p><p>To solve this problem in the general case we can first break it by considering subsets. Let’s start with a definition that allows us to express subsets of the problem:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/670/0*5XLHOWj9aQnT6aYx.png" /></figure><p>For each item we have two choices: either we include it and gain it’s value but we have reduced our available weight. Or, on the other hand, we ignore it and never use it. These two possibilities totally exclude each other so if we compute the maximum between them for every item (and for every possible maximum weight ≤ W) then we can get the overall maximum. And these two possibilities correspond to two subproblem definitions.</p><p>The recursive formula that express is this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/820/0*kciK7Nfj6FXrcE1u.png" /></figure><p>According to this formula we can see that the problem (i,j) depends on two subproblems: (i — 1, j - Wi) and (i — 1, j)</p><p>Here’s a diagram that describes the subproblem dependencies:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/325/0*32hPe7u2VLCnOq5_.png" /></figure><p>The blue square represents a subproblem we are trying to solve. It depends on two other subproblems (the ones pointed by the arrows). And the final answer is the red square at the lower-right corner (the value</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/70/0*XWciFaIgBOuc8yrC.gif" /></figure><p>). It represents the solution when we consider <strong>all</strong> the elements and with the problem’s input weight. We can also see the “base” cases here: when considering no item the maximum value we can produce is and the same happens when the maximum weight is 0 (this assumes all Wi&gt; 0).</p><p>Drawing this kind of diagrams can be useful because it helps us to determine the shape of the iterations. In this case, an element of a row depends on the values of the previous row. Thus, we can build up solutions going down one row at a time.</p><p>Let’s use Scala to solve this. A traditional imperative solution can be:</p><pre><strong>def</strong> <strong>knapsack</strong>(maxWeight: Int, value: Vector[Int], weight: Vector[Int]): Int = {<br>    val n = value.length<br>    val solutions: Array[Array[Int]] = Array.fill(n+1, maxWeight + 1)( 0 )<br>    (1 to n) foreach { i =&gt;<br>        (1 to maxWeight) foreach { j =&gt;<br>            solutions(i)(j) = <strong>if</strong>( j - weight(i-1) &gt;= 0 ) {<br>                Math.max( <br>                    solutions(i-1)(j) , <br>                    solutions(i-1)(j - weight(i-1)) + value(i-1) <br>                )<br>            } <strong>else</strong> {<br>                solutions(i-1)(j)<br>            }<br>        }<br>    } <br>    solutions(n)(maxWeight)<br>}</pre><p>This iterates one row at a time and computes the solution given the recursive formula. The time complexity of this is O(nW) where n is the number of items to consider and W is the maximum weight. And the space complexity is also O(nW). The previous solution can be improved given that we only need to remember the last row of solutions for each iteration:</p><pre><strong>def</strong> <strong>knapsack</strong>(maxWeight: Int, value: Vector[Int], weight: Vector[Int]): Int = {<br>    val n = value.length<br>    var solutions: Array[Int] = Array.fill(maxWeight + 1)( 0 )<br>    (1 to n) foreach { i =&gt;<br>        val newSolutions = Array.fill(maxWeight + 1)( 0 )<br>        (1 to maxWeight) foreach { j =&gt;<br>            newSolutions(j) = <strong>if</strong>( j - weight(i-1) &gt;= 0 ) {<br>                Math.max( <br>                    solutions(j) , <br>                    solutions(j - weight(i-1)) + value(i-1) <br>                )<br>            } <strong>else</strong> {<br>                solutions(j)<br>            }<br>        }<br>        solutions = newSolutions<br>    } <br>    solutions(maxWeight)<br>}</pre><p>The space complexity of this will be O(W).</p><h3>How to do the same thing functionally?</h3><p>How could we do the same thing functionally? One of the main objectives in functional programming is avoiding side-effects. The most prominent side-effect that we have here is the mutation of a data structure, and this can be avoided with immutable data structures.</p><p>But before doing that, we should ask ourselves: is it worth it? Should someone calling the function knapsack care if it is implemented using mutable variables? My opinion is that no: it doesn’t matter if knapsack is implemented with or without mutability, even from a functional programming point of view. Let me explain.</p><p>This is because the implementation of knapsack that I listed before is already “purely functional” with respect to observable mutations! Will it return the same results when called with the same arguments whether it is implemented with mutable or immutable data structures? Yes! Is it implemented with any side-effect? Well, it has a mutable variable but does that mutable variable escape the function definition? No. Is it a global variable whose mutation can be observed from the outside? No. Thus, with respect to “observable mutations”, the function knapsack is referentially transparent.</p><p>Having said that, the motivation behind implementing knapsack functionally is just an exercise on how we can model this problem in a functional fashion. The idea is just to stretch our “functional” muscles: to see how far the paradigm can take us in an apparently mutable domain. But my opinion is that, even when following the functional programming ideas, mutation is not bad as long as it’s just an implementation detail and not something that can be observed by the bigger system.</p><h3>What we have seen so far</h3><p>In this post we saw how to solve a common dynamic programming problem using an imperative approach. This involved mutation and a for loop. In the next post we will see an initial approach on how to do the same thing functionally.</p><h3>Additional resources</h3><p>If you want to know more about dynamic programming here are some good resources:</p><ul><li>The <a href="https://en.wikipedia.org/wiki/Dynamic_programming">Wikipedia page on DP</a> is actually very comprehensive.</li><li>The second part of <a href="https://www.coursera.org/specializations/algorithms">Stanford’s Algorithm’s course</a> has a section on DP.</li><li><a href="https://people.eecs.berkeley.edu/~vazirani/algorithms/chap6.pdf">This chapter</a> of Dasgupta, Papadimitriou &amp; Vazirani’s “Algorithms”.</li></ul><p>— <strong>Miguel Vilá</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=988528c00908" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>