Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C++ Hash Table Memoization: Simplifying Dynamic Programming


Getting The Max Value

More Insights

White Papers

More >>

Reports

More >>

Webcasts

More >>

If all I wanted to know was the maximum value that could be achieved, this single method in class Memo would be enough. Running a program that calls this routine for the tree in Figure 2 produces the following output:

GetMax(h)
GetMax(i)
GetMax(d)
GetMax(q)
GetMax(r)
GetMax(j)
GetMax(k)
GetMax(e)
GetMax(b)
GetMax(l)
GetMax(m)
GetMax(f)
GetMax(n)
GetMax(p)
GetMax(g)
GetMax(c)
GetMax(a)
Max = 31

GetMax() prints out each subproblem it's trying to solve, and you can see that each node is only called once. The listing also shows you that the nodes are evaluated in postorder, which is the order we would have needed to simulate with a pure bottoms-up implementation.

Getting the Solution

As I said, the implementation as is returns the maximum value, but to be really useful we need to actually know which of the people on the org chart are going to be invited to attend Bill's exercise class.

To come up with this list, we need to traverse the tree, only looking at nodes that are included as part of the solution. Nodes that are not included are those nodes that are skipped over because their parent is attending.

Just knowing that a node is included as part of the solution is not quite enough. A node could be included, yet not be attending because the maximum value for that node is achieved by not attending.

The code that actually develops the solution is called MarkIncluded(). It is called with the root node as the target, then successively walks down through the tree, marking child nodes that are part of the solution. As each node is marked, I can see if it is going to be attending, and its name is printed out:

void Memo::MarkIncluded( const Node &node ) 
{ 
  mSolutions[ &node ].mIncluded = true; 
  if ( mSolutions[ &node ].mAttending ) 
    std::cout <<node.Name() <<" "; 
  for ( size_t i = 0 ; i <node.Children() ; i++ ) { 
    if ( mSolutions[ &node ].mAttending ) 
      for ( size_t j = 0 ; j <node.Child( i ).Children() ; j++ ) 
        MarkIncluded( node.Child( i ).Child( j ) ); 
    else 
      MarkIncluded( node.Child( i ) ); 
  } 
} 

The logic is pretty simple. When this method is called for a node, that node is automatically marked as included in the solution. Depending on whether this node has chosen to attend or not, I either mark all of its children as included, or mark all of its children's children.

Adding this code to the program lets me produce this output for the tree in Figure 2:

Attendees: d e q r c l m n p

A quick check of the weights of those nodes shows that their sum is 31, which agrees with the output shown above.

Graphical Output

I like being able to see the results of of my algorithm in a graphical format, so I added one additional method, PrintTree() to class Memo. This method takes the final tree and prints it out in a format that can be processed into a nice graph using dot, one of the programs in the Graphviz package, a free set of tools from AT&T.

The final result of running the data set in Figure 2 through my test program is in Figure 3.

Figure 3: Program Output Using GraphViz

In Figure 3, nodes that are not attending are outlined with a dotted border. This means that either their parent node has chosen to attend, excluding them, or their optimal solution calls for them to not attend.

The individual node weight and name are shown in the two cells in the top half of the node. The bottom half has two cells that show the calculated value of GetMax() for that node, and the decision made as to whether that node attends or not in order to reach that maximum value.

Running the Test Program

The test program (available here) I used to create this output is a simple program that reads in a tree definition file, outputs the program data to the console, and creates a GraphViz format output file. It's run with two arguments, an input definition file (one sample is included), and the desired output file name:

memo tree01.def tree01.dot

Project and/or make files are included for Visual Studio 2003/2005 and gcc.

Note that the format of the tree definition file is documented in source code.

The main() routine for the test program (minus some error handling) looks like this:

int main( int argc, char *argv[] ) 
{ 
  std::ifstream input( argv[ 1 ] ); 
  Node root( input ); 
  Memo m; 
  std::ofstream output( argv[ 2 ] ); 
  std::cout <<"Max = " 
    <<m.GetMax( root ) 
    <<"\n"; 
  std::cout <<"Attendees: "; 
  m.MarkIncluded( root ); 
  std::cout <<"\n"; 
  m.PrintTree( output, root ); 
  return 0; 
} 

This program should give you an idea of how to harness the power of Dynamic Programming to solve some fairly difficult problems with relatively simple algorithms. Dynamic Programming is the key to this, and memoization with C++ hash maps helps make it possible.


Mark Nelson is a contributing editor to Dr. Dobb's Journal. He can be contacted at marknelson.us.


Related Reading






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.