HW #1: Brute Force Search Algorithms Sample Solution Prof. Nathan Sturtevant
Transcription
HW #1: Brute Force Search Algorithms Sample Solution Prof. Nathan Sturtevant
HW #1: Brute Force Search Algorithms Sample Solution Prof. Nathan Sturtevant University of Denver Denver, Colorado [email protected] Abstract This document describes the results for the first homework assignment in COMP-4704-1, Fall 2010. In particular, we perform an analysis of three brute-force search algorithms, BFS, DFS and DFID. We compare the performance of these algorithms on a grid-based domain, a n-ary tree, and on the sliding-tile puzzle. Our results confirm the predicted performance from lecture. Introduction In this homework we study the performance of brute-force algorithms. We study breadth-first search (BFS), depth-first search (DFS) and depth-first iterative deepening (DFID). We analyze these algorithms on three domains, which we describe in more detail in the next section. Our results confirm the analysis of the algorithms seen in class and give insight into which algorithms work best on different types of domains. Background This section provides background information about the algorithms and domains analyzed. Algorithm 1 BFS(sstart ) 1: vector sstart 2: while vector is not empty do 3: next pop front from vector 4: for each successor si of next do 5: if si is not in CLOSED then 6: Add si to closed 7: Put si on the back of vector 8: end if 9: end for 10: end while Algorithm 2 DFS(sstart ) 1: vector sstart 2: while vector is not empty do 3: next pop front from vector 4: for each successor si of next do 5: Put si on the front of vector 6: end for 7: end while Algorithms The first algorithm analyzed is BFS. A BFS expands nodes in order of distance (counted by edges, not edge cost) from the start state. BFS pseudo-code can be found in Algorithm 1. BFS has a simple queue for sorting nodes waiting to be expanded and has a closed list to perform duplicate detection. As a result, in an exponential domain with branching factor b and depth d, BFS takes O(bd ) time and space. The next algorithm analyzed is DFS, which expands states in order of depth. As a result, DFS is not guaranteed to be complete in an domain with cycles and may may run out of stack space in a large domain. Pseudo-code for DFS can be found in Algorithm 2. Note that the only difference between DFS and BFS comes in line 5 where the successors are put on the front of the vector instead of the back. (Compare with BFS in Algorithm 1 line 7.) If it finds a solution at depth d, DFS will only use O(d) space. As the algorithm is not guaranteed to be complete, the running time depends on the properties of the state space. Copyright c 2010, Nathan Sturtevant. All rights reserved. The final algorithm we analyze is DFID. DFID performs multiple depth-first searches, but bounds each search by a given depth. For a brute-force search algorithm, DFID is both time and space optimal, taking O(bd ) time and O(d) space. Pseudo-code for DFID is in Algorithm 3. We show the depth-limited DFS using a recursive function, while DFS and BFS used a queue. Domains We use three domains for analysis, a grid-based domain, a nary tree, and on the sliding-tile puzzle. These are illustrated in Figures 1. The first domain is a directed graph aligned to a grid with length and width k. The start location is in the lower-left hand corner. From each state you can either travel right, up, or diagonally right and up. This domain grows polynomially in k; there are k 2 total states. This domain has many short cycles. The second domain is an n-ary tree. The example shown Algorithm 3 DFID(sstart , d) 1: for i = 1 . . . d do 2: LIMITED DFS(sstart , i, 0) 3: end for LIMITED DFS(s, d, dcurr ) 1: if dcurr d then 2: return 3: end if 4: for each successor si of s do 5: LIMITED DFS(si , d, dcurr + 1) 6: end for is a binary tree, but any branching factor and depth can be specified for the tree. This domain grows exponentially in the depth; there are O(nd ) states for an n-ary tree with depth d. An n-ary tree has no cycles in it. The third and final domain is the sliding tile puzzle. In the sliding-tile puzzle the blank (labelled as 0) can swap with any adjacent tile. For a sliding-tile puzzle with t tiles, there are t!/2 possible states. The sliding tile puzzle grows exponentially, and also contains cycles. size 1 2 3 4 5 6 7 8 9 10 11 12 BFS 1 4 9 16 25 36 49 64 81 100 121 144 DFS 1 6 31 160 841 4,494 24,319 132,864 731,281 4,048,726 22,523,359 125,797,984 DFID 1 14 103 623 3,632 21,074 122,475 713,471 4,164,832 24351,542 142,562,799 835,435,855 Figure 2: Nodes expanded in the grid by BFS, DFS and DFID with varying grid size. Algorithm n-ary(2, 10) n-ary(3, 10) n-ary(3, 10) BFS 2047 88573 1398101 DFS 2047 88573 1398101 DFID 4082 132852 1864129 ( b b 1 ) 2 bd 4096 132860 1864135 Figure 3: Nodes expanded in the n-ary tree by various algorithms with varying branching factor. In this section we report the results of running each algorithm on each domain. Additionally, for BFS we run with and without duplicate detection. We have chosen to present results by domain, as this best illustrates the differences between the algorithms. The n-ary tree, with no cycles, is well suited for algorithms that do not perform duplicate detection. If we are simply trying to traverse the whole domain, DFS is the best approach, as it will expand each node exactly once. If we do not know the depth of the solution, DFID will likely be the best algorithm, as BFS will run out of memory (due to both the open and closed list). Grid-based movement Sliding Tile Puzzle Experimental Results In Figure 2 we show the total number of nodes expanded by BFS, DFS and DFID as we increase the size of the grid from 1 to 12. The results with BFS are with duplicate detection. Without duplicate detection BFS and DFS expand the same number of nodes. We note that BFS expands exactly k 2 nodes for a given grid size. DFS and DFID, however, turn a polynomial domain into an exponential domain. For the case of k = 12, the maximum path length in the grid is 24. Solving for b24 = 125, 797, 984 give an estimate of b as 2.18. For this domain, the presence of many small cycles means that duplicate detection is essential to efficiently explore the search space. N-ary tree We present results for each algorithm with an n-ary tree in Figure 3. Because there are no cycles in the tree, BFS and DFS expand exactly the same number of nodes, regardless of whether BFS performs duplicate detection. DFID, because it performs multiple iterations, expands more nodes that both of the other algorithms. For these parameters, the predicted number of nodes expanded by BFS and DFS matches exactly. The last column of the table is the theoretical number of nodes expanded by DFID (( b b 1 )2 bd ). Although the actual number of nodes does not match exactly, the formula closely predicts the number of nodes expanded by DFID. In the sliding tile puzzle we were unable to run DFS. Even when we checked for duplicates on the path explored so far, our recursive implementation ran out of stack space. This is because a DFS will recursively explore almost all states in the state space. DFID, however, is more successful. We presented detailed sliding-tile puzzle results here. First, we look at the number of nodes expanded by BFS on the 3x3 and 3x4 sliding tile puzzles in Figures 4 and 5. In the left portion of Figure 4 we show the number of states at each level of the sliding-tile puzzle when we perform duplicate detection. BFS is easily able to explore the entire state space. Without duplicate detection we aborted the search after 15ply, as it was obvious that BFS was going to quickly run out of memory. Note that without duplicate detection, at even depths the blank will always be on the side of the puzzle, producing a branching factor of 3.00. At odd depths, the blank will either be in the middle with a branching factor of 4.0, or in the corner with a branching factor of 2.0. This lets us compute that the blank will be in the middle 1/3 of the time and in the corner 2/3 of the time, even though the ratio of corner to middle states is higher. Figure 5 has results for the 3x4 sliding tile puzzle. A memory efficient implementation should be able to finish a breadth-first search in this domain (with only 239 million states), however we ran out of memory at approximately 0 1 2 3 4 5 6 7 8 n-ary tree (for n=2) sliding-tile puzzle Grid-based domain Figure 1: Illustration of domains. depth 31 after expanding 35 million states. Without duplicate detection we again aborted the search at depth 15. These results are just for BFS. We now compare BFS and DFID in the 3x3 and 3x4 sliding tile puzzle in Figure 6. With these results we can see that DFID expands far more nodes than a BFS because it does not perform duplicate detection. However, it is able to explore extremely large number of nodes without running out of memory. After thirty minutes it was able to finish an iteration of depth 31 with 4 billion nodes. It took about an hour to complete the depth 32 iteration.. In this case we were not searching for a particular goal state, so we just allowed the search to continue until 30 minutes ran out. If we were looking for a particular state at large depths, DFID would be able to solve larger problems than BFS. Conclusions In this homework we have analyzed three brute-force search algorithms. We have seen that in polynomial domains such as the grid, BFS is clearly the best algorithm to use, as its duplicate detection keeps the number of explored states from growing exponentially. DFS works better in exponential domains, but will have trouble finding optimal solutions unless we know the solution depth already. Additionally, DFS can run out of stack space when exploring large connected state spaces. In these instances DFID is the best algorithm to use, as it will not run out of memory and is guaranteed to find optimal solutions. Appendix Sample code for BFS is found in Figure 7. Other algorithms are implemented in a similar manner. Depth 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 BFS (3x3) 1 3 7 15 31 51 90 152 268 420 706 1102 1850 2874 4767 7279 11764 17402 26931 37809 54802 71912 95864 116088 140135 155713 170273 176547 180457 181217 181438 181440 - DFID (3x3) 1 4 11 26 57 108 199 358 653 1136 1995 3450 6097 10468 18287 31406 55125 94488 165139 283234 496217 850508 1487415 2550294 4465117 7653760 13390043 22955978 40181217 68879028 120521983 206615422 - BFS (3x4) 1 3 7 16 36 73 136 258 490 921 1702 3094 5588 10030 17884 31783 55998 97800 168967 288855 487218 810424 1326202 2137202 3385213 5270492 8052888 12062610 17683964 25331836 35397636 (memory) - DFID (3x4) 1 4 11 27 63 136 273 538 1058 2069 4017 7767 15024 29041 56058 108218 208997 403533 778735 1502836 2901277 5601010 10810394 20863933 40272489 77739799 150053400 289621017 559024410 1079056186 2082809349 4020190533 7759743423 Figure 6: Nodes expanded at each level in the 3x3 and 3x4 sliding-tile puzzle by BFS and DFID. Depth 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Nodes (Sum) 1 3 7 15 31 51 90 152 268 420 706 1102 1850 2874 4767 7279 11764 17402 26931 37809 54802 71912 95864 116088 140135 155713 170273 176547 180457 181217 181438 181440 Nodes (Level) 1 2 4 8 16 20 39 62 116 152 286 396 748 1024 1893 2512 4485 5638 9529 10878 16993 17110 23952 20224 24047 15578 14560 6274 3910 760 221 2 b (ratio of levels) inf 2.00 2.00 2.00 2.00 1.25 1.95 1.59 1.87 1.31 1.88 1.38 1.89 1.37 1.85 1.33 1.79 1.26 1.69 1.14 1.56 1.01 1.40 0.84 1.19 0.65 0.93 0.43 0.62 0.19 0.29 0.01 Nodes (Sum) 1 3 9 25 73 201 585 1609 4681 12873 37449 102985 299593 823881 2396745 6591049 Nodes (Level) 1 2 6 16 48 128 384 1024 3072 8192 24576 65536 196608 524288 1572864 4194304 b inf 2.00 3.00 2.67 3.00 2.67 3.00 2.67 3.00 2.67 3.00 2.67 3.00 2.67 3.00 2.67 Figure 4: Nodes expanded at each level in the 3x3 sliding-tile puzzle with BFS and duplicate detection (left) and without duplicate detection (right). Depth 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Nodes (Sum) 1 3 7 16 36 73 136 258 490 921 1702 3094 5588 10030 17884 31783 55998 97800 168967 288855 487218 810424 1326202 2137202 3385213 5270492 8052888 12062610 17683964 25331836 35397636 (memory) Nodes (Level) 1 2 4 9 20 37 63 122 232 431 781 1392 2494 4442 7854 13899 24215 41802 71167 119888 198363 323206 515778 811000 1248011 1885279 2782396 4009722 5621354 7647872 10065800 (memory) b (ratio of levels) inf 2.00 2.00 2.25 2.22 1.85 1.70 1.94 1.90 1.86 1.81 1.78 1.79 1.78 1.77 1.77 1.74 1.73 1.70 1.68 1.65 1.63 1.60 1.57 1.54 1.51 1.48 1.44 1.40 1.36 1.32 - Nodes (Sum) 1 3 9 26 79 236 719 2169 6595 19956 60591 183560 556933 1688075 5120045 15522426 Nodes (Level) 1 2 6 17 53 157 483 1450 4426 13361 40635 122969 373373 1131142 3431970 10402381 b inf 2.00 3.00 2.83 3.12 2.96 3.08 3.00 3.05 3.02 3.04 3.03 3.04 3.03 3.03 3.03 Figure 5: Nodes expanded at each level in the 3x4 sliding-tile puzzle with BFS and duplicate detection (left) and without duplicate detection (right).