001 package aima.search.informed; 002 003 import java.util.List; 004 005 import aima.search.framework.Node; 006 import aima.search.framework.NodeExpander; 007 import aima.search.framework.Problem; 008 import aima.search.framework.Search; 009 import aima.search.framework.SearchUtils; 010 011 /** 012 * Artificial Intelligence A Modern Approach (2nd Edition): Figure 4.11, page 112. 013 * 014 * <code> 015 * function HILL-CLIMBING(problem) returns a state that is a local maximum 016 * inputs: problem, a problem 017 * local variables: current, a node 018 * neighbor, a node 019 * 020 * current <- MAKE-NODE(INITIAL-STATE[problem]) 021 * loop do 022 * neighbor <- a highest-valued successor of current 023 * if VALUE[neighbor] <= VALUE[current] then return STATE[current] 024 * current <- neighbor 025 * </code> 026 * Figure 4.11 The hill-climbing search algorithm (steepest ascent version), which is the 027 * most basic local search technique. At each step the current node is replaced by the best 028 * neighbor; in this version, that means the neighbor with the highest VALUE, but if a heuristic 029 * cost estimate h is used, we would find the neighbor with the lowest h. 030 */ 031 032 /** 033 * @author Ravi Mohan 034 * 035 */ 036 public class HillClimbingSearch extends NodeExpander implements Search { 037 038 public enum SearchOutcome { 039 FAILURE, SOLUTION_FOUND 040 }; 041 042 private SearchOutcome outcome = SearchOutcome.FAILURE; 043 044 private Object lastState = null; 045 046 public HillClimbingSearch() { 047 } 048 049 // function HILL-CLIMBING(problem) returns a state that is a local maximum 050 // inputs: problem, a problem 051 public List search(Problem p) throws Exception { 052 clearInstrumentation(); 053 outcome = SearchOutcome.FAILURE; 054 lastState = null; 055 // local variables: current, a node 056 // neighbor, a node 057 // current <- MAKE-NODE(INITIAL-STATE[problem]) 058 Node current = new Node(p.getInitialState()); 059 Node neighbor = null; 060 // loop do 061 while (true) { 062 List children = expandNode(current, p); 063 // neighbor <- a highest-valued successor of current 064 neighbor = getHighestValuedNodeFrom(children, p); 065 066 // if VALUE[neighbor] <= VALUE[current] then return STATE[current] 067 if ((neighbor == null) 068 || (getValue(p, neighbor) <= getValue(p, current))) { 069 if (p.isGoalState(current.getState())) { 070 outcome = SearchOutcome.SOLUTION_FOUND; 071 } 072 lastState = current.getState(); 073 return SearchUtils.actionsFromNodes(current.getPathFromRoot()); 074 } 075 // current <- neighbor 076 current = neighbor; 077 } 078 } 079 080 public SearchOutcome getOutcome() { 081 return outcome; 082 } 083 084 public Object getLastSearchState() { 085 return lastState; 086 } 087 088 private Node getHighestValuedNodeFrom(List children, Problem p) { 089 double highestValue = Double.NEGATIVE_INFINITY; 090 Node nodeWithHighestValue = null; 091 for (int i = 0; i < children.size(); i++) { 092 Node child = (Node) children.get(i); 093 double value = getValue(p, child); 094 if (value > highestValue) { 095 highestValue = value; 096 nodeWithHighestValue = child; 097 } 098 } 099 return nodeWithHighestValue; 100 } 101 102 private double getValue(Problem p, Node n) { 103 return -1 * getHeuristic(p, n); // assumption greater heuristic value => 104 // HIGHER on hill; 0 == goal state; 105 } 106 107 private double getHeuristic(Problem p, Node aNode) { 108 return p.getHeuristicFunction().getHeuristicValue(aNode.getState()); 109 } 110 }