问题
I have implemented an A* algorithm to find the shortest path between two points in a grid world. For large path lengths the algorithm takes a very long time. I was first wondering if my implementation is correct, and if any optimization could take place?
The arguments for the aStar
algorithm, are the current position of you and the position you desire to travel to as (x,y)
tuples.
The Node.value
of a node is a direction to travel (NSEW), getAdjacentNodes()
returns a list of nodes directly adjacent to this one that we can travel to.
#Perform an A* search to find the best path to the dirt
def aStar(self, current, end):
openSet = set() #Set of explorable nodes
openHeap = [] #All paths heap, lowest cost on top
closedSet = set() #Best path so far
curNode = Node(0, current, self.manHatDist(current, end))
openSet.add(curNode)
openHeap.append((curNode.cost,curNode))
while openSet:
curNode = heapq.heappop(openHeap)[1]
if curNode.pos == end:
return self.getDirections(curNode)
openSet.remove(curNode)
closedSet.add(curNode)
for tile in self.getAdjacentNodes(curNode.pos):
if tile not in closedSet:
tile.parent = curNode
tile.cost = self.manHatDist(curNode.pos, end) + self.euclidDist(curNode.pos, current) + curNode.cost
if tile not in openSet:
openSet.add(tile)
heapq.heappush(openHeap, (tile.cost,tile))
return []
#Get the moves made to get to this endNode
def getDirections(self, endNode):
moves = []
tmpNode = endNode
while tmpNode.parent is not None:
moves.append(tmpNode.value)
tmpNode = tmpNode.parent
moves.reverse()
return moves
Node class
# Node class for A* search
class Node:
def __init__(self, value, pos, cost):
self.pos = pos
self.cost = cost
self.value = value
self.parent = None
def __lt__(a, b):
if(a.cost < b.cost):
return 1
return 0
def __gt__(a, b):
if(a.cost > b.cost):
return 1
return 0
EDIT - Here is the getAdjacentNodes
method
#Return all possible moves from given tile as Node objects
def getAdjacentNodes(self, curPos):
allMoves = ['North','South','East','West']
posMoves = []
for direction in allMoves:
if(self.canMove(direction, curPos)):
posMoves.append(Node(direction, self.getLocIfMove(curPos, direction), 0))
return posMoves
EDIT2 - Profiling result
Profile Result Pastebin Link
回答1:
This looks wrong to me:
for tile in self.getAdjacentNodes(curNode.pos):
if tile not in closedSet:
tile.parent = curNode
tile.cost = self.manHatDist(curNode.pos, end) + self.euclidDist(curNode.pos, current) + curNode.cost
if tile not in openSet:
openSet.add(tile)
heapq.heappush(openHeap, (tile.cost,tile))
First problem. The computation of the cost of the new tile is:
self.manHatDist(curNode.pos, end) + self.euclidDist(curNode.pos, current) + curNode.cost
but it ought to be:
curNode.cost
- self.manHatDist(curNode.pos, end)
+ self.euclidDist(curNode.pos, tile.pos)
+ self.manHatDist(tile.pos, end)
(You could avoid the subtraction in the computation of the cost of the new tile if you were cleverer about the way you represent the search nodes, but I will leave that to you.)
Second problem. Having discovered that tile
is not in closedSet
, you immediately assume that the best way to get to tile
is via curNode
. But isn't it possible that tile
is already in openSet
? If so, there might be another route to tile
that's better than the one via curNode
.* So this code ought to read:
for tile in self.getAdjacentNodes(curNode.pos):
if tile not in closedSet:
cost = (curNode.cost
- self.manHatDist(curNode.pos, end)
+ self.euclidDist(curNode.pos, tile.pos)
+ self.manHatDist(tile.pos, end))
if tile not in openSet or cost < tile.cost:
tile.parent = curNode
tile.cost = cost
openSet.add(tile)
heapq.heappush(openHeap, (cost,tile))
I can't say if this will solve your performance problems. But it might give better results.
* There couldn't be a shorter route if self.euclidDist(curNode.pos, tile.pos)
is always 1. But if that's the case, why bother with the euclidDist
method?
来源:https://stackoverflow.com/questions/19125808/python-review-and-speed-up-a-algorithm