{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Depth-Limited Search" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Depth-first search will not find a goal if it searches down a path\n", "that has infinite length. So, in general, depth-first search is not\n", "guaranteed to find a solution, so it is not complete. \n", "\n", "This problem is eliminated by limiting the depth of the search to some\n", "value $l$. However, this introduces another way of preventing depth-first\n", "search from finding the goal. If the goal is deeper than $l$ it will\n", "not be found.\n", "\n", "How would you make an intelligent guess for $l$ for a given search\n", "problem?\n", "\n", "Its time complexity is $O(b^l)$ and its space complexity is $O(bl)$.\n", "What would the space complexity be of the backtracking version of\n", "this search?\n", "\n", "Regular depth-first search is a special case, for which $l=\\infty$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Iterative-Deepening Search" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If a depth-limited depth-first search limited to depth $l$ does not find the goal,\n", "try it again with limit at $l+1$. Continue this until goal is found.\n", "\n", "Make depth-limited depth-first search complete by repeatedly applying\n", "it with greater values for the depth limit $l$.\n", "\n", "Feels like breadth-first search, in that a level is fully explored\n", "before extending the search to the next level. But, unlike\n", "breadth-first search, after one level is fully explored, all nodes\n", "already expanded are thrown away and the search starts with a clear\n", "memory. \n", "\n", "Seems very wasteful! Is it really? How many nodes are generated\n", "at the final level $d$?\n", "\n", "$$b^d$$\n", "\n", "How many nodes are expanded in\n", "the tree on your way to the final level, down to depth $d-1$? \n", "\n", "$$b + b^2 + \\cdots + b^{d-1} = O(b^{d-1})$$\n", "\n", "How much of a waste is it to throw away those $O(b^{d-1})$ nodes?\n", "Say $b=10$ and $d=5$. We are throwing away on the order of $10^4 =\n", "1,000$ nodes, regenerating them and then generating $b^d = 10^5 =\n", "10,000$ new nodes. Regenerating those 1,000 nodes seems trivial\n", "compared to making the 10,000 new ones.\n", "\n", "Our textbook authors say:\n", "\n", "> \"In general, iterative deepening is the preferred uninformed search method when the search space is large and the depth of the solution is not known.\"\n", "\n", "Watch this [short video by Richard Korf](http://www.youtube.com/watch?v=EnX8cQPiB1M), one of the developers of iterative deepening." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Recursive definition" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's discuss the recursive implementation in Figures 3.17 (with\n", "Figure 3.18). Rather than the explicit storage of expanded nodes in a\n", "python dictionary named `expanded`, we can rely on the local\n", "variables implicitly stored in the function call\n", "stack. \n", "\n", "First, define the recursive depth-limited search function that\n", "generates the children of a state and calls itself recursively on each\n", "of the child states. Let's define it as a mix of python and English.\n", "Let `take_action_f` be a function that generates one new state given a\n", "current state and a valid action from that state. Also let\n", "`actions_f` be a function that returns a list of valid actions from a\n", "given state. We will see examples of these in the next lecture notes." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def depth_limited_search(state, goal_state, actions_f, take_action_f, depth_limit):\n", " \n", " # If we have reached the goal, exit, returning an empty solution path.\n", " If state == goal_state, then\n", " return []\n", " \n", " # If we have reached the depth limit, return the string 'cutoff'.\n", " If depth_limit is 0, then\n", " Return the string 'cutoff' to signal that the depth limit was reached\n", " \n", " cutoff_occurred = False\n", " \n", " # For each possible action from state ...\n", " For each action in actions_f(state):\n", " \n", " # Apply the action to the current state to get a next state, named child_state\n", " child_state = take_action_f(state, action)\n", " \n", " # Recursively call this function to continue the search starting from the child_state.\n", " # Decrease by one the depth_limit for this search.\n", " result = depth_limited_search(child_state, goal_state, actions_f, take_action_f, depth_limit - 1)\n", " \n", " # If result was 'cufoff', just note that this happened.\n", " If result is 'cutoff', then\n", " cutoff_occurred = True\n", " \n", " # If result was not 'failure', search succeeded so add childState to front of solution path and\n", " # return that path.\n", " else if result is not 'failure' then\n", " Add child_state to front of partial solution path, in result, returned by depth_limited_search\n", " return result\n", " \n", " # We reach here only if cutoff or failure occurred. Return whichever occurred.\n", " If cutoff_occurred, then\n", " return 'cutoff'\n", " else\n", " return 'failure'" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def iterative_deepening_search(start_state, goal_state, actions_f, take_action_f, max_depth):\n", " \n", " # Conduct multiple searches, starting with smallest depth, then increasing it by 1 each time.\n", " for depth in range(max_depth):\n", " \n", " # Conduct search from startState\n", " result = depth_limited_search(start_state, goal_state, actions_f, take_action_f, depth)\n", " \n", " # If result was failure, return 'failure'.\n", " if result is 'failure':\n", " return 'failure'\n", " \n", " # Otherwise, if result was not cutoff, it succeeded, so add start_state to solution path and return it.\n", " if result is not 'cutoff', then\n", " Add start_state to front of solution path, in result, returned by depth_limited_search \n", " return result\n", " \n", " # If we reach here, no solution found within the max_depth limit.\n", " return 'cutoff'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Bidirectional Search" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If, and this is a big if, every action in a search problem has a known\n", "inverse action allowing search to go backwards, then an $O(b^d)$\n", "search can be reduced to two $O(b^{d/2})$ searches by iteratively, or\n", "simultaneously in parallel, searching forward from the start state and\n", "searching backwards from the goal state. This also assumes there is\n", "one goal state, or a finite number of goal states." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Uninformed Search Summary" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This table is from page 91 of our texbook. Here $b$ is the branching factor, $d$ is the depth of the shallowest solution, $m$ is the maximum depth of the search tree, and $l$ is the depth limit." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "| Criterion | Breadth-First | Depth-First | Depth-Limited | Iterative-Deepening | Bidirectional \n", "| :-: | :-: | :-: | :-: | :-: | :-:\n", "| Complete? | Yes | No | No | Yes | Yes |\n", "| Optimal? | Yes | No | No | Yes | Yes |\n", "| Time | $O(b^d)$ | $O(b^m)$ | $O(b^l)$ | $O(b^d)$ | $O(b^{d/2})$ |\n", "| Space | $O(b^d)$ | $O(bm)$ | $O(bl)$ | $O(bd)$ | $O(b^{d/2})$ |\n" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.5" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 1 }