Let me just make some comments on the basic backtracking
approach.  You will find the very same ideas in 8-Queens
and in the Knapsack problem.   I have added comments to
the skeleton: you can see the real method below.  Note
that you don't see all details in all problems.  We needed
to 'remove queen' in the 8-queens, while that wasn't
needed in Knapsack or in this problem.  In the Knapsack
method we gave you, we never recorded the solution.
That is addressed in the dynamic programming version
in the lecture notes, and could be added to the basic
Knapsack function as well.

I've included another backtracking example to look at.
The problem is to write an integer as the sum of 4 squares.
This is always possible, and makes a simple example.

Below you will find three versions
        General Backtracking framework
        Walk through the squares problem.
        Real solution to the squares problem.
The third solution is much like the second, but
the comments are more terse.  There are also some
ideas on how to speed up the solution.

static boolean
backtracking(some parameters)
{
        // Are we done? then return true

        // Have we no hope? then return false

        // Try each 'position' in turn
        for (first position to last postion)
        {
                If (this looks like a legal postion)
                        // Can I use this step and solve the rest of 
the problem?
                        if (backtracking(parameters modified to 
reflect my position))
                        {
                                remember my position
                                return true;
                        }
                        else
                                remove any traces of my position and try again
                                        in the next iteration of the loop
        }

        // If I reach here, I was not successful
        return false;
}

Pseudo code for the problem of 4 squares

//      Returns
//              Boolean - were we able to find a solution?
//      Input
//              value - integer we wish to express as the sum of squares
//              num   - number of integers we have to work with.
static boolean
tryslow(int value, int num)
{
        // Logic to deal with base cases:
        // Are we done?
        if (value == 0) return true;

        // Have we no hope?
        if (num == 0) return false;

        // Now start at the begining (1*1) work up
        // trying each square in turn.
        // Starting at 1*1, try 1, 4, 9, 16, etc
        for (int i = 1; i*i <= value; i++)
        {
                int sq = i*i;

                // How does this help us solve the problem?
                // Rather than trying to represent 'value', we
                // only need to represent 'value - i*i'
                // Note that we have one less square to work with
                if (tryslow (value-sq, num-1))
                {
                        // If we suceeded, record the result and return true
                        System.out.print(" " + sq);
                        return true;
                }
        }
        // None of the squares worked.  Try again.
        return false;
}

Real code for 4 squares, and some ideas on how to speed up the
solution to this problem.  The speedups are not shown below.

//      warring.java
//
//      Every positive integer can be expressed as the sum of four squares.
//      Thus 7 = 4 + 1 + 1 + 1
//
//      This program uses backtracking to discover such a sum for the first
//      200 numbers
//      The initial attempt solves the problem starting with 1*1, then 2*2, etc
//      We use two techniques to speed up the backtracking
//      1st Rather than start with 1*1, we start with the largest square
//              less than or equal to the number.  This is almost 40 
times faster
//      2nd We make the second recursive call stop searching when the square
//              is less than a third of the remainder, and make the second call
//              stop when the square is less than half the remainder.  This
//              is about twice as fast.
//
....
//      tryslow
//
//      The slowest of the three methods.  Start at 1 and work up.
//      Returns
//              Boolean - were we able to find a solution
//      Input
//      value - integer we wish to express as the sum of squares
//      num   - number of integers we have to work with.
static boolean
tryslow(int value, int num)
{
        if (value == 0)
                return true;    // Are we done?

        // Base case: we have no more numbers to work with
        if (num == 0)
                return false;

        // Start at 1 and work up
        for (int i = 1; i*i <= value; i++)
        {
                int sq = i*i;           // Place guess
                if (tryslow (value-sq, num-1))
                {
                        System.out.print(" " + sq);
                        return true;
                }
        }
        return false;   // Nothing worked.  Backtrack
}

- jeff parker

Reply via email to