diff --git a/README.md b/README.md index f66a5cb8d..dd8c0b38a 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and | 3.14 | Uniform-Cost-Search | `uniform_cost_search` | [`search.py`][search] | Done | Included | | 3.17 | Depth-Limited-Search | `depth_limited_search` | [`search.py`][search] | Done | | | 3.18 | Iterative-Deepening-Search | `iterative_deepening_search` | [`search.py`][search] | Done | | -| 3.22 | Best-First-Search | `best_first_graph_search` | [`search.py`][search] | Done | | +| 3.22 | Best-First-Search | `best_first_graph_search` | [`search.py`][search] | Done | Included | | 3.24 | A\*-Search | `astar_search` | [`search.py`][search] | Done | Included | | 3.26 | Recursive-Best-First-Search | `recursive_best_first_search` | [`search.py`][search] | Done | | | 4.2 | Hill-Climbing | `hill_climbing` | [`search.py`][search] | Done | | diff --git a/search.ipynb b/search.ipynb index ac621b622..5415dd89a 100644 --- a/search.ipynb +++ b/search.ipynb @@ -39,9 +39,10 @@ "* Search Algorithms Visualization\n", "* Breadth-First Tree Search\n", "* Breadth-First Search\n", + "* Best First Search\n", "* Uniform Cost Search\n", + "* Greedy Best First Search\n", "* A\\* Search\n", - "* Best First Search\n", "* Genetic Algorithm" ] }, @@ -948,21 +949,18 @@ "display_visual(user_input = False, algorithm = depth_first_graph_search, problem = romania_problem)" ] }, - { + { "cell_type": "markdown", "metadata": {}, "source": [ - "## UNIFORM COST SEARCH\n", - "\n", + "## BEST FIRST SEARCH\n", "Let's change all the node_colors to starting position and define a different problem statement." ] }, { "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, + "execution_count": 21, + "metadata": {}, "outputs": [], "source": [ "def best_first_graph_search(problem, f):\n", @@ -1032,10 +1030,27 @@ " node_colors[node.state] = \"gray\"\n", " iterations += 1\n", " all_node_colors.append(dict(node_colors))\n", - " return None\n", + " return None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UNIFORM COST SEARCH\n", "\n", + "Let's change all the node_colors to starting position and define a different problem statement." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ "def uniform_cost_search(problem):\n", " \"[Figure 3.14]\"\n", + " #Uniform Cost Search uses Best First Search algorithm with f(n) = g(n)\n", " iterations, all_node_colors, node = best_first_graph_search(problem, lambda node: node.path_cost)\n", " return(iterations, all_node_colors, node)" ] @@ -1048,7 +1063,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "a667c668001e4e598478ba4a870c6aec" + "model_id": "46b8200b4a8f47e7b18145234a8469da" } }, "metadata": {}, @@ -1057,8 +1072,8 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "135c6bd739de4aab8fc7b2fcb6b90954" - } + "model_id": "ca9b2d01bbd5458bb037585c719d73fc" + } }, "metadata": {}, "output_type": "display_data" @@ -1074,106 +1089,34 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## A\\* SEARCH\n", - "\n", + "## GREEDY BEST FIRST SEARCH\n", "Let's change all the node_colors to starting position and define a different problem statement." ] }, { "cell_type": "code", "execution_count": 24, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ - "def best_first_graph_search(problem, f):\n", - " \"\"\"Search the nodes with the lowest f scores first.\n", - " You specify the function f(node) that you want to minimize; for example,\n", - " if f is a heuristic estimate to the goal, then we have greedy best\n", - " first search; if f is node.depth then we have breadth-first search.\n", - " There is a subtlety: the line \"f = memoize(f, 'f')\" means that the f\n", - " values will be cached on the nodes as they are computed. So after doing\n", - " a best first search you can examine the f values of the path returned.\"\"\"\n", - " \n", - " # we use these two variables at the time of visualisations\n", - " iterations = 0\n", - " all_node_colors = []\n", - " node_colors = dict(initial_node_colors)\n", - " \n", - " f = memoize(f, 'f')\n", - " node = Node(problem.initial)\n", - " \n", - " node_colors[node.state] = \"red\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " \n", - " if problem.goal_test(node.state):\n", - " node_colors[node.state] = \"green\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " return(iterations, all_node_colors, node)\n", - " \n", - " frontier = PriorityQueue(min, f)\n", - " frontier.append(node)\n", - " \n", - " node_colors[node.state] = \"orange\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " \n", - " explored = set()\n", - " while frontier:\n", - " node = frontier.pop()\n", - " \n", - " node_colors[node.state] = \"red\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " \n", - " if problem.goal_test(node.state):\n", - " node_colors[node.state] = \"green\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " return(iterations, all_node_colors, node)\n", - " \n", - " explored.add(node.state)\n", - " for child in node.expand(problem):\n", - " if child.state not in explored and child not in frontier:\n", - " frontier.append(child)\n", - " node_colors[child.state] = \"orange\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " elif child in frontier:\n", - " incumbent = frontier[child]\n", - " if f(child) < f(incumbent):\n", - " del frontier[incumbent]\n", - " frontier.append(child)\n", - " node_colors[child.state] = \"orange\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - "\n", - " node_colors[node.state] = \"gray\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " return None\n", - "\n", - "def astar_search(problem, h=None):\n", - " \"\"\"A* search is best-first graph search with f(n) = g(n)+h(n).\n", - " You need to specify the h function when you call astar_search, or\n", + "def greedy_best_first_search(problem, h=None):\n", + " \"\"\"Greedy Best-first graph search is an informative searching algorithm with f(n) = h(n).\n", + " You need to specify the h function when you call best_first_search, or\n", " else in your Problem subclass.\"\"\"\n", " h = memoize(h or problem.h, 'h')\n", - " iterations, all_node_colors, node = best_first_graph_search(problem, lambda n: n.path_cost + h(n))\n", + " iterations, all_node_colors, node = best_first_graph_search(problem, lambda n: h(n))\n", " return(iterations, all_node_colors, node)" ] }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "3e62c492a82044e4813ad5d84e698874" + "model_id": "e3ddd0260d7d4a8aa62d610976b9568a" } }, "metadata": {}, @@ -1182,7 +1125,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "b661fd0c0c8d495db2672aedc25b9a44" + "model_id": "dae485b1f4224c34a88de42d252da76c" } }, "metadata": {}, @@ -1191,21 +1134,43 @@ ], "source": [ "all_node_colors = []\n", - "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n", - "display_visual(user_input = False, algorithm = astar_search, problem = romania_problem)" + "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n", + "display_visual(user_input = False, algorithm = greedy_best_first_search, problem = romania_problem)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A\\* SEARCH\n", + "\n", + "Let's change all the node_colors to starting position and define a different problem statement." ] }, { "cell_type": "code", - "execution_count": 44, - "metadata": { - "scrolled": false - }, + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "def astar_search(problem, h=None):\n", + " \"\"\"A* search is best-first graph search with f(n) = g(n)+h(n).\n", + " You need to specify the h function when you call astar_search, or\n", + " else in your Problem subclass.\"\"\"\n", + " h = memoize(h or problem.h, 'h')\n", + " iterations, all_node_colors, node = best_first_graph_search(problem, lambda n: n.path_cost + h(n))\n", + " return(iterations, all_node_colors, node)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "7f1ffa858c92429bb28f74c23c0c939c" + "model_id": "15a78d815f0c4ea589cdd5ad40bc8794" } }, "metadata": {}, @@ -1214,16 +1179,30 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "7a98e98ffec14520b93ce542f5169bcc" + "model_id": "10450687dd574be2a380e9e40403fa83" } }, "metadata": {}, "output_type": "display_data" - }, + } + ], + "source": [ + "all_node_colors = []\n", + "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n", + "display_visual(user_input = False, algorithm = astar_search, problem = romania_problem)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "scrolled": false + }, + "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "094beb8cf34c4a5b87f8368539d24091" + "model_id": "9019790cf8324d73966373bb3f5373a8" } }, "metadata": {}, @@ -1232,7 +1211,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "a8f89c87de964ee69004902763e68a54" + "model_id": "b8a3195598da472d996e4e8b81595cb7" } }, "metadata": {}, @@ -1241,120 +1220,16 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "2ccdb4aba3ee4371a78306755e5642ad" + "model_id": "aabe167a0d6440f0a020df8a85a9206c" } }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "all_node_colors = []\n", - "# display_visual(user_input = True, algorithm = breadth_first_tree_search)\n", - "display_visual(user_input = True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## BEST FIRST SEARCH\n", - "Let's change all the node_colors to starting position and define a different problem statement." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [], - "source": [ - "def best_first_graph_search(problem, f):\n", - " \"\"\"Search the nodes with the lowest f scores first.\n", - " You specify the function f(node) that you want to minimize; for example,\n", - " if f is a heuristic estimate to the goal, then we have greedy best\n", - " first search; if f is node.depth then we have breadth-first search.\n", - " There is a subtlety: the line \"f = memoize(f, 'f')\" means that the f\n", - " values will be cached on the nodes as they are computed. So after doing\n", - " a best first search you can examine the f values of the path returned.\"\"\"\n", - " \n", - " # we use these two variables at the time of visualisations\n", - " iterations = 0\n", - " all_node_colors = []\n", - " node_colors = dict(initial_node_colors)\n", - " \n", - " f = memoize(f, 'f')\n", - " node = Node(problem.initial)\n", - " \n", - " node_colors[node.state] = \"red\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " \n", - " if problem.goal_test(node.state):\n", - " node_colors[node.state] = \"green\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " return(iterations, all_node_colors, node)\n", - " \n", - " frontier = PriorityQueue(min, f)\n", - " frontier.append(node)\n", - " \n", - " node_colors[node.state] = \"orange\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " \n", - " explored = set()\n", - " while frontier:\n", - " node = frontier.pop()\n", - " \n", - " node_colors[node.state] = \"red\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " \n", - " if problem.goal_test(node.state):\n", - " node_colors[node.state] = \"green\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " return(iterations, all_node_colors, node)\n", - " \n", - " explored.add(node.state)\n", - " for child in node.expand(problem):\n", - " if child.state not in explored and child not in frontier:\n", - " frontier.append(child)\n", - " node_colors[child.state] = \"orange\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " elif child in frontier:\n", - " incumbent = frontier[child]\n", - " if f(child) < f(incumbent):\n", - " del frontier[incumbent]\n", - " frontier.append(child)\n", - " node_colors[child.state] = \"orange\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - "\n", - " node_colors[node.state] = \"gray\"\n", - " iterations += 1\n", - " all_node_colors.append(dict(node_colors))\n", - " return None\n", - "\n", - "def best_first_search(problem, h=None):\n", - " \"\"\"Best-first graph search is an informative searching algorithm with f(n) = h(n).\n", - " You need to specify the h function when you call best_first_search, or\n", - " else in your Problem subclass.\"\"\"\n", - " h = memoize(h or problem.h, 'h')\n", - " iterations, all_node_colors, node = best_first_graph_search(problem, lambda n: h(n))\n", - " return(iterations, all_node_colors, node)" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ + }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "5ae2d521b74743afa988c462a851c269" + "model_id": "25d146d187004f4f9db6a7dccdbc7e93" } }, "metadata": {}, @@ -1363,7 +1238,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "559c20b044a4469db7f0ab8c3fae1022" + "model_id": "68d532810a9e46309415fd353c474a4d" } }, "metadata": {}, @@ -1372,8 +1247,8 @@ ], "source": [ "all_node_colors = []\n", - "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n", - "display_visual(user_input = False, algorithm = best_first_search, problem = romania_problem)" + "# display_visual(user_input = True, algorithm = breadth_first_tree_search)\n", + "display_visual(user_input = True)" ] }, {