Lab 3 To be submitted before 5pm 3 February. Suppose we want to find something that is too difficult to find by an exhaustive search. (For example, find a noncyclic latin square of order 30 - backtrack search will run "forever" on this.) Start with an approximate solution (for example any square array which has the numbers 1,..,30 in it, each occurring 30 times). Find a function (which we'll call the error function) that measures how far our approximate solution is from a solution. Hillclimbing is: repeat make a random small change in the approximate solution if the error does not increase, keep this as the new approximate solution until (hopefully) a solution is reached (error = 0) To illustrate, lets think of using hillclimbing to find a latin square of order 4. First method: We might start with the approximate solution X 1 2 2 4 3 2 2 1 1 4 1 4 3 3 4 1 It is natural to take the error in a row or a column to be the number of missing entries causing it to not be a pemutation of 1,..,4. Our approximation has row errors 1,1,2,1 and column errors 2,1,1,2. We'll take the total error to be the sum of these, 11. A solution would have total error 0. What would be a "random small change"? Pretty obviously, it would be to randomly choose two postions in the array X and interchange the entries there. For example, if we interchanged X[2][0] and X[1][2] the new total error is 9 so we are closer to a solution. We can think of a "search space" in which the "points" are all possible approximate solutions (including solutions) and in which points are adjacent if one can be obtained from the other by a small change. Hillclimbing starts at a point in this space and randomly seeks a path from it to a solution. The path it seeks is a special one - is is one along which the error does not increase. In our first method, starting from the approximate solution 1 2 2 4 3 2 2 1 1 4 1 4 3 3 4 1 the search space is L A R G E. Second method: Start wih the approximation 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 noting that it rows are already permutaions of 1,..,n and that we only need to get rid of the errors in is columns. Thus we shall make random small changes that preserve the property that the rows already are permutations. A random small change is: randomly choose a row then randomly choose two positions in that row and interchange the entries in those positions. The search space now consists of all arrays whose rows are permutations. This search space is MUCH SMALLER. That is why the second method is better. QUESTION - in our explanation of hillclimbing, why did we not say "if the error decreases, keep this as the new aproximate solution" ? NOTE: Hillclimbing is not guaranteed to work - one hopes it will hit a solution within a "reasonable" time. NOTE: The hillclimbing may make its way into a region of the search space from which there are relatively few "reasonably short" special paths to a solution. (There might even be no special paths leading to a solution.) The hillclimbing might then continue running "forever". So it is best to abort it after a "reasonable time" and start over. It may even be that from our initial approximation there is no special path to a solution. So it is often a good idea to choose the initial approximation somewhat randomly. NOTE: The parameters "reasonable time", "forever" and "reasonable number" are detemined by experiment (and the patience of the experimenter). For lab2 you must submit a program 3.c (or .cc or .cpp) that uses hillclimbing to find nxn latin squares. I suggest you use the second approach dicussed above because it so reduces the size of the search space. In view of the notes above I suggest that you start with an nxn array each of whose rows is a random permutation of 1,..,n. I suggest that whenever you find a solution you restart with a new aproximation. I suggest that you do not implement the aborting after a reasonable time (or after a reasonable number of small changes failing to reduce the error) unless you find you need to. With these kind of progams what matters in the computer is raw CPU speed. I shall compile and run your program on a machine with a 2.4Ghz CPU - I shall expect your program to produce latin squares of order 30 at the rate of about one every 3 seconds or better. Your program takes two command line parameters - the first is the order of the latin squares, the second is the number of squares to find. For example phillips3 30 10 writes 10 30x30 latin squares to the console then teminates. Final note: Your program will be generating random numbers a huge number of times. What matters is how fast the random number generator is, not how good it is. I suggest we all include , initialze the built in random number generator with srand(time(NULL)) and generate a random number in the range 1,..,n with rand()%n. (Other methods have caused some problems in lab1 through incompatibility between different C++ compilers - this should not happen but it did!) Have fun.