Course Home | Homework | Programming | Schedule & Lecture Notes | Submit

Saint Louis University

Computer Science A220/P126
Data Structures and Object-Oriented Programming

Michael Goldwasser

Spring 2005

Dept. of Math & Computer Science

Programming Assignment 02

Magic Squares

Due: Thursday, 3 February 2005, 8pm

Please see the general programming webpage for details about the programming environment for this course, guidelines for programming style, and details on electronic submission of assignments.

The files you may need for this assignment can be downloaded here.

Collaboration Policy

For this assignment, you are allowed to work with one other student if you wish (in fact, we suggest that you do so). If any student wishes to have a partner but has not been able to locate one, please let the instructor know so that we can match up partners.

Please make sure you adhere to the policies on academic integrity in this regard.


Contents:


Overview

A magic square is an arrangement of the numbers from 1 to n2 in an nxn matrix, with each number occuring exactly once, and such that the sum of the entries of any row, any column or either main diagonal is the same.

For example, here is an example of a 3x3 magic square:

 2  9  4
 7  5  3
 6  1  8

Notice that the values in any row, any column, or either of the two main diagonals sum to 15 in this example. More generally, the target sum for an nxn magic square must be precisely n(n2+1)/2, as we know that the numbers from 1 to n2 add up to (n2)(n2+1)/2, which will be divided evenly accross n rows (or respectively columns).


Brute Force Approach

Page 155 of the text discusses a generic way to solve puzzles such as this, essentially relying on the brute force of modern computers to compute and test all possible assignments. The recursive approach starts with an empty square and a list of unused values. It considers what value to place in the next available cell of the square, trying each possibile value from the list of unused values. For each one, it recurses with the slightly more complete square and a new list of unusued values (with the newly used value no longer on such list).

In class, we saw how this approach could be directly applied to the magic squares problem. Tracing through the recursion, the first completed square is

 1  2  3
 4  5  6
 7  8  9

Of course, this square is not magic. So the recursion backtracks and considers other possible assignments, such as

 1  2  3
 4  5  6
 7  9  8

eventually exhausting every possible assignment, and reporting those which were truly magic.

We are providing you with working code which implements this brute-force approach to solving the problem. And indeed, for a 3x3 square, the computer is fast enough to try all possible assignments in a matter of seconds, reporting 8 correct solutions (though we note that all 8 of these are actually symmetric to each other).

Unfortunately, as fast as computers are, we seem to hit a road block trying this out on larger squares. Even for a 4x4 square, the original program seems to take way too long (i.e. days?!). The reason is that there are far too many possibilites, and thus far too many recursive calls being made. The following chart outlines some of the numbers involved

n Number of Actual Magic Squares
(ignoring symmetries)
Number of Candidate Assignments
(i.e., n2!)
Number of Recurisve Calls
made by brute force approach
Running Time
(on turing)
1 1 1 2 0.00 sec
2 0 24 60 0.00 sec
3 1 362880 986410 1.15 sec
4 880 20922789888000
(that 20 trillion)
even more c. 240 days
5 275305224 25!
(over 1025)
even more ha!

Our Goal

The goal of this assignment is to show how relatively simple strategies can be used to streamline the process in a remarkable way. Using the 4x4 case as a benchmark, we will show how the brute force approach, which takes eight months worth of computations, can be improved to a program which finds all solutions in under 10 seconds of computation time.

In the following section, we outline a series of specific improvement strategies, each of which you must implement. Please be careful to ensure the correctness of your program as you make changes. Though our original verson is quite slow, it works correctly. There is little use for developing a faster program which is wrong!

We recommend that you take these one at a time, using the 3x3 case for testing, and the 4x4 case as a benchmark for measuring the efficiency.


Required Improvements


Expected Speedup

To give you some benchmarks to compare to, here are some statistics we've gathered about the improvements we achieve through a combination of the improvements for this assignment.

For the 3x3 case, even brute force is quick enough, but we summarize the internal benchmarks as follows:
Improvements used Number of Recurisve Calls Running Time
(on turing)
Brute Force 986,410 1.15 sec
consider pruning
upon complete
row/col/diag
2378 0.00 sec
also prune when
not canonical
1200 0.00 sec
also with improved
order of fill
312 0.00 sec
and with
Extra Credit
222 0.00 sec

For the 4x4 case, we observe the following efficiencies:
Improvements used Number of Recurisve Calls Running Time
(on turing)
Brute Force > 20 trillion c. 240 days
consider pruning
upon complete
row/col/diag
630,492,673 28 min
also prune when
not canonical
314,969,485 14.9 min
also with improved
order of fill
2,571,437 8.87 sec
and with
Extra Credit
1,313,885 5.54 sec


Files We Are Providing

All such files can be downloaded here.

For this assignment, we will be providing you with several files which comprise the complete program, though you will not need to modify, or even read, many of them. We will briefly discuss the purpose of each file:


Using the Driver and Debugging Options

The driver expects a single command-line argument which is the square's width, n. It then begins the recursion. In order to provide feedback as it executes, it provides the following output:

For ease of debugging, we have added additional functionality. If a second command-line argument is given to the driver, such as

Magic 4 debug
the software automatically enters a debugging mode in which the partially filled square is printed as each recursive call is made. Of course, this will generate a large amount of data. You would probably not be able to sift through this much data, but you may wish to look at the beginning of it. This can be done by piping the output to the more command, such as:
Magic 4 debug | more
Alternatively, you can redirect the output to a file and look at it later, though the file may become huge. This would be done as
Magic 4 debug > filename

Finally, you may wish to print out a (partial) square from other places of your code during development. Please note that we already provide an overloaded output operator. This would allow you to print your current square from within any method of the square class using the syntax:

std::cout << (*this);


Files to Submit


Extra Credit (1 point)

The lesson thus far has been that it is in your best interest to prune the recursion as soon as you are able, even if you must use more complicated logic in determining infeasible partial solutions.

In the original assignment, we waited until a given row (resp. column, diagonal) was complete, before checking its sum and pruning if it did not match the target. However, in some cases, we should be able to prune even sooner. For example consider the following partial solution

 1  2  3  . 
 .  .  .  .
 .  .  .  .
 .  .  .  .

Given that the target value for 4x4 is 34 and that individual values are between {1, .., 16}, it is pointless to continue on the above example. No single value is enough, given that the row sums to 6 thus far. In similar spirit, if the first three entries were {16, 15, 14} we may as well stop, as this already sums to 45, not to mention the remaining square which will add at least one.

For extra credit, rewrite the partialValidate to detect such cases, ideally further improving the overall efficiency of the process.


Michael Goldwasser
CS A220/P126, Spring 2005
Last modified: Wednesday, 05 January 2005
Course Home | Homework | Programming | Schedule & Lecture Notes | Submit