什么是 A* 算法?

Python 中 A* 算法及代码实例_最短巡径

  • A* 是最受欢迎的寻路选择,因为它相当灵活,可以在各种环境中使用。
  • 它是一种人工智能算法,用于查找从开始到结束状态的最短路径。
  • 它可以应用于角色路径查找、解谜等等。它确实有无数的应用。
  • 斯坦福研究所(现为SRI International)的Peter Hart、Nils NilssonBertram Raphael于1968年首次发表了该算法。
  • A* 算法同时使用从起点到起点的实际距离和到目标的估计距离。

它是如何工作的?

现在让我们看看 A* 算法是如何工作的。它基于以下概念——

  • 计划从哪里开始,它的目标是从哪里开始。
  • 如何衡量实现目标的进展
  • 如何生成子项或所有可能的解决方案路径。

在其主循环的每次迭代中,A* 需要确定要扩展的路径。它基于路径的成本和对将路径一直延伸到目标所需的成本的估计来执行此操作。具体而言,A* 选择最小化的路径

f(n)=g(n)+h(n)

n = 路径上的下一个节点

g(n) = 从起始节点到 n 的路径成本

h(n) = 一个启发式函数,用于估计从 n 到目标的最便宜路径的成本

A* 算法

现在您将看到 A* 算法的算法。

# A* Algorithm
 
1. GENERATE A LIST of all possible next steps 
   towards goal from current position
 
2. STORE CHILDREN in priority queue 
   based on distance to goal, closest first
 
3. SELECT CLOSEST child and REPEAT until goal reached or no more children 

创建基类

首先,从队列中导入 PriorityQueue。然后你必须定义一个名为 State 或任何你想要的类。此类基本上是基类。

# PriorityQueue is a data structure. It organize items based on priority iset. 
from queue import PriorityQueue
 
#This is the base class that is to be used to store all the important steps that are 
#used for A* algorithm
class State(object):
    def __init__(self, value, parent, start = 0, goal = 0):
        self.children = []    #Children is a list of all membering possibilities 
        self.parent = parent  #store current parent
        self.value = value    #store current value
        self.dist = 0         #store current distance, dist is not actually gonna be set
                              #here, it is just a placeholder. It actually meant to be set to sub state
                              #or subclasses of State class
 
#Now Check the parent is plucked in 
        #if the parent is plucked in do following
        if parent:
            self.start = parent.start   # store start state
            self.goal = parent.goal     # store goal state
            self.path = parent.path[:]  # copy the parent path to your path. [:] is actually
                                        # allows to make a copy of that list(self.path) into our own list.
                                        # if [:] will be not here then self.path will have same value as parent.path
                                        # That's why [:] is important.
           
            self.path.append(value)     #Store all values into our path. This path is basically 
                                        #building own self and keeping track to where we at.
#check for if there is no parent
        else:
            self.path = [value] # set a path with list of objects started with our current value
            self.start = start  # store start state
            self.goal = goal    # store goal state
 
 
# create two empty functions that would be later defined in sub class.
    def GetDistance(self):
        pass
    def CreateChildren(self):
        pass

创建子类

现在我们将创建一个子类,其中包含两个方法 GetDistance() 和 CreateChildren( ) 方法。因此,请编写以下代码。

class State_String(State):
    def __init__(self, value, parent, start = 0, goal = 0 ):
        super(State_String, self).__init__(value, parent, start, goal) # create constructor
        self.dist = self.GetDistance() # override distance variable by calling GetDistance() method 
 
    def GetDistance(self):
           # first check to see if we have reached to our goal, and if we have then simply return 0  
            if self.value == self.goal:
                return 0
            dist = 0
 
           #Define a loop to go through each letter of the goal
            for i in range(len(self.goal)):
                letter = self.goal[i] # get the current letter
                
                dist += abs(i - self.value.index(letter)) #find index of letter in current value
                                                          #This will give the distance of letter is from its target p
            return dist   # simply return distance
 
#Define function to generate our children
    def CreateChildren(self):
            #if there are no children then go ahead and generate the children
            # this is just an extra precaution that we don't want to children twice 
 
            if not self.children:
                for i in range(len(self.goal)-1): # go through every possibilities or every possible arrangement of the letter.
                    val = self.value
 
                    # switching the second letter and the first letter of every pairs of letters
                    # and we track on the beginning and track on the end and then we have a new arrangement of letter in val.
                    val = val[:i] + val[i+1] + val[i] + val[i+2:]
 
                    # create a child and store the value of the child and pass self to store the parent of the child
                    child = State_String(val, self)
                    self.children.append(child) # finally add this child to our children list
 

创建A_Star_Solver子类

现在我们将创建一个真正神奇的类。因此,让我们编写以下代码。

class A_Star_Solver:
    def __init__(self, start, goal):
        self.path = []  # store final solution from start state to goal state
        self.vistedQueue =[] #it keeps track all the children that are visited
        self.priorityQueue = PriorityQueue()
        self.start = start  # store start state
        self.goal = goal    # store goal state
 
    def Solve(self):
        startState = State_String(self.start,0,self.start,self.goal) # it doesn't have any parent state. 
 
        count = 0
   
        # priorityQueue.put() is used to add children, you have to pass a tuple inside it.
        # The tuple contain 0, count and startState. 0 is priority number that we want
        self.priorityQueue.put((0,count, startState))
 
       # this while loop contain all the magic that is to be happenend
        while(not self.path and self.priorityQueue.qsize()):
               closesetChild = self.priorityQueue.get()[2]  # getting topmost value from the priority queue
               closesetChild.CreateChildren()    
               self.vistedQueue.append(closesetChild.value)   # it keep track all the children that we are visited
               for child in closesetChild.children:
                   if child.value not in self.vistedQueue:
                    count += 1
                    if not child.dist:
                       self.path = child.path
                       break
                    self.priorityQueue.put((child.dist,count,child))
        if not self.path:
            print("Goal Of  is not possible !" + self.goal )
        return self.path

创建 Main 函数

现在,我们将创建一个最终代码,它实际上调用了所有存在的东西。因此,请编写以下代码。

if __name__ == "__main__":
    start1 = "hema"
    goal1 = "mahe"
    print("Starting....")
    a = A_Star_Solver(start1,goal1) #Initializing object
    a.Solve()  # call Solve() method
    for i in range(len(a.path)):    #printing the result
        print("{0}){1}".format(i,a.path[i]))

A* 算法 Python 完整程序

from queue import PriorityQueue
 
#Creating Base Class
class State(object):
    def __init__(self, value, parent, start = 0, goal = 0):
        self.children = []
        self.parent = parent
        self.value = value
        self.dist = 0
        if parent:
            self.start = parent.start
            self.goal = parent.goal
            self.path = parent.path[:]
            self.path.append(value)
 
        else:
            self.path = [value]
            self.start = start
            self.goal = goal
 
    def GetDistance(self):
        pass
    def CreateChildren(self):
        pass
 
 
# Creating subclass
class State_String(State):
    def __init__(self, value, parent, start = 0, goal = 0 ):
        super(State_String, self).__init__(value, parent, start, goal)
        self.dist = self.GetDistance()
 
    def GetDistance(self):
            if self.value == self.goal:
                return 0
            dist = 0
            for i in range(len(self.goal)):
                letter = self.goal[i]
                dist += abs(i - self.value.index(letter))
            return dist
 
    def CreateChildren(self):
            if not self.children:
                for i in range(len(self.goal)-1):
                    val = self.value
                    val = val[:i] + val[i+1] + val[i] + val[i+2:]
                    child = State_String(val, self)
                    self.children.append(child)
 
# Creating a class that hold the final magic
class A_Star_Solver:
    def __init__(self, start, goal):
        self.path = []
        self.vistedQueue =[]
        self.priorityQueue = PriorityQueue()
        self.start = start
        self.goal = goal
 
    def Solve(self):
        startState = State_String(self.start,0,self.start,self.goal)
 
        count = 0
        self.priorityQueue.put((0,count, startState))
        while(not self.path and self.priorityQueue.qsize()):
               closesetChild = self.priorityQueue.get()[2]
               closesetChild.CreateChildren()
               self.vistedQueue.append(closesetChild.value)
               for child in closesetChild.children:
                   if child.value not in self.vistedQueue:
                    count += 1
                    if not child.dist:
                       self.path = child.path
                       break
                    self.priorityQueue.put((child.dist,count,child))
        if not self.path:
            print("Goal Of  is not possible !" + self.goal )
        return self.path
 
# Calling all the existing stuffs
if __name__ == "__main__":
    start1 = "hema"
    goal1 = "mahe"
    print("Starting....")
    a = A_Star_Solver(start1,goal1)
    a.Solve()
    for i in range(len(a.path)):
        print("{0}){1}".format(i,a.path[i]))

检查输出

Python 中 A* 算法及代码实例_A*搜寻算法_02