Documentation ¶
Overview ¶
Package astar implements the A* (“A Star”) search algorithm as described in the paper by Peter E. Hart et al, “A Formal Basis for the Heuristic Determination of Minimum Cost Paths” http://ai.stanford.edu/~nilsson/OnlinePubs-Nils/PublishedPapers/astar.pdf
The “open” and “closed” sets in this implementation are named “priority queue” and “explored set” respectively.
Time complexity of the algorithm depends on the quality of the heuristic function Estimate().
If Estimate() is constant, the complexity is the same as for the uniform cost search (UCS) algorithm – O(b^m), where b is the branching factor (how many successor states on average) and m is the maximum depth of the decision tree.
If Estimate() is optimal, the complexity is O(n).
The algorithm is implemented as a Search() function which takes astar.Interface as a parameter.
Basic usage (counting to 10):
type Number int func (n Number) Start() interface{} { return Number(1) } func (n Number) Finish() bool { return n == Number(10) } func (n *Number) Move(x interface{}) { *n = x.(Number) } func (n Number) Successors() []interface{} { return []interface{}{n - 1, n + 1} } func (n Number) Cost(x interface{}) float64 { return 1 } func (n Number) Estimate(x interface{}) float64 { return math.Abs(10 - float64(x.(Number))) } var n Number = 10 if path, walk, err := astar.Search(&n); err == nil { fmt.Println(path) fmt.Println(walk) } // Output: [1 2 3 4 5 6 7 8 9 10] —— the solution. // [1 2 3 4 5 6 7 8 9 10] —— states explored by the algorithm before it could find the best solution.
You could allow only “subtract 7” and “add 5” operations to get to 10:
func (n Number) Successors() []interface{} { return []interface{}{n - 7, n + 5} } // Output: [1 6 11 4 9 14 7 12 5 10] // [1 6 11 4 9 16 14 7 12 -1 2 5 10]
Or subtract 3, 7, and multiply by 9:
func (n Number) Successors() []interface{} { return []interface{}{n - 3, n - 7, n * 9} } // Output: [1 9 6 3 27 20 13 10] // [1 9 6 2 3 18 11 8 15 12 4 5 -2 -1 0 -5 -6 -4 -3 27 20 13 10]
Etc.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Interface ¶
type Interface interface { // Initial state. Start() interface{} // Is this state final or canceled? Finish() bool // Move to a new state. Move(interface{}) // Available moves from the current state. Successors(current StatePointer) []interface{} // Path cost between the current and the given state. Cost(interface{}) float64 // Heuristic estimate of “how far to go?” between the given // and the final state. Smaller values mean closer. Estimate(interface{}) float64 }
Interface describes a type suitable for A* search. Any type can do as long as it can change its current state and tell legal moves from it. Knowing costs and estimates helps, but not necessary.
type OptionalState ¶
type OptionalState *state
func Search ¶
func Search(p Interface) OptionalState
Search finds the p.Finish() state from the given p.Start() state by invoking p.Successors() and p.Move() at each step. Search returns the final state. If the shortest path cannot be found, nil is returned.
Example (PouringPuzzle) ¶
// Water pouring puzzle. // // Measure out 6 ounces of water using two glasses of 9 and 4 oz. // You're allowed to pour water from one glass to another as well as emptying // and refilling them. // // Use A* Search to solve it. // // =()= // .-||--| // . ___| // '==’ // || // || // | | || // | | // | | | | // | | | | // | | | | // | | | | // +-----+ +----+ // 9 oz. 4 oz. package main import ( "os" "text/template" ) // Glasses’ capacities and the goal. type Puzzle struct { CapFirst, CapSecond int Goal int } // Glasses state. type State struct { p *Puzzle Action string First int Second int } func (s State) Start() interface{} { return State{s.p, "Both Empty", 0, 0} } func (s State) Finish() bool { // One of the glasses contains the goal amount. return s.p.Goal == s.First || s.p.Goal == s.Second } func (s *State) Move(x interface{}) { *s = x.(State) } func (s State) Cost(x interface{}) float64 { return 1 } func (s State) Estimate(x interface{}) float64 { return 1 } func (s State) Successors(current StatePointer) []interface{} { succ := []interface{}{} First, Second, CapFirst, CapSecond := s.First, s.Second, s.p.CapFirst, s.p.CapSecond // Fill first glass. succ = append(succ, State{s.p, "Fill First", CapFirst, Second}) // Fill second glass. succ = append(succ, State{s.p, "Fill Second", First, CapSecond}) // Empty first glass. succ = append(succ, State{s.p, "Empty First", 0, Second}) // Empty second glass. succ = append(succ, State{s.p, "Empty Second", First, 0}) // Pour from the first glass into the second. if First+Second > CapSecond { succ = append(succ, State{s.p, "First –> Second", First - (CapSecond - Second), CapSecond}) } else { succ = append(succ, State{s.p, "First –> Second", 0, First + Second}) } // Pour from the second glass into the first. if First+Second > CapFirst { succ = append(succ, State{s.p, "Second –> First", CapFirst, Second - (CapFirst - First)}) } else { succ = append(succ, State{s.p, "Second –> First", First + Second, 0}) } return succ } const PouringTmpl = ` {{range .}} {{printf "%-16s (%v %v)\n" .Action .First .Second}}{{end}} ` func main() { if state := Search(&State{p: &Puzzle{ CapFirst: 9, CapSecond: 4, Goal: 6, }}); state != nil { path := []State{} for ; state != nil; state = state.Previous { path = append(path, state.Pather.(State)) } for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 { path[i], path[j] = path[j], path[i] } template.Must(template.New("Pouring Puzzle").Parse(PouringTmpl)).Execute(os.Stdout, path) } }
Output: Both Empty (0 0) Fill First (9 0) First –> Second (5 4) Empty Second (5 0) First –> Second (1 4) Empty Second (1 0) First –> Second (0 1) Fill First (9 1) First –> Second (6 4)
type StatePointer ¶
type StatePointer *state