Heuristic Search Blai Bonet Depth-first search and iterated depth-first search Universidad Sim´ on Bol´ıvar, Caracas, Venezuela c 2015 Blai Bonet Goals for the lecture Dealing with time and space Time: just wait: go for a coffee, go for lunch, go on a trip, . . . • How to deal with time and space requirements Space: buy more memory. Expensive . . . may work in some cases . . . • Depth-first search: incomplete and suboptimal, but linear space • Iterative depth-first search: incomplete*, optimal and linear space c 2015 Blai Bonet Lecture 6 It’s easier and cheaper to manage time than space! Lecture 6 c 2015 Blai Bonet Lecture 6 Depth-first search Depth-first search for explicit graphs [CLRS] 1 void depth-first-search(Vertex root): 2 3 Depth-first search explores the search tree in depth-first manner always expanding the most recent generated node 4 5 6 7 % initialization foreach Vertex u color[u] = White distance[u] = ∞ parent[u] = null 8 Nodes can be ordered for expansion with a LIFO queue, but it is easier to formulate the search recursively 9 10 % depth-first visit on root dfs-visit(root) 11 12 Depth-first search is typically implemented as a tree-search algorithm (i.e. minimal duplicate elimination) 13 14 15 16 17 18 19 20 c 2015 Blai Bonet Lecture 6 void dfs-visit(Vertex u): color[u] = Gray % time of discovery foreach Vertex v in adj[u] if color[v] == White parent[v] = u distance[v] = distance[u] + 1 dfs-visit(v) color[u] = Black % time of finalisation c 2015 Blai Bonet Depth-first search: example c 2015 Blai Bonet Lecture 6 Depth-first search: example Lecture 6 c 2015 Blai Bonet Lecture 6 Depth-first search for implicit graphs Advantages and disadvantages of depth-first search Advantages: – Easy to implement 1 2 3 % depth-first search without duplicate elimination Node depth-first-search(): return dfs-visit(make-root-node(init())) – Efficient in space: only need to store current branch and sibling nodes along branch Node dfs-visit(Node n): if n.state.is-goal() return n – Space requirement is linear in depth of graph (constant branching) 4 5 6 7 8 9 10 11 foreach <s,a> in n.state.successors() Node m = dfs-visit(n.make-node(s,a)) if m != null return m Disadvantages: 12 13 return null % failure: there is no path from node n to goal – Incomplete (if tree is infinite, it can got stuck in an infinite branch) – Suboptimal: it may return a suboptimal path to goal Challenge: develop an optimal and linear-space algorithm c 2015 Blai Bonet Lecture 6 Iterative deepening depth-first search c 2015 Blai Bonet Lecture 6 Iterative deepening depth-first search: example Iterative deepening depth-first search does: – repeated depth-limited depth-first searches – with increasing depth limits for each search – until reaching a goal node It is almost equivalent to breadth-first search but uses less memory: – it visit the nodes within each iteration in a depth-first order – but the nodes in the tree are discovered in a breadth-first order It is a linear space algorithm and a tree-search algorithm (i.e. it performs minimal or no duplicate pruning) c 2015 Blai Bonet Lecture 6 c 2015 Blai Bonet Lecture 6 Iterative deepening depth-first search: example Second iteration: depth-first search in sub-tree of height 1 First iteration: depth-first search in sub-tree of height 0 c 2015 Blai Bonet Lecture 6 Iterative deepening depth-first search: example Third iteration: depth-first search in sub-tree of height 2 c 2015 Blai Bonet Iterative deepening depth-first search: example c 2015 Blai Bonet Lecture 6 Iterative deepening depth-first search: example Fourth iteration: depth-first search in sub-tree of height 3 Lecture 6 c 2015 Blai Bonet Lecture 6 Iterative deepening depth-first search: pseudocode Iterative deepening depth-first search: example 1 2 3 Node iterative-deepening-depth-first-search(): Node root = make-root-node(init()) bound = 0 4 5 6 7 8 9 % perform depth-limited searches with increasing depth bounds while true Node n = bounded-dfs-visit(root,0,bound) if n != null return n bound = bound + 1 10 11 12 13 14 15 Node bounded-dfs-visit(Node n, unsigned d, unsigned bound): % base cases if d > bound return null if n.state.is-goal() return n 16 17 Fifth iteration: depth-first search in sub-tree of height 4 18 19 20 21 c 2015 Blai Bonet Lecture 6 % recursion foreach <s,a> in n.state.successors() Node m = bounded-dfs-visit(n.make-node(s,a),d+1,bound) if m != null return m return null % failure: there is no path from node n to goal c 2015 Blai Bonet Alternative formulation Lecture 6 Iterative deepening depth-first search: alternative 1 2 3 Each iteration of iterative deepening depth-first search must guarantee: Node iterative-deepening-depth-first-search(): Node root = make-root-node(init()) bound = 0 4 5 6 7 – It visits at least one new node 8 – It remains optimal (first goal found corresponds to an optimal path) 9 10 11 12 13 Instead of increasing the bound one-by-one: % perform depth-limited searches with increasing depth bounds while true pair<Node,unsigned> p = bounded-dfs-visit(root,bound) if p.first != null return p.first bound = p.second 14 pair<Node,unsigned> bounded-dfs-visit(Node n, unsigned bound): % base cases if n.g > bound return (null,n.g) if n.state.is-goal() return (n,n.g) 15 16 – Calculate the depth of the nodes generated but not expanded 17 18 – Set the bound so that next iteration expand those nodes 19 20 21 22 23 c 2015 Blai Bonet Lecture 6 % recursion t = ∞ foreach <s,a> in n.state.successors() Node n’ = n.make-node(s,a) pair<Node,unsigned> p = bounded-dfs-visit(n’,bound) if p.first != null return p t = min(t,p.second) return (null,t) % failure: there is no path from node n to goal c 2015 Blai Bonet Lecture 6 Analysis Properties of iterative deepening depth-first search Iterative deepening depth-first search does: – Partially Complete: if goal is reachable, outputs a path (else it doesn’t terminate if search tree is infinite) – Complete depth-first searches for trees of depth 1, 2, 3, . . . , d – The root is expanded d times, its children d − 1 times, its grandchildren d − 2 times, . . . – Optimality: it returns a shortest path – Time complexity: O(bd ) (i.e. exponential in goal depth d) # expanded nodes ≤ d + (d − 1)b + (d − 2)b2 + · · · + bd−1 = d−1 X (d − k)bk = k=0 = bd d X d X k=1 – Space complexity: O(bd) (i.e. linear in goal depth d) kbd−k k=1 ∞ X d kb−k ≤ b k=0 kb−k = Time and space complexities calculated in canonical search tree with branching factor b where shallowest goal appears at depth d bd+1 (b − 1)2 c 2015 Blai Bonet Lecture 6 Iterative deepening uniform-cost search c 2015 Blai Bonet Lecture 6 Iterative deepening uniform-cost search: pseudocode 1 2 3 Node iterative-deepening-cost-search(): Node root = make-root-node(init()) bound = 0 4 5 It is for uniform-cost search what iterative deepening depth-first search is for breadth-first search: 6 7 8 9 – linear-space algorithm enforcing a uniform-cost discovery order 10 11 12 13 It performs cost-limited depth-first searches, increasing the cost limit with each iteration until reaching a goal node 14 pair<Node,unsigned> cost-bounded-dfs-visit(Node n, unsigned bound): % base cases if n.g > bound return (null,n.g) if n.state.is-goal() return (n,n.g) 15 16 17 18 19 20 21 22 23 c 2015 Blai Bonet % perform cost-limited searches with increasing cost bounds while true pair<Node,unsigned> p = cost-bounded-dfs-visit(root,bound) if p.first != null return p.first bound = p.second Lecture 6 % recursion t = ∞ foreach <s,a> in n.state.successors() Node n’ = n.make-node(s,a) pair<Node,unsigned> p = cost-bounded-dfs-visit(n’,bound) if p.first != null return p t = min(t,p.second) return (null,t) % failure: there is no path from node n to goal c 2015 Blai Bonet Lecture 6 Summary • In general, it is easier to deal with time than to deal with memory • Depth-first search is incomplete and suboptimal but linear space • Iterative deepening depth-first search: optimal for unit costs and linear space • Iterative deepening uniform-cost search: optimal for costs and linear space c 2015 Blai Bonet Lecture 6
© Copyright 2024