a*star 寻路可视化非常慢 / python

a*star pathfinding visualization is incredibly slow / python

我尝试使用 python 和 pygame 实现最短寻路算法。算法和可视化似乎有效。但是,当我将开始节点和结束节点彼此远离时,找到路径所需的时间呈指数增长。找到一条长度为 5 或 6 个节点的路径需要数小时。我认为问题出在 astar 函数中的循环之一,但我不知道是哪一个以及如何解决它。我在互联网上搜索了有关该问题的信息,但找不到任何解决方案。这是代码:

import pygame


class cube():
    rows = 30
    w = 840
    def __init__(self, pos=None, color=None):
        self.pos = pos
        self.color = color
        self.neighbours = []


    def draw(self, surface):
        dis = self.w // self.rows
        i = self.pos[0]
        j = self.pos[1]


        pygame.draw.rect(surface, self.color, (i * dis + 1, j * dis + 1, dis - 2, dis - 2))
class node(cube):

    def __init__(self, pos=None, color=None, parent=None):
        super().__init__(pos, color)
        self.parent = parent

        self.g = 0
        self.h = 0
        self.f = self.g + self.h

    def __eq__(self, other):
        return self.pos == other.pos

//here is the astar function

def astar(start, end):
    global rows, path, obs_list, start_node, end_node, window, closed_list, blue, children, red, green, grey

    counter = 0
    green = (0,255,0)
    red = (255,0,0)
    blue = (0,0,255)
    grey = (170,170,170)
    start_node = node(start, red)
    end_node = node(end, red)
    start_node.g = start_node.h = start_node.f = 0
    end_node.g = end_node.h = end_node.f = 0


    open_list = []
    closed_list = []
    open_list.append(start_node)

    while len(open_list) > 0:

        # Get the current node
        current_node = open_list[0]

        current_index = 0
        for index, item in enumerate(open_list):
            if item.f < current_node.f:
                current_node = item

                current_index = index

        # Pop current off open list, add to closed list
        open_list.pop(current_index)
        closed_list.append(current_node)
        visualize(window, open_list)


        if current_node.pos == end_node.pos:

            path = []
            current = current_node
            while current is not None:
                path.append(current.pos)
                current = current.parent
            return path[::-1]  # Return reversed path

        children = []
        for new_position in [(0, -1), (0, 1), (-1, 0), (1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)]:
            node_position = (current_node.pos[0] + new_position[0], current_node.pos[1] + new_position[1])



            #check if neighbour isn't a obstacle
            if node_position in obs_list:
              #  print("not possible")
                continue

            #check if neighbour isn't in closed list

            #create a new node
            new_node = node(node_position, green, current_node)



            # new node is the child of the previous node. Add child node to the list
            children.append(new_node)
        #     print(children)
            #loop through children
            for child in children:


                for closed_child in closed_list:
                    if child == closed_child:
                        continue

                child.g = current_node.g + 10
                child.h = ((child.pos[0] - end_node.pos[0]) **2) + ((child.pos[1] - end_node.pos[1]) **2)
                child.f = child.g + child.h

                for open_node in open_list:
                    if child == open_node and child.g > open_node.g:
                        continue



          #       visualize(window, children)
                    # Add the child to the open list
                open_list.append(child)


def draw_grid(w, rows, window):
    size_between = w // rows
    x = 0
    y = 0
    for l in range(rows):
        x = x + size_between
        y = y + size_between

        pygame.draw.line(window, (255,255,255), (x, 0), (x, w))
        pygame.draw.line(window, (255,255,255), (0,y), (w,y))


def visualize(surface, list):
    global red, blue, green, grey, counter
    for i,c in enumerate(list):
        c.draw(surface)
        pygame.display.update()




def show_path(surface):
    global path
    for i in path:
        node_path = cube(i, color=(0,0,255))
        node_path.draw(surface)
        pygame.display.update()

    for c in (path):
        node_p = cube(c, color=(35, 180, 89))
        node_p.draw(surface)

def draw_inital(surface):
    surface.fill((0, 0, 0))
    draw_grid(840, 30, surface)
    start_node.draw(surface)
    end_node.draw(surface)
    pygame.display.update()

def mouse_press(x):
    global rows, window, obs, start_node, end_node, obs_list
    t = x[0]
    w = x[1]

    g1 = t // (840 // rows)
    g2 = w // (840 // rows)
    obs = cube((g1,g2), (125, 125, 125))


    if obs.pos == start_node.pos:
        obs.color = (255,0,0)
        obs.draw(window)
        pygame.display.update()

    elif obs.pos == end_node.pos:

        obs.color = (255, 0, 0)
        obs.draw(window)
        pygame.display.update()

    else:
        if obs.pos not in obs_list:

            obs_list.append(obs.pos)
            obs.draw(window)
            pygame.display.update()




def main():
    global start_node, end_node, rows, window, counter, obs_list, start, end
    width = 840
    rows = 30
    window = pygame.display.set_mode((width,width))

    start = (12, 24)
    end = (12, 26)
    obs_list = []

    start_node = node(start, color=(255,0,0))
    end_node = node(end, color=(255,0,0))

    start_node.draw(window)
    end_node.draw(window)
    draw_inital(window)

    loop = True
    while loop:

        ev = pygame.event.get()
        for event in ev:
            if event.type == pygame.QUIT:
                pygame.quit()
            if pygame.mouse.get_pressed()[0]:
                try:
                    pos = pygame.mouse.get_pos()
                    mouse_press(pos)

                except AttributeError:
                    pass
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    loop = False
                    break

    path = astar(start, end)
    print(path)
    show_path(window)




    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False


main()

你的代码很难读。这是一些观察结果。

小错误

child.g = current_node.g + 10

需要成为

child.g = current_node.g + 1

这解决了问题,但只能靠运气。它实际上不是解决方案!

真正的错误

您向 open_list 添加了太多元素。

        for new_position in ...:
            # create a new node
            new_node = node(node_position, green, current_node)
            ...
            children.append(new_node)
            #     print(children)
            # loop through children
            for child in children:
                ...
                open_list.append(child)

请注意,您要为每个邻居 (for new_position in ...) (8x) 附加一个 new child 到 children 列表,然后迭代所有 children 到目前为止。这是 1/2 * 8 * 8 = 32 children 添加的。

一个简单的解决方法是每个 neighbor/child 只有一个循环,而不是目前的两个循环。

优先队列

您没有为开放列表使用优先队列。这意味着对于从打开列表中弹出的每个元素,您正在扫描整个列表。

使用这个:https://docs.python.org/3/library/heapq.html

设置成员资格

for closed_child in closed_list:
    if child == closed_child:
        continue

应该成为,但需要使 'node' 类型可哈希:

closed_list = set()
...
if child in closed_list:
    continue

启发式函数

错误:计算欧氏距离时忘记了平方根。