from search_2 import *
+import math
+
+# My implementation of RBFS
+def recursive_best_first_search(problem, h):
+ return RBFS(problem, Node(problem.initial), math.inf, h)[0]
+
+def RBFS(problem, node, f_limit, h):
+ if problem.is_goal(node.state):
+ return node, 0
+ successors = []
+ for child in expand(problem, node):
+ successors.append(child)
+ if not successors:
+ return failure, math.inf
+ for s in successors:
+ s.f = max(g(s) + h(s), g(node) + h(node))
+ while True:
+ successors.sort(key = lambda x: x.f)
+ best = successors[0]
+ if best.f > f_limit:
+ return failure, best.f
+ if len(successors) > 1:
+ alternative = successors[1].f
+ else:
+ alternative = math.inf
+ result, best.f = RBFS(problem, best, min(f_limit, alternative), h)
+ if result is not failure:
+ return result, best.f
+
+def astar_misplaced_tiles(problem): return astar_search(problem, h=problem.h1)
+def recursive_best_first_misplaced_tiles(problem): return recursive_best_first_search(problem, h=problem.h1)
+def astar_manhatten(problem): return astar_search(problem, h=problem.h2)
+def astar_manhatten_with_weight(problem): return astar_search(problem, h=problem.h3)
+def recursive_best_first_manhatten(problem): return recursive_best_first_search(problem, h=problem.h2)
+def recursive_best_first_manhatten_with_weight(problem): return recursive_best_first_search(problem, h=problem.h3)
+
+
+e1 = EightPuzzle((1, 4, 2, 0, 7, 5, 3, 6, 8))
+e2 = EightPuzzle((1, 2, 3, 4, 5, 6, 7, 8, 0))
+e3 = EightPuzzle((4, 0, 2, 5, 1, 3, 7, 8, 6))
+e4 = EightPuzzle((7, 2, 4, 5, 0, 6, 8, 3, 1))
+e5 = EightPuzzle((8, 6, 7, 2, 5, 4, 3, 0, 1))
+
+report([breadth_first_search,
+ uniform_cost_search,
+ recursive_best_first_manhatten,
+ recursive_best_first_manhatten_with_weight,
+ astar_manhatten,
+ astar_manhatten_with_weight], [e1, e2, e3, e4, e5])
+
breadth_first_search: + 81 nodes | 82 goal | 5 cost | 35 actions | EightPuzzle((1, 4, 2, 0, 7, 5, 3, 6, 8), + 160,948 nodes | 160,949 goal | 22 cost | 59,960 actions | EightPuzzle((1, 2, 3, 4, 5, 6, 7, 8, 0), + 218,263 nodes | 218,264 goal | 23 cost | 81,829 actions | EightPuzzle((4, 0, 2, 5, 1, 3, 7, 8, 6), + 418,771 nodes | 418,772 goal | 26 cost | 156,533 actions | EightPuzzle((7, 2, 4, 5, 0, 6, 8, 3, 1), + 448,667 nodes | 448,668 goal | 27 cost | 167,799 actions | EightPuzzle((8, 6, 7, 2, 5, 4, 3, 0, 1), +1,246,730 nodes |1,246,735 goal | 103 cost | 466,156 actions | TOTAL + +uniform_cost_search: + 124 nodes | 46 goal | 5 cost | 50 actions | EightPuzzle((1, 4, 2, 0, 7, 5, 3, 6, 8), + 214,952 nodes | 79,187 goal | 22 cost | 79,208 actions | EightPuzzle((1, 2, 3, 4, 5, 6, 7, 8, 0), + 300,925 nodes | 112,082 goal | 23 cost | 112,104 actions | EightPuzzle((4, 0, 2, 5, 1, 3, 7, 8, 6), + 457,766 nodes | 171,571 goal | 26 cost | 171,596 actions | EightPuzzle((7, 2, 4, 5, 0, 6, 8, 3, 1), + 466,441 nodes | 174,474 goal | 27 cost | 174,500 actions | EightPuzzle((8, 6, 7, 2, 5, 4, 3, 0, 1), +1,440,208 nodes | 537,360 goal | 103 cost | 537,458 actions | TOTAL + +recursive_best_first_manhatten: + 15 nodes | 6 goal | 5 cost | 10 actions | EightPuzzle((1, 4, 2, 0, 7, 5, 3, 6, 8), + 271,746 nodes | 99,853 goal | 22 cost | 99,874 actions | EightPuzzle((1, 2, 3, 4, 5, 6, 7, 8, 0), +2,807,027 nodes |1,042,820 goal | 23 cost |1,042,842 actions | EightPuzzle((4, 0, 2, 5, 1, 3, 7, 8, 6), + 746,166 nodes | 284,844 goal | 26 cost | 284,869 actions | EightPuzzle((7, 2, 4, 5, 0, 6, 8, 3, 1), + 326,744 nodes | 126,504 goal | 27 cost | 126,530 actions | EightPuzzle((8, 6, 7, 2, 5, 4, 3, 0, 1), +4,151,698 nodes |1,554,027 goal | 103 cost |1,554,125 actions | TOTAL + +recursive_best_first_manhatten_with_weight: + 15 nodes | 6 goal | 5 cost | 10 actions | EightPuzzle((1, 4, 2, 0, 7, 5, 3, 6, 8), +2,409,806 nodes | 901,511 goal | 24 cost | 901,534 actions | EightPuzzle((1, 2, 3, 4, 5, 6, 7, 8, 0), +20,658,284 nodes |7,566,616 goal | 25 cost |7,566,640 actions | EightPuzzle((4, 0, 2, 5, 1, 3, 7, 8, 6), + 588,906 nodes | 211,503 goal | 30 cost | 211,532 actions | EightPuzzle((7, 2, 4, 5, 0, 6, 8, 3, 1), + 597,734 nodes | 233,773 goal | 33 cost | 233,805 actions | EightPuzzle((8, 6, 7, 2, 5, 4, 3, 0, 1), +24,254,745 nodes |8,913,409 goal | 117 cost |8,913,521 actions | TOTAL + +astar_manhatten: + 15 nodes | 6 goal | 5 cost | 10 actions | EightPuzzle((1, 4, 2, 0, 7, 5, 3, 6, 8), + 3,614 nodes | 1,349 goal | 22 cost | 1,370 actions | EightPuzzle((1, 2, 3, 4, 5, 6, 7, 8, 0), + 5,373 nodes | 2,010 goal | 23 cost | 2,032 actions | EightPuzzle((4, 0, 2, 5, 1, 3, 7, 8, 6), + 10,832 nodes | 4,086 goal | 26 cost | 4,111 actions | EightPuzzle((7, 2, 4, 5, 0, 6, 8, 3, 1), + 11,669 nodes | 4,417 goal | 27 cost | 4,443 actions | EightPuzzle((8, 6, 7, 2, 5, 4, 3, 0, 1), + 31,503 nodes | 11,868 goal | 103 cost | 11,966 actions | TOTAL + +astar_manhatten_with_weight: + 15 nodes | 6 goal | 5 cost | 10 actions | EightPuzzle((1, 4, 2, 0, 7, 5, 3, 6, 8), + 1,720 nodes | 633 goal | 24 cost | 656 actions | EightPuzzle((1, 2, 3, 4, 5, 6, 7, 8, 0), + 1,908 nodes | 709 goal | 25 cost | 733 actions | EightPuzzle((4, 0, 2, 5, 1, 3, 7, 8, 6), + 1,312 nodes | 489 goal | 30 cost | 518 actions | EightPuzzle((7, 2, 4, 5, 0, 6, 8, 3, 1), + 2,519 nodes | 935 goal | 29 cost | 963 actions | EightPuzzle((8, 6, 7, 2, 5, 4, 3, 0, 1), + 7,474 nodes | 2,772 goal | 113 cost | 2,880 actions | TOTAL + ++
We can see that A* expands significantly less nodes than RBFS. This is because A* maintains a queue which prevents it from needlessly re-expanding nodes. AIMA asks us to consider what will happen if we add a random constant to the heuristic in RBFS. To get a larger view of what happens to both A* and RBFS when we weight the heuristic I will apply a weighting to both. From the above results we can see that the situation with RBFS is not clear cut. Sometimes we exapnd significantly more nodes and sometimes we expand significantly less. With A* we get the usual success story.
+However, we should look at the time taken for each of the algorithms.
+import time
+
+t0 = time.time()
+recursive_best_first_manhatten(e4)
+t1 = time.time()
+print("Time taken to execute RBFS: ", t1 - t0)
+print("Execution time per node: ", (t1 - t0) / 746,166)
+
+t0 = time.time()
+recursive_best_first_manhatten_with_weight(e4)
+t1 = time.time()
+print("Time taken to execute weighted-RBFS: ", t1 - t0)
+print("Execution time per node: ", (t1 - t0) / 588,906)
+
+t0 = time.time()
+astar_manhatten(e4)
+t1 = time.time()
+print("Time taken to execute A*: ", t1 - t0)
+print("Execution time per node: ", (t1 - t0) / 10,832)
+
+t0 = time.time()
+astar_manhatten_with_weight(e4)
+t1 = time.time()
+print("Time taken to execute weighted-A*: ", t1 - t0)
+print("Execution time per node: ", (t1 - t0) / 1,312)
+
Time taken to execute RBFS: 7.069175720214844 +Execution time per node: 0.009476106863558771 166 +Time taken to execute weighted-RBFS: 5.464362859725952 +Execution time per node: 0.009293134115180192 906 +Time taken to execute A*: 0.08570456504821777 +Execution time per node: 0.008570456504821777 832 +Time taken to execute weighted-A*: 0.008708953857421875 +Execution time per node: 0.008708953857421875 312 ++
While RBFS takes significantly longer, the cost per node is about the same. This is because RBFS does not maintain a queue.
+