Uninformed Search
Search Problem Definition
Real World → Search problem → Search Algorithm
We define states, actions, solutions (= selecting a state space) through abstraction.
Then we turn states into nodes for the search algorithm.
Solution
A solution is a sequence of actions leading from the initial state to a goal state.
- Initial state
- Successor function
- Goal test
- path cost (additive)
Example
We are on a holiday in Romania, Arad. Our flight leaves tomorrow from Bucharest.
-
initial state
starting_state = "at Arad"
-
successor function
S(prevState)={<prevState -> action, nextState>, ...}
S(Arad)={<Arad -> gotoZerind, Zerind>, ...}
-
goal test
explicit:
x == "at Bucharest"
implicit:
hasAirport(x, "Bucharest")
-
path cost (additive)
sum of distances, number of actions executed,
c(x, a, y)
is the step cost, assumed to be ≥ 0
-
initial state
Goal testing
- at expansion
- at generation → better performance, only storing necessary nodes
Tree-Search algorithm TS
Offline, simulated exploration of state space.
The frontier / open list set contains all child nodes that can be expanded.
Pseudocode
function TREE-SEARCH(problem) returns a solution, or failure initialize the frontier using the initial state of problem loop do if the frontier is empty then return failure choose a leaf node and remove it from the frontier if the node contains a goal state then return the corresponding solution expand the chosen node, adding the resulting nodes to the frontier
Graph-Search algorithm GS
Same as tree search but also stores all visited nodes in an explored set / closed list of already considered states to prevent endless looping.
Can be exponentially more efficient than tree search.
Pseudocode
function GRAPH-SEARCH(problem) returns a solution, or failure initialize the frontier using the initial state of problem initialize the explored set to be empty loop do if the frontier is empty then return failure choose a leaf node and remove it from the frontier if the node contains a goal state then return the corresponding solution add the node to the explored set expand the chosen node, adding the resulting nodes to the frontier
Uninformed search algorithms
completeness always finds existing solutions
space complexity max number of nodes in memory
time complexity max number of nodes generated/expanded.
solution optimality always finds the least-cost solution
maximum branching factor of search tree
depth of shallowest solution
maximum depth (may be )
depth limit
Overview
under the conditions:
- if is finite
- if step costs for positive
- optimal with identical step costs
1) Breadth-first search BFS
Frontier as queue.
The space and time complexity is too high.
Here the goal test is done at generation time.
Pseudocode
function BREADTH-FIRST-SEARCH(problem) returns a solution, or failure node ← a node with STATE = problem.INITIAL-STATE, PATH-COST = 0 if problem.GOAL-TEST(node.STATE) then return SOLUTION(node) frontier ← a FIFO queue with node as the only element explored ← an empty set loop do if EMPTY?(frontier) then return failure node ← POP(frontier) add node.STATE to explored for each action in problem.ACTIONS(node.STATE) do child ← CHILD-NODE(problem, node, action) if child.STATE is not in explored or frontier then if problem.GOAL-TEST(child.STATE) then return SOLUTION(child) frontier ← INSERT(child,frontier)
completeness yes, if is finite
time complexity
in depth
If solution is at depth and max branching is :
Goal test at generation time:
Goal test at expansion time:
space complexity
solution optimality yes, if step costs ≥ 1
2) Uniform-cost search UCS
Fronier as priority queue ordered by costs.
Here the goal test is done at expansion time.
Pseudocode
function UNIFORM-COST-SEARCH(problem) returns a solution, or failure node ← a node with STATE = problem.INITIAL-STATE, PATH-COST = 0 frontier ← a priority queue ordered by PATH-COST, with node as the only element explored ← an empty set loop do if EMPTY?(frontier) then return failure node ← POP(frontier) if problem.GOAL-TEST(node.STATE) then return SOLUTION(node) add node.STATE to explored for each action in problem.ACTIONS(node.STATE) do child ← CHILD-NODE(problem, node, action) if child.STATE is not in explored or frontier then frontier ← INSERT(child,frontier) else if child.STATE is in frontier with higher PATH-COST then replace that frontier node with child
completeness Yes, if step-cost and is finite
time complexity
in depth
if all step-costs
and is the cost of the optimal solution
then and therefore
if goal-tested at expansion, not generation - therefore .
space complexity
solution optimality Yes, if goal-tested at expansion.
3) Depth-first search DFS
Fronier as stack.
Much faster than BFS if solutions are dense.
Backtracking variant: store one successor node at a time - generate child node with change.
completeness No - only complete with explored-set (graph search) and in finite spaces.
time complexity - Terrible results if .
space complexity
solution optimality No
4) Depth-limited search DLS
DFS limited with - returns a subtree/subgraph.
Pseudocode
function DEPTH-LIMITED-SEARCH(problem, limit) returns a solution, or failure/cutoff return RECURSIVE-DLS(MAKE-NODE(problem.INITIAL-STATE), problem, limit)function RECURSIVE-DLS(node, problem, limit) returns a solution, or failure/cutoff if problem.GOAL-TEST(node.STATE) then return SOLUTION(node) else if limit = 0 then return cutoff else cutoff occurred? ← false for each action in problem.ACTIONS(node.STATE) do child ← CHILD-NODE(problem, node, action) result ← RECURSIVE-DLS(child, problem, limit − 1) if result = cutoff then cutoff occurred? ← true else if result != failure then return result if cutoff occurred? then return cutoff else return failure
completeness No - May not terminate (even for finite search space)
time complexity
space complexity
solution optimality No
5) Iterative deepening search IDS
Iteratively increasing in DLS to gain completeness.
Pseudocode
function ITERATIVE-DEEPENING-SEARCH(problem) returns a solution, or failure for depth = 0 to ∞ do result ← DEPTH-LIMITED-SEARCH(problem, depth) if result 6= cutoff then return result
completeness Yes
time complexity
in depth
Only times more inefficient than BFS therefore
space complexity
in depth
The total number of nodes generated in the worst case is:
Most of the nodes are in the bottom level, so it does not matter much that the upper levels are generated multiple times. In IDS, the nodes on the bottom level (depth ) are generated once, those on the next-to-bottom level are generated twice, and so on, up to the children of the root, which are generated times.
Therefore asymptotically the same as DFS.
solution optimality Yes, if step cost ≥ 1
In general IDS is very attractive and prefered when the search space is large and the depth of the solution is unknown.
It is usually only a little more expensive than BFS.