B Bra HHHSfln m an H ■ *• jjjj RS 8 H Hr Hk iri 1 ■B B ■ ■ B ARE M ■ H ■ Digitized by the Internet Archive in 2013 http://archive.org/details/useofmacrosinbac687bitn , „ uiucdcs-r -7^-687 «-? USE OF MACROS IN BACKTRACK PROGRAMMING by James Richard Bitner December 197*4- The i -' t^.o JAN 1 « 75 DEPARTMENT OF COMPUTER SCIENCE UNIVERSITY OF ILLINOIS AT URBANA-CHAMPAIGN URBANA, ILLINOIS uiucdcs-r-7^-687 USE OF MACROS IN BACKTRACK PROGRAMMING by James Richard Bi trier December 197^ Department of Computer Science University of Illinois at Urbana-Champaign Urbana, Illinois 6l801 This work was supported in part by the Department of Computer Science and in part by the National Science Foundation under Grant GJ-^1538 and was submitted in partial fulfillment of the requirements for the degree of Master of Science in Computer Science, 1975* I II r- ft V ! 1 ill Ill ACKNOWLEDGMENT I wish to thank Professor E. M. Reingold for his help in the preparation of this thesis, and Connie Slovak for the fine typing job. Finally, I thank our graphics department for the five excellent figures in this paper. i .'* Il i IV TABLE OF CONTENTS Page 1. INTRODUCTION 1 2 . THE QUEEN ' S PROBLEM 6 3 . THE Y-PENTOMINO PROBLEM 12 k. SQUARING THE SQUARE 23 5. DIFFERENCE PRESERVING CODES 26 LIST OF REFERENCES 31 ;-* LIST OF FIGURES Figure Page 1 . Solution to the 5n x 12 pentomino problem 13 2 . The board on which placement occurs li+. 3 . The pieces and how they are formed into a tree l6 h . Extending and matching regions o . . . 19 5 . The effect of the second symmetry 29 I II II I iii 1. INTRODUCTION Backtrack is a programming technique to perform an exhaustive search for a solution to a given problem. The solution we are seeking can be expressed as an n-tuple (a , ..^a ), where the dimension n may- or may not be known beforehand. Backtrack searches for solutions by continuously trying to extend partial solutions and "backtracking" to shorter solutions when no extensions are possible. Currently, this method is used in a wide range of combinatorial problems including parsing (l), game -playing (15), and optimization (10). Other appli- cations are outlined in (8). We begin the procedure by choosing the smallest of the possible candidates for a . We then choose the smallest a such that (a,, a ) is a partial solution, and continue to extend the partial solution until a solution is found (and the program halts), or there are no choices for some a. . We must then "backtrack" and make another l choice for a. ... The procedure continues in this manner until all l-l choices for a have been exhausted or a solution has been found. In the following more formal description (l8), S, is the set of choices for a, to extend the vector (a_, . . .,a, , ) . Compute S k-1 while k>0 do ( while S, / do / a^_ *- smallest element in S if (a , . ..,&.) is a solution then record it k *■ k + 1 k<-k - 1 STOP all solutions have been found Compute S, . There are several heuristic techniques to speed up backtrack, and these are the topic of this thesis. One of these is the use of macros, Backtrack programs are usually rather short, and we are willing to use a longer program that requires more storage, if it will execute faster. Macros can be used to achieve this. If the length of the solution is known, a macro can be written to copy the inner loop of the backtrack procedure n times. The macro then looks like : Compute S. L. : if S.= then go to L. _ l — i r a l-l a. <- minimum element of S. i l S. «-S. - (a.) l ii This macro (called CODE i here) is then used as follows CODE CODE CODE n record (a..,..., a ) as a solution go to L L : stop — all solutions have been found As we shall see, macros are also useful when the length of the solution is not known. This technique results in several savings. (1) The step "Compute S." may be very different for different i. Here, we can use the macro to "customize" each block to compute S. in the most efficient way. (2) Often, this facilitates the greater and easier use of registers, which are much faster than memory. Each block can have exclusive use of one or more registers. This can be accomplished during the expansion of the macro, but is in general difficult to accomplish in a non-macro program. (3) No costly loop counter is needed, and we need not continually branch to the top of a loop. In addition, no end checks are necessary. We need not always test for advancing from the final level or backtracking from the first level. The macro program only branches when backtrack is necessary. In addition to these savings, others result depending on the specific program. Another way of viewing the backtrack procedure is as a search throuch a forest. Roots of the trees in the forest correspond to choices for a.. . Their sons correspond to possible choices for a^, given the particular choice for a, . A solution can then be viewed as a path from a root to a terminal node. The macro technique previously discussed was a method of increasing the speed of the program (that is, the rate at which we process nodes). There are several other methods that decrease the number of nodes in the search tree, and hence, also speed up the program. (1) Preclusion - This is a very general technique found in nearly all backtrack programs. In the generation of solutions, back- tracking should occur as soon as a partial solution is found that will not produce any solutions. As an example, let us consider the queen's problem (discussed in Part 2): in how many ways can n non-attacking queens be placed on an nxn chessboard? A very simple use of preclusion would be to note that no two queens can occupy the same row or column (else they would attack each other). Hence, only permutations on the numbers (l, ...,n) are considered; any other kind of placement would never produce a solution and is ignored. (2) Branch Merging - When possible, do not search branches of the tree that are isomorphic to branches that have already been searched. In our example, we can note that solutions with the first queen in a row > [— ] are isomorphic to a solution with the first queen below row [— ], by reflection about the middle. We need only search through those branches where the first queen is below row |"p] and remember that for each solution found, there is another one isomorphic to it. (3) Search Rearrangement - In general, nodes of low degree should occur early in the search tree, and nodes of high degree should occur later. Since preclusion appears to occur at a fixed depth, fewer nodes may need to be examined. In the queen's problem, after placing the queen in the first column, we next place a queen in the second column. Though any other column would be permissible, the second offers us fewer alternatives. In later columns, the diagonal lines of attack of the first queen may have spread off of the board, prohibiting fewer squares. Sometimes, we even search for the extension that offers the fewest choices (see Part k). (k) Branch and Bound - This technique is used primarily when we are searching for a solution of minimum "cost", such as the traveling salesman problem. Once a solution is found, all partial solutions with greater cost (assuming all costs are positive) can be discarded. In using this technique, it is beneficial to get a good solution early in the search, and it is sometimes necessary to arrange the search so good solutions will be found early. The following programs illustrate the use of macros and these four programming heuristics. 2. THE QUEEN'S PROBLEM The problem is to count the number of ways to place n queens on an nxn chessboard so that no two queens attack each other. Usually, both the total number of solutions and the number of inequivalent (under rotations and/or reflection) solutions are desired. This problem has been extensively investigated; the first mention of it seems to be in (2), while most of the pre-computer results are in (9) and (lk). Walker (l8) used backtrack on SWAC to find the number of solutions for 6gn^l3» Lin (ll) found the number of solutions for n = Ik, and a program written by Bunch (3) took three hours to solve the n = 15 case. The following describes a program that cut this time to 25 minutes and also solved the n = 16 case. There are some observations that allow us to cut down the size of the search tree. We need not consider all conceivable placements of queens; some can be immediately precluded because they will never result in a solution. For example, no two queens can occupy the same row (else they would attack each other). Similarly, no two queens can occupy the same column. Thus, we can view a solution as a permutation (a.,,..., a ) where a. is the row of the queen in the i column. In addition, not all solutions need be generated. Here, we can use the branch merging technique. If two solutions are known to be equivalent, we need only generate one of them. If we need to know the total number of solutions, then each solution we generate that is equivalent to another adds two to our solution count instead of one. There are several such cases. First, a solution with a > |*^] is equivalent (by reflection about the middle of the board) to another solution with a < [-] . We will only generate those with a < |"^"|, and count each as two equivalent solutions. In a similar manner, if n is odd and a.. = -r— (the middle row), each solution with a > [^] + 2 is equivalent to a solution with a g [— 1 -2. Note a = f— ] and a = [2.] ± 1 are not allowed, since they would attack the first queen. Finally, we need not generate any solutions where a, = 1. Any such solution is equivalent to a solution that we will generate. A solution may have a queen in at most one of its corners, since two would be attacking. Hence, if we rotate the solution l80° (and reflect about the middle if the new a.. > ["— ] ), we obtain an equivalent solution that will be generated. It now meets our requirements : the new a / 1 since the queen now in the first column did not start in a corner, and the final reflection insures a g [— ']. To summarize, the branch merging technique allows us to restrict a^ and a as follows: and if n is odd and a.. = -r— , 1 g a g \— ] - 2. The program for an n x n board generates n blocks of code. Each block has the responsibility of placing a queen in a certain column, making sure it does not attack any previously placed queens, and checking that it satisfies the restraints mentioned above. Note In fact, after the programs had been run, it was noted that placements . , , n+1 , with a = -£— need prohibiting a-^ = 1. with a = £— need not be considered. The reasoning is similar to that 8 that the macro approach is useful here, especially in the checking for the preclusion restraints. Testing for these in a non-macro version would require a test on each pass through the inner loop. For example, to test for 1 % a p % [p-] -2, we would have to check whether we are about to place a queen in the second column. Then we can check for l^apg [— 1 -2. A vast majority of these tests are wasted, since the check for the second column almost always fails. Most of our time is spent deeper in the tree. In the macro version, we need not worry about whether the queen is in the second row or not. The test for l|a g [^-1 - 2 is only generated in the second block, and not in the others where it would fail every time. Similar remarks hold for symmetry checking in the first column. A test that is performed very often, and hence must be made very efficiently, is the test to see if a queen will be attacked by any previously placed queens if placed in a given square. This is done by keeping three bit vectors, LEFT, CENTER, and RIGHT. When we place a queen on the board, it may attack squares in three different directions: (1) Diagonally, moving upwards (LEFT vector) (2) Horizontally (CENTER vector) (3) Diagonally, moving downwards (RIGHT vector) A bit which is set in one of these bit vectors tells us that square is prohibited by an earlier queen attacking in the corresponding direction. We can determine which of the squares are not attacked merely by OR-ing the three vectors together. Zero bits in the result correspond to squares not attacked. These vectors are also easily updated. When we place a queen in row i, we first save the three vectors so they can be restored when we backtrack. We then need only set the j^* 1 bit in each vector, shift the LEFT vector one position left, and the RIGHT vector one position right. Now the proper positions will be prohibited, when we wish to check the next column. Again, the use of macros proves useful. The bit vector of all unattacked positions (obtained from the OR operation) is stored in a different register for each column. Each block is coded differently in order to use the register reserved for its row. The three bit vectors are also stored entirely in the registers, but since only 15 registers are available for use, the registers which the vectors are stored in depends on the current block. For the first three blocks, they are in registers 12-11+-. Then the contents of registers 0-2 are saved in storage and the vectors are moved in there. Again, the macro technique is useful. The first three blocks reference registers 12-11+ for the vectors. The third block moves them to registers 0-2, and the remaining blocks reference these registers. The fourth moves the vectors back to registers 12-11+- when backtrack from there occurs. Note how useful it is to "customize" each block. We continue placing and removing queens until n queens have been placed. We now have a solution which must be checked for isomorphisms. We rotate and reflect the permutation through all eight equivalent solutions. It is recorded only if all isomorphisms (excluding those with a.. = l) are lexicographically greater than it. Otherwise, it is discarded since we have already generated an equivalent solution. Note that isomorphisms with a = 1 must be ignored in this checking since they are not generated. A permutation (a , ...,a ) is lexicographically greater than a permutation (b..,...,b ) if for some n, a. =b. for i < j and a.>b.. 10 The use of macros is also very helpful in this checking. The first step in the testing is to convert the solution (in which the a. bit is set in the i th word) to the numbers 0, ...,n-l. This is accomplished by table lookup. Each a. is regarded as a number from 2 to 2 , and this is used as an offset to reference a table which has 0, ...,n-l in the correct locations. The table is rather large (32K) but as usual, we can afford to sacrifice storage to gain an increase in speed. Now the solution must be rotated and reflected. The contents of the registers must be altered as follows. Suppose register i initially has contents j". Then after the rotation, register j must have contents n-l-j (the registers are numbered through n-l). To accomplish this, the macro generates n "store" instructions followed by n "load" instructions. The i h store instruction stores the contents of the i-l s ^ register into the register field of the i h load instruction. After all registers have been stored, the i^ n load instruction loads the constant n-i into the register determined by the actions of the store instructions. Reflection is accomplished in a similar manner, with the i"kh load instruction loading the constant i. To test each isomorphic solution against the original, n comparisons and 2n conditional branches are generated. The i^ n comparison compares the i" 1 component of the isomorphic solution with ■che i^ n component of the original. Two condition branches follow each comparison. If the isomorphic component is greater, we branch to the next rotation. If the original is greater, the original solution must be discarded. If the two are equal, neither branch is taken, and we i ( compare the next pair of components. ( 11 Without the use of macros, we would need to use a loop to index through the permutations. This would require that both permutations be kept in storage, since it is difficult to index through the registers. Instead, we generate n instructions, each one pertaining to a specific register. No indexing is necessary, and the isomorphic solution can be kept in the registers. The program succeeded in solving the problem for n ^ 16. Some of the results are given in the following table. The l6 x 16 case was done in seven separate runs, each one with the first queen in a different row. n number of total number of mequivalent , . / . \- n , . , .. time (mm) solutions solutions 1^ 365,596 ^5,752 4 15 2,279,18^ 285,053 25 16 14,772,512 1,846,955 168 All programs were run on the IBM System/360-75 a "t "the University of Illinois at Urbana-Champaign. 12 3. THE Y- PENTOMINO PROBLEM This problem is to find the smallest n such that a 5n X 12 board can be covered by the Y-pentomino Previously, the best solution was due to D. Klarner, with n = l6 (lk) . Again, macros prove useful. Macros were first used by Fletcher (5) for such pentomino problems, and later used for soma-cube problems by Peterson (12). The following describes a program that found solutions for n = 10 and n = 11 and demonstrated by exhaustive search that no solutions exist for n<10. Further, the solution for n = 11 gives solutions for all n ^ 11 (see Figure l). The program considers each of the eight isomorphisms of the pentomino as a separate piece and tries to cover the board with them. The check of whether a piece can be placed is accomplished by examining the five squares that the piece would cover. All of these must be zero (indicating an empty square) for the placement to be legal. Once a piece is placed, the squares it covers are made non-zero to prevent another piece from covering them. The board on which this placement occurs is surrounded by non-zero squares to prevent pieces from extending off of the board or "wrapping -around" from one row to the next (see Figure 2 ) . The program proceeds sequentially through the board and tries to cover the first empty square it finds. If a piece can cover that square, and does not also cover any previously placed pieces or extend 13 _r This figure gives solutions for the 5nxl2 problem for n 1 10. The 50x12 solution is formed by using the tiling on the left (without the shaded area) as the upper half of the solution and the tiling on the right as the bottom half. To produce a solution for n >10, the shaded area is repeated n - 10 times. Figure 1 Solution to the 5nxl2 pentomino problem Ik The 5n xl2 board is surrounded by non-zero squares (shaded in the figure above) which prevent pieces from extending off of the board or "wrapping around" from one row to the next. Figure 2 The board on which placement occurs 15 off of the board, the program places the piece and advances to the next empty square. If no piece can cover the given square, we back- track by removing the most recently placed piece. The program proceeds in this manner until a solution is found, or all possible ways of placing the pieces has been exhausted. A clever method due to Fletcher (5) is used to determine which pieces can cover a given square. i/fe first number the squares of the board (including those between the rows) consecutively starting with one. Each piece is put anywhere on the board and the lowest numbered square it covers is noted. This square is called the lead square. The number of the lead square is subtracted from the numbers of each of the four other squares, giving four offsets from the lead square. This procedure is repeated for all eight pieces. Some of these offsets occur many times. It would be wasteful to check the same square several times, so the offsets are formed into a tree (see Figure 3)« Each node in the tree has an offset as its value, and each path from the root to a terminal node corresponds to one piece. The offsets along this path tell us which squares the piece will cover. All of these must be empty or they cannot be placed. The macro generates code to traverse this tree in preorder (i.e. a node is traversed, and then its subtrees). At each node, the program tests the square whose offset from the lead square equals the value of the current node. If the square is empty, we continue deeper into the subtree. If it is full, this node's subtree is not checked since none of those pieces can be placed. The macro generates nested blocks of code of the following form. 16 14 15 16 17 PIECE #1 16 31 32 48 PIECE* 5 p I?; 15 16 17 18 15 16 32 48 PIECE #2 PIECE* 3 16 32 33 48 17 PIECE #6 PIECE #7 ii II 16 17 32 48 PIECE #4 1 2 3 18 PIECE #8 The eight isomorphisms and then offsets from the lead square. Note the final board had a row of length 16. Twelve squares were of the original board, and two at each end were used to stop "wrap-around" Below is one of the trees that could be used to test for placement of these pieces. 14 18 15 17 31 33 Figure 3 The pieces and how they are formed into a tree 17 For internal nodes : i <- value of the present node Fill square with offset i If the square with offset i J Call the macro recursively from the lead is empty THEN \ for each son of this node I Clear square with offset i For terminal nodes : ±<- value of the present node If the square with offset i r Fill square with offset i f Execute a subroutine branch from the lead is empty THEN \ to "NEXT" Clear square with offset i The macro is invoked as follows: NEXT : Push return and lead square addresses onto the stack Find the next empty square and make it the new lead Execute the expanded macro Pop return and lead square addresses of the stack Branch to the return address When a terminal node is reached, an entire piece has been placed, and a jump is executed to code ("NEXT") that pushes the lead square address and the return address onto a stack (for use in back- track), finds the next empty square, which becomes the lead, and reexecutes the expanded macro to check if the new lead square can be covered. When control passes out of the bottom of the macro, all coverings of the current lead have been tried, and the program back- tracks by popping the two addresses off of the stack and returning (back into the macro) to examine new coverings of the previous lead square. The stack initially contains the address of a final print routine, so the program will branch there after all placements have been tried. 18 This method is very efficient and checked all ng5 for solutions and found none in h minutes. The n = 6 case ran out of time after 30 minutes. Using this program as a "basis, we can solve the problem, but that requires a little analysis. Let us look at the region that the program has covered at any given moment. If this region can be covered in several different ways, the program will act identically after each time it covers the region. That is, the particular tiling of the region does not matter, only the shape of the region. A way of utilizing this would be to record the regions generated in the n = 1 case, discarding any that are identical. These can then be used as starting points for generating regions for n = 2. This use of branch merging would be effective in discarding many equivalent regions. It would have the added bonus of supplying a "head-start" for n = 2,3> ••• since we need only extend the regions for n-1 to get the regions for n. It is not necessary to tile the whole region, only the extension from n-1 to n (see Figure k-a.) . These regions can be used in another way to reduce the amount of work necessary. The earlier method would continue to extend these regions until it succeeded in filling a 5nxl2 board (or more likely, ran out of time). We can now do better than that since we will be able to generate a solution by trying to "match" two regions together, one forming the upper half of the solution, the other the lower half. That is, by generating regions of size n, we can check for solutions of size 2n+l (see Figure hh) . Since the growth of the search tree is exponential, a great savings is introduced here. 19 5X12 BOARDS (a) A region of size n -1 (covering n -1 5 xl2 boards) can be "extended" to a region of size n. In the process of extension, only the shaded area is generated, not the entire region. n < n 4 ♦ i n < n < - (b) We try to "match" two regions together as shown. Note the resulting solution is size 2n+l. Figure k Extending and matching regions 20 Still, a given solution can be divided into two regions in many different ways, and the above would find all of these divisions. We need only generate one such division to find any solution. Fortunately, there is a method to divide any solution into two unique regions. This method forces the regions to have certain properties. Hence, we will reduce the number of regions to be generated by considering only such regions (called "boundaries"), and still find a solution if one exists. To accomplish this division, divide the solution to the 5n x 12 board into 5x12 "sub-boards", starting at one of the ends. Look at the \— ] sub-board. All of the pieces above this sub-board belong to the upper half, and all those below belong to the lower. To determine which half a piece in the \— ] sub-board belongs to, count the number of squares it has in rows 1 and 2 of that board. Compare this with the number in rows k and 5, and ignore row 3» Assign the piece to the upper half if the first count is greater; the lower half if the second is greater. Because of the particular structure of the y-pentomino, the two counts can never be the same. We now must consider exactly what properties a region must have in order to be a boundary. It is immediate that a boundary must fill all of its sub -boards except the bottom one. Row 1 in the bottom sub -board must be filled since any piece covering a square in row 1 must lie in the upper half. Only pieces 1,2,7,8 may have their lead squares in row 2 since if pieces 3-6 have their lead squares there, they must belong to the lower half. Squares in row 2 may also be left blank i since pieces from the lower half can cover them. Pieces with lead squares i i in row 3 must belong to the lower half, so we do not permit a boundary ( to have such pieces. 21 Finally, we observe that we may leave no more than two adjacent squares empty in row 2. If left empty, such squares must he covered from the lower half, so the only possibilities are squares 3-6. Three or more of these cannot have their lead square adjacent, because there is no legal place for the "side square" of the middle piece. So to generate a boundary, it is necessary to fill the previous sub-board. Then the first row of the next sub-board must be filled. Then each empty square in the second row can be filled by any of pieces 1,2,7,8 that may be legally placed there, or it may be left open. The original macro is used to fill the second row, with the tree for pieces 1,2,7,8 as input. In the second row, we must take care that we do not leave an empty square next to two squares previously left empty . Since the first row is always full, and the fifth row is always empty, a boundary can be represented by a 36-bit vector. We "match" two boundaries together by flipping one of them to form a lower half and then checking for a fit. Note two boundaries need only be checked if the number of ones in both of their bit vectors totals exactly 36 (otherwise some square would be left open or covered twice). Using this scheme, we can generate all 1-boundaries (those boundaries completely covering one board) . We then try to match them together to produce a solution for n = 3 (since matching boundaries of size n gives solutions of size 2n + l). These 1-boundaries are also used to generate 2-boundaries. We then match 1-boundaries with 2-boundaries to check n = k, then 2-boundaries with other 2-boundaries to check for n=5. This works well, however, the number of boundaries does grow rather quickly, causing problems with storage. In fact, there are 226 1-boundaries and 939 2-boundaries. 22 In order to slow the rate of growth, branch merging was used to allow a significant number of boundaries to be ignored. (1) If an m-boundary and an n-boundary (mt H(a a )>t where H(x, y) is the Hamming distance between two codewords x and y. For numbers "close together" (within t of each other), the code preserves the difference between the two numbers. For numbers "far apart" (whose difference is greater than t), the code merely guarantees that the codewords will have more than t bits different. Such codes have uses in pattern recognition and error detection and correction (13) • In particular, we want to find the longest possible DP-1 code of n bits. The normal backtrack procedure is followed: attempts to extend the current code are made by trying to add codewords to the end of the code. Backtracking occurs when no such extensions are possible. It is necessary to try as possible extensions only the n codewords adjacent to (differing in only one bit) the last codeword added, since any other codeword would violate the first DP property. We can drastically reduce the size of the search tree if we use several symmetries to provide branch merging. We define two codes to be isomorphic if one can be obtained from the other by complementation and/or interchange of columns (the i th column consists of the i th bit 27 from each codeword). It is clear that such an operation does not effect the DP property of the code. From this definition, we can show that any DP-t code is isomorphic to a code whose first t+3 codewords are: 000... 00. ..000 000. o.OO... 001 000... 00... Oil 000... 01.. .111, t+2 ones We can force the first codeword to all zeros since we can complement all columns in which the first codeword has a one. For t = 1, the second codeword must consist of all zeros with a single one, and we can inter- change columns so that it is rightmost. Further, we know that the third codeword must have two ones (by comparing it to the first codeword) and that one of these ones must be in the rightmost position (comparing it to the second codeword). Thus we can move the other one into the next to the right column by means of an interchange without changing the first two codewords, and so the symmetry holds for t = 1. The proof is completed by induction. Suppose the symmetry holds for t - 1. Since any DP-t code is also a DP-(t-l) code, the first t+2 codewords can be assumed to fit the rd pattern. Since the code is DP-t, we can compare the t+3 codeword with the first t+2 codewords to conclude that it must have t + 1 ones in its right and one somewhere else. Thus, a single interchange transforms it to the desired form without disturbing the earlier codewords. We can further note that any DP-t code is isomorphic to a code in which the first one to appear in column i (counting from the right) occurs before the first one to appear in column i-1 for 2^i^n. This 28 is trivially true since we can always interchange columns to meet the requirements. Applying these two restraints to the problem of finding optimal DP-1 codes prunes the tree significantly. The first symmetry says we must start with, say, (000000,000001,000011,000111) for n = 6. Figure 5 shows the effect of the second symmetry. The dotted lines show which subtrees are precluded. Since this preclusion occurs near the root, the number of nodes pruned from the tree is significant. It is important to have an effective way of determining which of the n adjacent codewords can be added to the end of a code. To facilitate this, we can use a bit vector to store which codewords may not be added because they are "too close" to codewords earlier in the code. We can update the bit vector (initially zero) by setting bits corresponding to codewords adjacent to the codeword just added. This vector must be saved and then later restored as we backtrack. A similar technique was used in the queen's problem. In both cases, all the necessary information was stored in a bit vector, which was updated as we advanced. It is never necessary to perform a test on the entire partial solution — only the bit vector. Speed is gained because bit operations can process 32 bits in parallel, and costly checks involving long partial solutions are no longer necessary. This bit vector also allows us to apply the technique of branch and bound. Since only codewords corresponding to a zero in the bit vector may be added to the code, we can obtain an upper bound on the length of the code at any given point in the tree. If this is smaller than the longest code so far, we can immediately backtrack. Such a simple technique proved highly effective, resulting in a reduction of nearly ten times in the size of the n = 6 search tree. 29 O O / / / / / / / / / / / / / / 9 o o o o o o o o o o o o 8 8 CD u CD •d •H CO c o o 0) b & p cu -P s Q H C ca" -d 0) t3 0) a c o o CO CU a> CO J3 o CU a ja 05 -p O .c • o >> P •H K o ,c -P r^ £ CD Ch £ H CU o ^ ^ CO cu CO T* a ra C a) O C o •H cu m H CO cu Ei cu ^j CD X! P X -P oO o •H ^ O pq .Q •P CD CU £ i 30 A macro is used to generate a short subroutine for each of the 2 n codewords. For example, if W is a codeword, and W , ...,W are the n adjacent codewords, then the macro would produce the following subroutine for W: add W to the code If the code is longer than the longest so far then record it. If W can be added then call W If W can be added then call W — n n remove W from the code return 1 t II il The subroutine also checks for the several preclusions previously mentioned. The macro approach allows each of the 2 blocks to be very I! efficient. Adding W to the code consists of OR-ing the bit mask of adjacent codewords into the bit vector. This bit mask is determined once, at assembly, and it need not be looked up from a table or regenerated every time this codeword needs to be added. The n subroutine calls are also specialized for each block. The branch addresses need not be determined every time a codeword is added. The program found the optimal 6-bit DP-1 code (of length 27) in 25 seconds. This was reduced to 3 seconds by the introduction of the branch and bound technique. The 7 -bit code is currently beyond its capacity. It would take 1^0 hours to search through the estimated 10 nodes. The branch and bound technique did enable an upper bound of 63 and a lower bound of k-9 to be established. 31 LIST OF REFERENCES (1) Aho, A. V. and J. D. Ullman, The Theory of Parsing, Translation, and Compiling, Volume I: Parsing, Prentice-Hall, Englewood Cliffs, N.J., 1972. (2) Ball, W. W. R., Mathematical Recreations and Essays, revised by H. S. ¥. Coxeter, Macmillan, New York, i960. (3) Bunch, S. R., Personal communication. (k) Chvatal, V., D. A. Klarner, and D. E. Knuth, Selected combinatorial research problems, Technical Report No. STAN -CS -72 -292, Computer Science Department, Stanford University, June 1972. (5) Fletcher, J. G., A program to solve the pentomino problem by the recursive use of macros, CACM 8 (1965), 62I-623. (6) Gardner, M., Mathematical Games, Scientific American, 215 (Sept. 1966), p. 264. (7) Gardner, M. , Mathematical Games, Scientific American, 2l6 (Jan. I967), p. 118. (8) Golomb, S. W. and L. D. Baumbert, Backtrack programming, JACM 12 (1965), 516-524. (9) Kraitchik, M., Mathematical Recreations, W. W. Norton, New York, 194-2. Revised edition, Dover, New York, 1953* (10) Lawler, E. L. and D. E. Wood, Branch and bound methods: a survey, Operations Research Ik (1966), 699-719. (11) Lin, S., Personal communication. (12) Peterson, G., Personal communication. (13) Preparata, F. and J. Nievergelt, Difference-preserving codes, IEEE Transactions on Information Theory, 20 (1974), 643-649. (14) Sainte-Lague, M. A., Les Reseaux (ou Graphes ), Memorial des Sciences Mathematiques, Fasc. 18, Gauthier-Vi liars, Paris, 1926. (15) Slagle, J. R., Artificial Intelligence - The Heuristic Programming Approach, McGraw-Hill, New York, 1971. 32 (16) Stein, S. K., Mathematics : The Man -Made Universe, Freeman, San Francisco, 19&9 • (17) Tutte, W. T., The quest of the perfect square, Amer . Math. Monthly J2j No. 2 Part 2 (1965), 29-35- (18) Walker, R. J., An enumerative technique for a class of combinatorial problems, Combinatorial Analysis ( Proceedings of Symposium on Applied Mathematics, Volume X), American Mathematical Society, Providence, Rhode Island, i960. (19) Willcocks, T. H., A note on some perfect squared squares, Canad . J. Math . 3 (1951), 3C4-308. 3LI0GRAPHIC DATA EET Fitlc and Subtitle 1. Report No. uiuc DC s-R -7^-687 USE OF MACROS IN BACKTRACK PROGRAMMING Author(s) James R. Bitner 3. Recipient's Accession No. 5. Report Date December, 197^ 8. Performing Organization Rept. No. Performing Organization Name and Address Department of Computer Science University of Illinois at Urb ana -Champaign Urbana, Illinois 6l801 10. Project/Task/Work Unit No. Sponsoring Organization Name and Address National Science Foundation Washington, D.C. 11. Contract/Grant No. NSF GJ-41538 13. Type of Report & Period Covered 14. ipplementary Notes \bstracts This thesis discusses the use of macros and other heuristic techniques to increase the speed of backtrack programs. Detailed explanations of several macro programs are given for illustration. These techniques were successful in decreasing execution time by factors of 5 to 10 times over previous non- macro programs. 7. K, \ UorJs and Document Analysis. 17a. Descriptors Backtrack, depth-first-search, exhaustive search, macros, combinatorial computing, non-attacking queen's problem, difference preserving codes, pentominos, tiling problems, squaring the square 7b. | U-nnfiers/Open-Ended Terms 17c. ( <>S A IT Fie Id /Group 18 Availability Statement 19. Security Class (This Report) TI N rj ASSIFIED 20. Security Class ( Thi s Page UNCI. ASSIFIED 21. No. of Pages 36 22. Price USCOMM-DC 40329-P7 1 FOF1M NTIS-35 (10-70) e muM WWHUuuaa ihJi»fcMfcJcSi>lkk\iVi OCT 2,7 1975 .....^HOFUIHOISUH— « . C002 no M ■••W« T « Piogttm minim 3 0112088401598 B8H ■QBH MM MB .»'/*■ BH BB1 HUBB HJ .>»