(8-6-05)优先级遍历(Priority-based Search)算法:基于tkinter的多算法路径规划程序(5)

(7)函数breadth_first_search实现了广度优先搜索算法。它使用一个队列来存储待探索的节点,并通过迭代地从队列中取出节点来搜索路径。在搜索过程中,它会调用`add_neighbours`函数来添加节点的相邻节点,并在添加节点后继续搜索。当找到目标节点时,函数会停止搜索,并调用`paint`函数来绘制路径。最后,函数返回搜索得到的路径的距离、步数、队列的最大大小、弹出的节点数以及搜索所用的时间。

def breadth_first_search(start_time):
    queue = []
    found = False
    rear = 0
    front = 0
    (x, y) = gui.selected_cities[0]
    (end_x, end_y) = gui.selected_cities[1]

    point = Point(x, y)
    end = Point(end_x, end_y)
    start = point
    # print("Start {}, {}. End {}, {}".format(start.x, start.y, end.x, end.y))
    point.index = find_in_ovals(x, y)
    queue.append(point)  # source
    visited = np.zeros(gui.city_count)
    visited[point.index] = 1
    popped = 0
    max_element = 0

    while len(queue) > 0 and not found:
        point = queue[front]
        # print("Çekilen nokta: ", point.x, point.y)
        rear -= 1
        if point.equal(end):
            found = True
            print("Path found")
        else:
            top = add_neighbours(point, queue, rear, visited, end, 2)
            if len(queue) > max_element:
                max_element = len(queue)
            # print("Stack boyutu:", len(queue))
            popped += 1
        front += 1

    if found:
        total_distance, step_size = paint(point, start, max_element, popped)
        return total_distance, step_size, max_element, popped, time.time() - start_time
    else:
        message = "Path is not exist."
        gui.step_label.config(text=message)
    return 0, 0, 0, 0, time.time() - start_time

(8)函数paint用于绘制搜索得到的路径,其参数包括起始节点 `start`、搜索得到的目标节点 `point`、队列或栈的最大大小 `max_element` 以及弹出的节点数 `pops`。函数首先通过回溯得到路径上的所有节点,并计算路径的总距离和步数。然后,它在画布上绘制路径,并更新界面以显示搜索结果的动态过程。绘制路径时,函数会根据搜索速度设置的不同调整绘制的延迟时间。最后,函数返回路径的总距离和步数。

def paint(point, start, max_element, pops):
    path = []
    step_size = 0
    total_distance = 0
    delay = 0.3
    if gui.speed.get() == 0:  # fast
        delay = 0.2
    elif gui.speed.get() == 1:
        delay = 0.5

    while not point.equal(start):
        path.append(point)
        total_distance += distance(point.x, point.y, point.parent.x, point.parent.y, 1)
        point = point.parent
        step_size += 1

    message = "Step Size: {}, Distance: {}".format(step_size, int(total_distance))
    message2 = "Maximum Stack Size: {}\nNumber of Pops: {}".format(max_element, pops)
    gui.step_label.config(text=message)
    gui.output_label.config(text=message2)

    for i in range(len(path) - 1, -1, -1):
        gui.canvas.create_line(path[i].x + 5, path[i].y + 5, path[i].parent.x + 5, path[i].parent.y + 5,
                               width=3, fill="yellow")
        gui.window.update()
        time.sleep(delay)

    return total_distance, step_size

(9)下面这段代码定义了run函数,用于执行路径搜索算法并返回搜索结果。首先,它检查连接数、城市数以及连接数阈值是否符合要求,如果不符合则显示相应的错误消息。然后,根据用户选择的算法类型执行对应的搜索函数,并记录搜索过程中的路径距离、步数、最大堆栈/队列大小、弹出节点数以及总运行时间。最后,函数返回这些结果。

def run():
    if gui.conn_count < gui.city_total.get() - 1:
        msg.showerror(title="Connection Count Error", message="The number of connections is {} it's less than threshold"
                                                              " {}.".format(gui.conn_count, gui.city_total.get() - 1))
        return
    elif gui.city_count + 1 < gui.city_total.get():
        msg.showerror(title="City Count Error", message="The number of cities is {} but it's supposed to be {}.".
                      format(gui.city_count, gui.city_total.get()))
        return
    elif gui.conn_count < gui.entry.get() - 1:
        msg.showinfo(title="Connection Count Info", message="The number connections is {} but it's supposed to be {}."
                     .format(gui.conn_count, gui.entry.get()))
    start = time.time()
    if gui.algorithm.get() == 0:  # a *
        total_distance, step_size, max_element, popped, total_time = a_star_and_best_first_search(0, start)
    elif gui.algorithm.get() == 1:  # best first search
        total_distance, step_size, max_element, popped, total_time = a_star_and_best_first_search(1, start)
    elif gui.algorithm.get() == 2:
        total_distance, step_size, max_element, popped, total_time = depth_first_search(start)
    elif gui.algorithm.get() == 3:
        total_distance, step_size, max_element, popped, total_time = breadth_first_search(start)
    else:
        msg.showerror(title="Algorithm Selection Error", message="You did not choose the algorithm.")

    return total_distance, step_size, max_element, popped, total_time

(10)定义函数select_indices,用于随机选择两个城市索引,并确保它们不是邻居关系。它首先随机选择一个城市索引 `index1`,然后找到与该城市相连的城市,并将它们添加到 `neighbours` 列表中。接着,它再次随机选择另一个城市索引 `index2`,直到满足以下两个条件之一:`index1` 不等于 `index2`,或者 `(x1, y1)` 不在 `neighbours` 中。最后,它将所选的城市坐标添加到 `gui.selected_cities` 列表中。

def select_indices():
    index1 = random.randint(0, gui.city_total.get() - 1)
    index2 = index1

    neighbours = []
    i = 0
    x, y = gui.ovals[index1]["coords"]
    while i < len(gui.lines):  # find neighbours
        x1, y1, x2, y2 = gui.lines[i]["coords"]
        if distance(x, y, x1 - 5, y1 - 5, 0) == 0:
            neighbours.append((x2 - 5, y2 - 5))
        elif distance(x, y, x2 - 5, y2 - 5, 0) == 0:
            neighbours.append((x1 - 5, y1 - 5))
        i += 1

    index2 = random.randint(0, gui.city_total.get() - 1)
    x1, y1 = gui.ovals[index2]["coords"]
    while index1 == index2 or (x1, y1) in neighbours:  # işimizi şansa bırakmıyoruz
        index2 = random.randint(0, gui.city_total.get() - 1)
        x1, y1 = gui.ovals[index2]["coords"]
    print("Selected: ", x, y, x1, y1)
    gui.selected_cities.append((x, y))
    gui.selected_cities.append((x1, y1))

(11)定义函数test,用于运行一系列测试并将结果写入到 CSV 文件中。在测试过程中,它首先设置城市总数和连接数,并创建一个 CSV 文件以存储测试结果。然后,它循环执行以下步骤:

  1. 生成随机城市布局。
  2. 随机选择两个城市作为起点和终点。
  3. 分别以欧几里德距离和曼哈顿距离作为启发式函数运行 A* 算法。
  4. 分别以欧几里德距离和曼哈顿距离作为启发式函数运行 BFS 算法。
  5. 运行深度优先搜索(DFS)算法。
  6. 运行广度优先搜索(BFS)算法。

在每次运行算法后,将结果写入 CSV 文件中。最后,将这个过程重复执行一百次,以进行大规模测试。

def test():
    gui.city_total.set(20)
    gui.entry.set(40)

    with open('test_outputs.csv', 'w', newline='') as file:
        fieldnames = ['algorithm', 'distance', "step size", "max element", "popped", "time"]
        writer = csv.DictWriter(file, fieldnames=fieldnames)
        writer.writeheader()

        for i in range(100):
            print("Adım:", i)
            gui.create_randomly()
            gui.selected_cities = []
            select_indices()
            accepted = 0
            for j in range(6):
                if j == 0:
                    gui.algorithm.set(0)
                    gui.evaluation.set(0)
                    total_distance, step_size, max_element, popped, total_time = run()
                    accepted = total_distance
                    writer.writerow({"algorithm": "A* with Euclidean", "distance": accepted / total_distance , "step size": step_size,
                                  "max element": max_element, "popped": popped, "time": total_time})

                elif j == 1:
                    gui.algorithm.set(0)
                    gui.evaluation.set(1)
                    total_distance, step_size, max_element, popped, total_time = run()
                    writer.writerow({"algorithm": "A* with Manhattan", "distance": accepted / total_distance , "step size": step_size,
                                     "max element": max_element, "popped": popped, "time": total_time})

                elif j == 2:
                    gui.algorithm.set(1)
                    gui.evaluation.set(0)
                    total_distance, step_size, max_element, popped, total_time = run()
                    writer.writerow({"algorithm": "BFS with Euclidean", "distance": accepted / total_distance,
                                     "step size": step_size, "max element": max_element, "popped": popped, "time": total_time})

                elif j == 3:
                    gui.algorithm.set(1)
                    gui.evaluation.set(1)
                    total_distance, step_size, max_element, popped, total_time = run()
                    writer.writerow({"algorithm": "BFS with Manhattan", "distance": accepted / total_distance, "step size": step_size,
                                     "max element": max_element, "popped": popped, "time": total_time})

                elif j == 4:
                    gui.algorithm.set(2)
                    total_distance, step_size, max_element, popped, total_time = run()
                    writer.writerow({"algorithm": "DFS", "distance": accepted / total_distance, "step size": step_size,
                                     "max element": max_element, "popped": popped, "time": total_time})

                elif j == 5:
                    gui.algorithm.set(3)
                    total_distance, step_size, max_element, popped, total_time = run()
                    writer.writerow({"algorithm": "Breadth FS", "distance": accepted / total_distance, "step size": step_size,
                                     "max element": max_element, "popped": popped, "time": total_time})

(12)在下面这段代码中,当直接运行程序时会创建一个tkinter 应用程序的根窗口 root,然后初始化一个名为 Gui 的对象 gui,最后启动 Tkinter 的事件循环以显示 GUI 界面。如果想要执行测试函数 test(),只需取消注释 test() 调用的行即可。

if __name__ == "__main__":
    root = tk.Tk()
    gui = Gui()
    # test()
    root.mainloop()

通过如下命令运行本项目,

python Project.py

在测试过程中可以尝试不同的组合,以确保程序在各种情况下都能够正确地执行,并产生预期的结果。我们可以尝试以下几种操作:

  1. 启用不同的算法:尝试分别选择A*、最佳优先搜索、深度优先搜索和广度优先搜索,观察它们的行为和结果有何不同。
  2. 调整速度设置:尝试不同的速度设置(快速、中等、慢速),观察动画效果的变化和程序执行的速度。
  3. 改变城市和连接数量:尝试不同数量的城市和连接,观察程序对于不同规模数据的处理能力。
  4. 测试随机数据生成:测试随机数据生成功能,观察生成的城市和连接是否符合预期,并且能否正确地运行路径搜索算法。
  5. 通过这些测试,可以确保程序在各种情况下都能够正常工作,并产生正确的输出结果。

例如设置10个“Cities”、20个“Connections”,使用“Best First Search”算法寻早最短路径的效果如图8-6所示。

图8-6  最短路径可视化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

感谢鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值