Memoization in C++

The class of Dynamic Programming problems are very interesting. These problems all boil down to a class of optimization problems. Such problems are characterized by the distinctive reduction in their design. This reduction can lead to smaller sub-problems that are solved and the results bubbled up. In essence, dynamic programming problems are solved bottom-up, since the results of the sub-problems drive those above them.

The problem with such a technique lies in the fact that every solution requires you to solve every sub-problem to obtain an optimal solution. Memoization provides a simple and largely effective solution to alleviate this problem by storing results to these overlapping sub-problems for future use. Let’s look at an example using the evergreen fibonacci series:

Inside the guts of Memoization

The naive solution to recursive fibonacci series is :

int fib (int n)
{
    if( n <=1 )    return 1;
    else           return fib ( n-1 ) + fib ( n-2 );
}

Let’s look at what happens when we run it for a couple of values :

fib (5) ; // = fib(4)  + fib(3)
          // = (fib(3) + fib(2)) + (fib(2) + fib(1))
          // = ( (fib(2) + fib(1)) + (fib(1) + fib(0)) ) + 
          //               ( (fib(1) + fib(0)) + fib(1))
          // = ( ( (fib(1) + fib(0)) + fib(1)) + 
          //               (fib(1) + fib(0)) ) + ( (fib(1) + fib(0)) + fib(1))
          // 7 '+' operators.

fib (6) ; // 6 overlapping subproblems, 
          // results in '+' operator being run 11 times, 
          // extrapolating from above.

But we already calculated fib (5), didn’t we? And since

fib (6) = fib (5) + fib(4);
and
fib (5) = fib(4) + fib(3);

We’ve already calculated fib (4) too. So fib (6) needs to just add the 2 values, resulting in just a single ‘+’ operation.

To use this information in our solution, let’s use a global map that stores intermediate solutions. (we can use hash_maps for faster un-ordered  access when the TR1 specification and C++0x is standardized)

std::map<int, int> fibHash;

int memoized_fib (int n)
{
    std::map<int, int>::iterator fibIter = fibHash.find(n);
    if( fibIter != fibHash.end() ) return *fibIter;

    int fib_val;
    if( n <=1 )    fib_val = 1;
    else           fib_val = memoized_fib ( n-1 ) + memoized_fib ( n-2 );
  
    fibHash[ n ] = fib_val;
    return fib_val;
}

This is a fairly one off solution, and requires modifications for using with multiple functions… but with C++0x we can do fully automatic memoization as described here : http://cpptruths.blogspot.in/2012/01/general-purpose-automatic-memoization.html

Coming up : my feeble attempt at auto-memoization in the current version of C++.

 

Leave a Reply