diff --git a/X1.py b/X1.py new file mode 100644 index 0000000..e3dd654 --- /dev/null +++ b/X1.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X1.py diff --git a/X2.py b/X2.py new file mode 100644 index 0000000..9192806 --- /dev/null +++ b/X2.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X2.py + +# encoding: utf-8 +# 第一题,讲二进制数转换为10进制数 +def binToDec_int(b): + d = 0 + for i in range(len(b)): + # 请在此添加代码,补全函数BinToDec_int() + # -----------Begin---------- + if b[i] == '1': + d = d + 2 ** (len(b) - 1 - i) + # ------------End----------- + return d + + +def binToDec_float(b): + # 请在此添加代码,补全函数BinToDec_int() + # -----------Begin---------- + d = 0.0 + for i in range(len(b)): + # 请在此添加代码,补全函数BinToDec_int() + # -----------Begin---------- + if b[i] == '1': + d = d + 2 ** (-(i+1)) + # ------------End----------- + return d + + +if __name__ == '__main__': + + tests = [] + inputlist = input() + # print(type(inputlist)) + # for i in inputlist: + # tests.append(str(i)) + tests = inputlist.split(',') + for num in tests: + if '.' in num: + num1 = num.split('.') + print(binToDec_int(num1[0])+binToDec_float(num1[1])) + else: + print(binToDec_int(num)) \ No newline at end of file diff --git a/X3.py b/X3.py index 6f20d55..197f61c 100644 --- a/X3.py +++ b/X3.py @@ -1,205 +1,81 @@ -# -*- encoding: utf-8 -*- -""" - @Author: packy945 - @FileName: X3.py - @DateTime: 2023/7/12 14:07 - @SoftWare: PyCharm -""" -# -*- encoding: utf-8 -*- -from head import * - -class Node: - # 标记节点按钮 - def node_mark_display(self): - for w in frame_right.winfo_children(): - w.destroy() - # 清除右侧组件 - curx = -1 - y0 = 0.1 - for i in d.Nodes: - if i[2] == 1: - curx = i[0] - lab = tk.Label(frame_right, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) - lab.place(relx=0.18, rely=0.02, width=200, height=30) - lab1 = tk.Label(frame_right, text=f"当前共有{len(d.Nodes)}个节点", bg=RED, fg=BLACK, font=('微软雅黑', 15)) - lab1.place(relx=0.1, rely=0.0 + y0, width=200, height=30) - lab1 = tk.Label(frame_right, text="当前节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) - if curx == -1: - text = "未选择" - else: - text = f"{d.Nodes[curx][1]}" - - lab2 = tk.Label(frame_right, text=text, bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) - lab1.place(relx=0.1, rely=0.05 + y0, width=80, height=30) - lab2.place(relx=0.4, rely=0.05 + y0, width=80, height=30) - B = tk.Button(frame_right, text="设置标签", command=lambda: self.node_rename()) - B.place(relx=0.7, rely=0.05 + y0, width=80, height=30) - B = tk.Button(frame_right, text="删除当前节点", command=lambda: self.node_del_exact()) - B.place(relx=0.1, rely=0.1 + y0, width=150, height=30) - if d.Nodes[curx][4] == 1: - text = '设为非必经节点' - else: - text = '设为必经节点' - B = tk.Button(frame_right, text=text, command=lambda: self.dominator(curx)) - B.place(relx=0.1, rely=0.15 + y0, width=150, height=30) - B = tk.Button(frame_right, text="增加一个节点", command=lambda: self.node_add()) - B.place(relx=0.1, rely=0.25 + y0, width=150, height=30) - B = tk.Button(frame_right, text="减少一个节点", command=lambda: self.node_del()) - B.place(relx=0.1, rely=0.3 + y0, width=150, height=30) - - def dominator(self, x): - d.Nodes[x][4] ^= 1 - G.draw() - self.node_mark_display() - - def node_mark(self, name): - if name >= len(d.Nodes): - self.node_404(name) - return - - for node in d.Nodes: - if node[0] == name: - node[2] = 1 +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X3.py + +from collections import Counter +··· +class Solve (): + def node_dfs(self): # 遍历 深度优先算法 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] + for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break else: - node[2] = 0 - G.draw() - self.node_mark_display() - - def node_del_exact(self): - if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告 - tk.messagebox.askokcancel(title='错误', message='节点过少') - return - flag = 0 - cur = -1 - for node in d.Nodes:# 寻找当前选定的节点 - if node[2] == 1: - cur = node[0] - flag = 1 - if flag == 0: - tk.messagebox.askokcancel(title='错误', message='未选择节点') - return - d.nodes_num -= 1#节点数目减一 - d.ang = 360 / d.nodes_num#重新计算圆心角 - nodes = d.Nodes.copy()#复制节点 - d.Nodes.clear()#清除Data中的节点 - num = 0 - for n in nodes:#逐个复制节点 - if n[0] == cur:#若是删除的节点,则不复制 - continue - n[0] = num#重新编号 - rad = math.radians(num * d.ang)#计算当前角度 - x = int(math.cos(rad) * d.R)#计算当前点x坐标(相对) - y = int(math.sin(rad) * d.R)#计算当前点y坐标(相对) - n[3] = (d.center[0] + x, d.center[1] + y)#计算绝对坐标 - d.Nodes.append(n)#将当前点加入点集合中 - num += 1 - edges = d.Edges.copy()#将原有边集复制出来 - d.Edges.clear()#删除Data中的边集 - num = 0 - for e in edges:#复制边 - if e[1] == cur or e[2] == cur:#删除的边不复制 - continue - if e[1] > cur:#修改边对应的点的编号 - e[1] -= 1 - if e[2] > cur:#修改边对应的点的编号 - e[2] -= 1 - e[0] = num#重新编号 - d.Edges.append(e)#将边加入边集中 - num += 1 - G.draw()#重新绘图 - self.node_mark_display() - - def node_rename(self): - flag = 0 - for node in d.Nodes: - if node[2] == 1: - cur = node - flag = 1 - if flag == 1: - result = tkinter.simpledialog.askstring(title='修改节点标签', prompt='请输入修改后的标签:', - initialvalue=cur[1]) - else: - tk.messagebox.askokcancel(title='错误', message='未选择节点') - return - if result: - cur[1] = result - G.draw() - self.node_mark_display() - - def node_clear(self): - for node in d.Nodes: - node[2] = False - G.draw() - - - - def node_add(self): - # 增加一个节点 - nodes = len(d.Nodes) + 1 - # 如果节点数目大于五,则将连接详细信息改为不显示 - if nodes > 5: - d.edgeinfo = 0 - self.node_init(nodes) - G.draw() - self.node_mark_display() - - def node_set(self): - node = tkinter.simpledialog.askinteger(title='修改节点数目', prompt='请输入修改后的节点数目:', - initialvalue=str(len(d.Nodes))) - if node: - self.node_init(node) - G.draw() - - def node_del(self): - if d.nodes_num <= 2: - tk.messagebox.askokcancel(title='错误', message='节点过少') - return - nodes = len(d.Nodes) - 1 - if nodes < 6: - d.edgeinfo = 1 - self.node_init(nodes) - G.draw() - self.node_mark_display() - - def node_init(self, node): - if not node: - return - if d.source == 1: - d.data(node) - elif d.source == 2: - if node > 13: - tk.messagebox.askokcancel(title='错误', message='节点过多') - return - d.HEI_data(node) - else: - if node > 13: - tk.messagebox.askokcancel(title='错误', message='节点过多') - return - d.LIAO_data(node) - if node > 5: - d.edgeinfo = 0 - else: - d.edgeinfo = 1 - - def graph_del(self): - global d - del d - - - - def node_refresh(self): - nodes = len(d.Nodes) - self.node_init(nodes) - G.draw() - self.node_mark_display() - - def node_404(self, name): - # 弹出对话框 - tk.messagebox.askokcancel(title='错误', message='该节点不存在') - # print(result) - + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][ + result[j[0]][1]] = j[6] + distances[result[j[0]][1]][ + result[j[0]][0]] = j[6] + cost, ways = self.dfs(distances) + print(self.way_sum) + self.ans = ways.copy() + if len(ways) != l + 1: + tk.messagebox.askokcancel(title='结果', + message='该图不存在dfs路径') + # print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], + ways[way + 1])][4] = True + self.end = time.time() # 记录结束时间 + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], + ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', + message='dfs算法路径') + self.end = time.time() # 记录结束时间 + def greedy(self, distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] \ + < min_distance and distances[curr_city][ + city] != 0: + min_distance = distances[curr_city][city] + next_city = city + self.way_sum += 1 # 遍历路径的数目加一 + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + # print(path) + return path if __name__ == '__main__': - G.draw() - node = Node() - node.node_mark_display() - mainloop() \ No newline at end of file + ···编程[11.2]与编程[11.5]主函数 + s. node_greedy () # 开始遍历搜索合法路径 \ No newline at end of file diff --git a/X4.py b/X4.py new file mode 100644 index 0000000..08e40ff --- /dev/null +++ b/X4.py @@ -0,0 +1,1464 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X4.py +from tkinter import Tk +import random +import math +import time +import tkinter as tk +from tkinter import * +from tkinter import messagebox +import numpy as np +import networkx as nx + + +MODE = 0 +BLUE = "#0080FF" +BLACK = "#000000" +RED = "#FFAAAA" +YELLOW = "#FFFACD" +LINE = '#c8d2c8' +GREY = '#070b19' +GREEN = '#5ba585' +NODE = '#33a8cd' +ZERO = 'gold' +MARK_NODE = 0 +Specify_Nodes = '' +Edges_list = [] +NAME = [] +CLICK = [0, 0, 0] + +class Data: + def __init__(self, source: int, num: int): + global NAME + if num <= 0 or num > 10: + raise ValueError("num must be a positive integer") + self.edgeinfo = 1 # 是否显示详情 + self.nodes_num = num # 节点个数 + self.ang = 360 / self.nodes_num # 圆心顶点角度 + self.R = 300 # 外接圆半径 + self.bc = 2 * self.R * math.sin(math.pi / self.nodes_num) # 节点之间的正多边形距离 + self.canvas_len = int(2 * self.R + 80) # 画布边长 + self.center = (self.canvas_len // 2, self.canvas_len // 2) # 画布中心点坐标 + self.drop = [] + self.index = 0 + if source == 3 or source == 2: # 指定删除 + # name = [str(i) for i in range(1, num + 2)] + # print(name) + if source == 2: + NAME.pop() + else: + # print(Specify_Nodes) + NAME.remove(Specify_Nodes) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + print(self.Nodes) + self.Edges = self.edges_delete(source) # 创建第一条连接 + else: + if source == 1: + name = str(int(NAME[-1])+1) + # print(name) + NAME.append(name) + else: + NAME = [str(i) for i in range(1, num + 1)] + # print(NAME) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + print(self.Nodes) + self.Edges = self.edges_creat(source) # 创建第一条连接 + self.edge_add(2, source) # 创建第2条连接 + self.edge_add(3, source) # 创建第3条连接 + + def nodes_creat(self, n_sum=None): + if n_sum == None: + n_sum = self.nodes_num + nodes = [] # 初始化node表 + # 设置画布中心点坐标x0,y0 + x0 = self.center[0] + y0 = self.center[1] + for i in range(n_sum): # 通过几何运算得到多边形各个顶点坐标 + rad = math.radians(i * self.ang) # 计算第i个点的弧度 + x = int(math.cos(rad) * self.R) # 计算第i个顶点x坐标 + y = int(math.sin(rad) * self.R) # 计算第i个顶点y坐标 + # name = '' + str(i + 1) # 给第i个顶点命名 + name = '' + self.name[i] + mark = 0 # 节点为未标记 + dominator = 1 # 设置为必经节点 + nodes.append([i, name, mark, (x0 + x, y0 + y), dominator]) # 将当前节点加入node中 + return nodes + + def edges_delete(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 + # print(str(int(Specify_Nodes) + 1)) + for list_edge in Edges_list: + # print(list_edge) + edges.append([ser, list_edge[1], list_edge[2],list_edge[3], list_edge[4],list_edge[5], list_edge[6], + list_edge[7], list_edge[8]]) # 将连接加入到边的集合中 + nox = [int(num) for num in edges[ser][5].split('-')] + # print(nox) + if len(nox) == 2: + ss = edges[ser][5] + else: + ss = edges[ser][5][:-2] + # print(ss) + if source == 2: + if str(len(self.Nodes)+1) in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + else: + if Specify_Nodes in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + ser += 1 + return edges + + def edges_creat(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 # 初始化链接对象编号 + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + # print(nodes1) + self.drop = [] + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + # print(nodes1) + for node1 in nodes1: # 遍历删除后的节点信息 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + if self.drop.count(n2) > 3 or self.drop.count(n1) > 3: + dist_c = random.randint(101, 120) # 随机生成距离成本 + time_c = random.randint(101, 120) # 随机生成链接的时间 + else: + dist_c = random.randint(10, 120) # 随机生成距离成本 + time_c = random.randint(10, 120) # 随机生成链接的时间 + seq = 1 # 链接序号 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}" # 链接标签 + enable = 1 # 连接是否可用 + if dist_c >= 100 or time_c >= 100: # 设置某些节点为不显示 + enable = 0 + else: + self.drop.append(n1) + self.drop.append(n2) + if source == 1: + # print(n2, len(self.Nodes)) + if n2 == len(self.Nodes)-1: + # print('进来了') + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + else: + # print(self.index) + edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5],Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + self.index += 1 + else: + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + ser += 1 # 计数加一 + return edges + + def edge_add(self, no, source): + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + for node1 in nodes1: # 遍历删除后的节点信息 + ser = len(self.Edges) # 链接序号 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + show = 0 # 设置为不显示 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}-{no}" # 链接标签 + dist_c = random.randint(30, 100) # 距离成本 + time_c = random.randint(1, 30) # 链接的时间 + enable = no # 连接是否可用 + if source == 1: + if n2 == len(self.Nodes)-1: + # print('进来了') + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) # 将连接加入到边的集合中 + else: + # print(no ,self.index) + self.Edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5], Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + + self.index += 1 + else: + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) + + def coord_creat(self): + '''返回每个节点的坐标''' + coordinate = [] + x0 = self.center[0] + y0 = self.center[1] + for i in range(self.nodes_num): + rad = math.radians(i * self.ang) + x = int(math.cos(rad) * self.R) + y = int(math.sin(rad) * self.R) + coordinate.append((x0 + x, y0 + y)) + return coordinate + + def node_co(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[3] + + def node_no(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[0] + def node_coordinate(self, n): # 通过节点编号返回矩阵的坐标 + result = [] + for i in range(n): + for j in range(i + 1, n): + result.append((i, j)) + return result + +# G = Graph() +# print(1) +nodes = 6 # 设置初始节点数量 +d = Data(0, nodes) +# print(1) +root = Tk() # 设置主界面 +root.title("旅行商问题") # 设置标题 +root.geometry(f"{d.canvas_len + 700}x{d.canvas_len + 100}") # 设置大小 +root.resizable(0, 0) # 设置不能调整显示边框 + +frame = tk.Frame(root, padx=20, pady=20, width=d.canvas_len + 340, height=d.canvas_len + 50) +frame.grid() # 绘制显示框 + +frame_top = tk.Frame(frame, width=d.canvas_len, height=50) +frame_top.grid(row=0, column=0) # 绘制信息输出栏 + +cv = canvas = tk.Canvas(frame, bg='white', bd=2, relief="sunken", width=d.canvas_len + 330, height=d.canvas_len) +canvas.grid(row=1, column=0) # 放置绘图Canvas画布 + +frame_right = tk.Frame(root, bg=RED, width=300, height=d.canvas_len + 50) # 右边栏 +frame_right.grid(row=0, column=1) # 右边栏 + +class Graph: + # Graph定义 + def edge_info(self, cv: tk.Canvas, info: str, n1: tuple, n2: tuple): # 显示路径信息 + if d.edgeinfo == 0: # 若设置为不显示则直接返回 + return + # print(n1) + # print(n2) + x = (n1[0] - n2[0]) // 2 + n2[0] # 计算中点x坐标 + y = (n1[1] - n2[1]) // 2 + n2[1] # 计算中点y坐标 + if (n2[1] - n1[1]) != 0: # 斜率为非0度时的显示 + reat = math.atan((n2[0] - n1[0]) / (n2[1] - n1[1])) # 根据斜率计算弧度 + ang = round(math.degrees(reat)) + 90 # 根据弧度转角度 + if ang > 89: # 斜度大于90度调整坐标 + text_x, text_y = x + 15, y + else: # 斜度大于90度调整坐标 + text_x, text_y = x, y + 15 + else: # 斜率为0度时的显示 + ang = round(math.degrees(0)) # 根据弧度转角度 + text_x, text_y = x - 30, y + 10 + # print(info.split(' ')[-1]) + cv.create_text(text_x, text_y, text=f'{info.split()[-2]}', fill="black", font=('微软雅黑', 12), angle=ang) # 根据信息显示文字 + + def draw(self): + cv.delete('all') # 清空画布 + dis, tim = self.edges_show(cv, d.Edges) # 绘制边 + self.lab_show(frame_top, len(d.Nodes), tim, dis) # 显示边信息 + self.nodes_show(cv) # 绘制点 + cv.update() # 更新画布 + + + def lab_show(self, frame: tk.Frame, nodes: int, time: int, distance: int): + labs = ["节点数目", "时间", "距离(成本)"] + x0 = d.canvas_len // 3 + for i in range(len(labs)): + lab1 = tk.Label(frame, text=labs[i], fg=BLACK, font=('微软雅黑', 12)) + if i == 0: + lab2 = tk.Label(frame, text=f"{nodes}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + elif i == 1: + lab2 = tk.Label(frame, text=f"{time}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + else: + lab2 = tk.Label(frame, text=f"{distance}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=10 + x0 * i, y=10, width=80, height=30) + lab2.place(x=10 + x0 * i + 80, y=10, width=80, height=30) + lab1 = tk.Label(frame, text='分钟', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=170 + x0, y=10, width=50, height=30) + lab1 = tk.Label(frame, text='千米', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=170 + x0 * 2, y=10, width=50, height=30) + + def nodes_show(self, cv: tk.Canvas): + COL = NODE # 初始化节点颜色 + for i in d.Nodes: # 遍历每一个节点 + x, y = i[3] # 记录该节点坐标 + if i[2] == 1 or i[2] == 2: # 如果节点被标记,则将节点标为红色 + COL = RED + elif i[0] == 0: # 如果节点为起始节点,则将节点标记为特殊颜色 + COL = ZERO + elif i[2] == 5: # 如果节点已经被访问,则将节点标记(用户路径功能) + COL = 'gold' + else: # 否则则使用初始颜色 + COL = NODE + if i[4] == 1: # 如果是必经节点,则加黑色边框 + w = 1 + else: # 如果是非必经节点,则没有边框 + w = 0 + # 根据坐标,圆的颜色与是否有边框绘制圆圈来表示节点 + cv.create_oval(x - 20, y - 20, x + 20, y + 20, fill=COL, width=w, ) # outline='#ee28a3') + cv.create_text(x, y, text=f'{i[1]}', fill=GREY, font=('华文楷体', 18)) + cv.update() + + def edges_show(self, cv: tk.Canvas, edges: d.Edges): + """ + 显示链接 + :param cv: tkcanvas对象 + :return: + """ + distance = 0 # 初始化被标记的线的总路径 + time1 = 0 # 初始化被标记的线的总时间 + for edge in edges: + node_1 = d.node_co(edge[1]) # 链接线编号对应的坐标1 + node_2 = d.node_co(edge[2]) # 链接线编号对应的坐标2 + # print(node_1,node_2) + if edge[8] == 2 and edge[3] == 1: # 若该链接为两点间第二条连线 + self.more_edge_show(2, edge) + elif edge[8] == 3 and edge[3] == 1: # 若为两点间第3条连线 + self.more_edge_show(3, edge) + if edge[8] == 1 and edge[3] == 1: # 若为两点间第1条连线且该连接存在 + self.edge_info(cv, f'{edge[5]} {edge[6]} {edge[7]}', node_1, node_2) + point = [node_1, node_2] + if edge[4] == 1 and edge[8] == 1 and edge[3] == 1: # 如果路径被标记,则用绿色绘制 + if MODE == 1: + cv.create_line(point, fill=RED, width=4) + elif MODE == 2: + cv.create_line(point, fill=BLUE, width=4) + else: + cv.create_line(point, fill=GREEN, width=4) + distance += edge[6] # 更新总路径 + time1 += edge[7] # 更新总时间 + elif edge[8] == 1 and edge[3] == 1: # 如果是节点之间的第一条线,则用正常颜色标记 + cv.create_line(point, fill=LINE, width=2) + if d.Nodes[d.node_no(edge[2])][2] + d.Nodes[d.node_no(edge[2])][2] == 3: # 若该线被用户选中,则用蓝色虚线标记 + cv.create_line(point, fill=BLUE, width=5, dash=(4, 14)) + return (distance, time1) # 返回标记路径的总时间与路程 + + # def node_edge(self, a, b): # 根据点的编号找边的编号 + # if a > b: + # x = a + # a = b + # b = x + # return int(a * (2 * len(d.Nodes) - a - 3) / 2 + b - 1) + + def node_edge(self, a, b): # 根据点的编号找边的编号 + for edge in d.Edges: + if a == edge[1] and b == edge[2]: + return edge[0] + if b == edge[1] and a == edge[2]: + return edge[0] + + def way_node_edge(self, a, b): # 根据点的编号找边的编号 + way = [] + for node in d.Nodes: + way.append(int(node[1])) + # print(way) + # print(a, b) + x, y = way[a]-1, way[b]-1 + for edge in d.Edges: + if x == edge[1] and y == edge[2]: + return edge[0] + if y == edge[1] and x == edge[2]: + return edge[0] + + def delete_all(self): + cv.delete('all') # 清空画布 + self.lab_show(frame_top, 0, 0, 0) # 显示边信息 + W.user_way = [0] + W.way_route_markings() + +class Edge: + def __init__(self): + self.mark = [] + # 记录已标记的节点数目(连接操作) + self.user_cur = 0 + # 记录用户所在点 + + def node_edge(self, no, n1, n2): # 根据节点返回编号 + if n1 > n2: + n = n1 + n1 = n2 + n2 = n + for e in d.Edges: + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 + def edge_del_all(self, mode): + # mode = 1:优先最短路径 + # mode = 2:优先最短时间 + # cur1 = node_edge() + if mode == 1: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_cost(e[1], e[2]) + else: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_time(e[1], e[2]) + G.draw() + + def edge_merge_cost(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][6]), (cur2, d.Edges[cur2][6]), (cur3, d.Edges[cur3][6])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + def edge_merge_time(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][7]), (cur2, d.Edges[cur2][7]), (cur3, d.Edges[cur3][7])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + +class Solve: + def __init__(self): + self.ans = [] # 记录答案 + self.mutians = [] # 若有多个最短路,则保存在mutians中 + self.way_sum = 0 # 记录遍历的路径数目 + self.minn = float("inf") # 初始化最小值为正无穷 + self.start = 0 # 起始点 + self.end = 0 # 终点 + self.mark = [] # 记录该点是否已经经过 + self.user_cur = 0 # 记录用户所在点 + self.cura = -1 # 记录用户选择的第一个点 + self.curb = -1 # 记录用户选择的第二个点 + self.con = 0 # 记录必经的节点数目 + + def node_greedy(self): # 贪心 + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + self.start = time.time() # 记录开始时间 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.greedy(distances) + self.ans = ways.copy() + # 如果找到的路径不合法,则表示贪心法无法求解得到合法路径 + # print(ways) + # for i in distances: + # print(i) + if len(ways) != l+1 : + tk.messagebox.askokcancel(title='结果', message='该图不存在贪心算法路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + G.draw() + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + # print(ways[len(ways) - 1], i) + # d.Edges[G.way_node_edge(ways[len(ways) - 1], i)][4] = True # 标记起点终点形成环线 + # print(ways) + tk.messagebox.askokcancel(title='结果', message='贪心算法路径') + self.end = time.time() # 记录结束时间 + G.draw() + def greedy(self,distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + next_city = city + self.way_sum += 1 # 遍历路径的数目加一 + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + # print(path) + return path + + def tsp_backtracking(self): + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + self.start = time.time() # 记录开始时间 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.backtracking(distances) + print(self.way_sum) + if ways: # 判断路径是否存在 + self.ans = ways.copy() + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='回溯算法路径') + self.end = time.time() # 记录结束时间 + G.draw() + else: + tk.messagebox.askokcancel(title='结果', message='该图不存在回溯算法路径') + G.draw() + return -1 + + def backtracking(self, distances): + num_cities = len(distances) + path = [0] # 起始城市为0 + visited = [False] * num_cities + visited[0] = True + min_distance = float('inf') + shortest_path = None + + def backtrack(curr_city, curr_distance, visited, path): + nonlocal min_distance, shortest_path + if len(path) == num_cities: + # 到达所有城市,更新最短路径 + if curr_distance + distances[curr_city][0] < min_distance: + min_distance = curr_distance + distances[curr_city][0] + shortest_path = path + [0] + else: + for next_city in range(num_cities): + if not visited[next_city]: + # 选择下一个未访问的城市 + visited[next_city] = True + path.append(next_city) + new_distance = curr_distance + distances[curr_city][next_city] + self.way_sum += 1 # 遍历路径的数目加一 + # 剪枝条件:当前路径已经大于最短路径,不继续搜索 + if new_distance < min_distance: + backtrack(next_city, new_distance, visited, path) + # 回溯 + visited[next_city] = False + path.pop() + + backtrack(0, 0, visited, path) + return shortest_path + + def node_dfs(self): # 遍历 深度优先算法 + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + self.start = time.time() # 记录开始时间 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + cost, ways = self.dfs(distances) + print(self.way_sum) + self.ans = ways.copy() + if len(ways) != l + 1: + tk.messagebox.askokcancel(title='结果', message='该图不存在dfs路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + G.draw() + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='dfs算法路径') + self.end = time.time() # 记录结束时间 + G.draw() + + def dfs_tsp(self,graph, start, current, path, visited, cost, min_cost, min_path): + if len(path) == len(graph) and graph[current][start] != np.inf: + path.append(start) + cost += graph[current][start] + if cost < min_cost[0]: + min_cost[0] = cost + min_path[0] = path.copy() + path.pop() + cost -= graph[current][start] + return + + for next_node in range(len(graph)): + if graph[current][next_node] != np.inf and not visited[next_node]: + visited[next_node] = True + path.append(next_node) + cost += graph[current][next_node] + self.way_sum += 1 # 遍历路径的数目加一 + self.dfs_tsp(graph, start, next_node, path, visited, cost, min_cost, min_path) + visited[next_node] = False + path.pop() + cost -= graph[current][next_node] + + def dfs(self,graph): + n = len(graph) + min_cost = [float('inf')] + min_path = [[]] + + for start_node in range(n): + visited = [False] * n + path = [start_node] + cost = 0 + visited[start_node] = True + self.dfs_tsp(graph, start_node, start_node, path, visited, cost, min_cost, min_path) + return min_cost[0], min_path[0] + + def find_hamiltonian_cycles(self,start, distances): + n = len(distances) + path = [start] + cycles = [] + + def is_valid(node, position): + if distances[path[position - 1]][node] == np.inf: + return False + if node in path: + return False + return True + + def find_paths(node): + for next_node in range(n): + if is_valid(next_node, len(path)): + path.append(next_node) + if len(path) < n: + find_paths(next_node) + elif len(path) == n and distances[path[-1]][start] != np.inf: + cycles.append(path + [start]) + path.pop() + + find_paths(start) + return cycles + def check(self): + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 将路径中的 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # for i in distances: + # print(i) + all_hamiltonian_cycles = self.find_hamiltonian_cycles(0, np.array(distances)) + + if all_hamiltonian_cycles: + tk.messagebox.askokcancel(title='结果', message=f'该图存在 {len(all_hamiltonian_cycles)} 条路径\n') + else: + tk.messagebox.askokcancel(title='结果', message='该图不能完全互通') + +def node_edge(n1, n2, no=1): # 根据点的编号找边的编号 + if n1 > n2: # 为了防止充分编号,让n1小于n2 + n1, n2 = n2, n1 + for e in d.Edges: # 遍历所有路径,找到符合要求的返回 + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 # 若找不到,则返回-1 + +class Ways (): + def __init__(self): + self.user_way = [0] # 记录用户路径 + self.user_cur = 0 # 记录用户所在点 + self.user_flag = set() # 记录用户已经过的点 + def auto_ways(self): # 标记用户路径 + for w in d.Edges: # 清空路径标记 + w[4] = 0 + for N in d.Nodes: # 清空节点标记 + N[2] = 0 + for w in self.user_way: # 标记用户已选的点 + d.Nodes[w][2] = 5 + # 标记用户经过的路径 + for w in range(len(self.user_way) - 1): + d.Edges[G.node_edge(self.user_way[w],self.user_way[w + 1])][4] = 1 + # 如果已经访问所有节点,自动回到初始节点 + if len(set(self.user_way)) == len(d.Nodes): + d.Edges[G.node_edge(self.user_way[len(self.user_way) - 1], 0)][4] = 1 + G.draw() # 按照标记重新绘制图形 + + def check_user_best(self): # 检查用户路径是不是最佳路径 + # global user_way + # global ans + s.node_dfs() + # 长度不一样肯定不匹配 + if not len(self.user_way) == len(s.ans): + return -1,0 + dis_user = 0 + dis_ans = 0 + # print(user_way) + # print(ans) + num = len(self.user_way) + # print(self.user_way) + # print(s.ans) + for i in range(num - 1): + dis_user += d.Edges[G.node_edge(self.user_way[i], self.user_way[i+1])][6] + dis_ans += d.Edges[G.way_node_edge(s.ans[i], s.ans[i + 1])][6] + return dis_ans, dis_user + # if dis_ans == dis_user: # 匹配成功,用户路径是最短路 + # return 1 + # # 匹配失败,不是最短路 + # return 0 + + def show_best_ans(self, top, best, user): + lab1 = tk.Label(top, text="最短路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + # print(s.ans) + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + lab2 = tk.Label(top, text=f"最佳路径总成本:{best}\n用户路径总成本:{user}", bg=BLUE, fg=BLACK, + font=('微软雅黑', 12), wraplength=200, justify='left') + lab2.place(relx=0.4, rely=0.7 + y0, width=200, height=100) + + def show_BEST(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_right, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.8, width=80, height=30) + else: # 否则说明用户路径不是最短路径 + self.servey_path_display() + def servey_path_display(self): + lab1 = tk.Label(frame_right, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way = b_way[:-2] + lab2 = tk.Label(frame_right, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def check_user_greey(self): # 检查用户路径是不是贪心路径,不是的话返回错误点 + s.node_greedy()# 计算贪心路径 + if not len(self.user_way) == len(s.ans):# 路径长度不一样肯定不匹配,直接返回 + # self.auto_ways() + return 0 + way = [(int(node[1])-1) for node in d.Nodes] + result1 = list(map(str, self.user_way)) + result1 = ' '.join(result1) + ways = [way[an] for an in s.ans] + result2 = list(map(str, ways)) + result3 = ' '.join(result2) + result2.reverse() # 倒序排列列表元素 + result4 = ' '.join(result2) + if result4 == result1 or result3 == result1: + return -1 #-1表示是贪心路径 + return 1 + + + def show_greddy_ans(self, top): + lab1 = tk.Label(top, text="贪心路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + # 对用户路径进行标记与记录 + def way_route_markings(self): + global MARK_NODE + MARK_NODE = 2 + # E.newedge_del_all(1) + for w in frame_right.winfo_children(): + w.destroy() + # 清除右侧组件 + self.user_way = [0] + lab = tk.Label(frame_right, text="路径操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + global entry1,entry2,default_value + # 定义默认值变量 + default_value = tk.StringVar() + default_value.set(self.user_way[-1]+1) + laba1 = tk.Label(frame_right, text="当前节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + laba1.place(relx=0.00, rely=0.02 + y0, width=80, height=30) + # 创建第一个输入框 + entry1 = tk.Entry(frame_right, textvariable=default_value) + entry1.place(relx=0.3, rely=0.02 + y0, width=50, height=30) + labb1 = tk.Label(frame_right, text="当前下一节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + labb1.place(relx=0.48, rely=0.02 + y0, width=100, height=30) + # 创建第二个输入框 + entry2 = tk.Entry(frame_right) + entry2.place(relx=0.82, rely=0.02 + y0, width=50, height=30) + + labb3 = tk.Label(frame_right, text="是否进行连接:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + labb3.place(relx=0.2, rely=0.08 + y0, width=120, height=30) + # 按钮 + button = tk.Button(frame_right, text="确认", command=lambda: self.extract_values()) + button.place(relx=0.7, rely=0.08 + y0, width=80, height=30) + button2 = tk.Button(frame_right, text="撤销", command=lambda: self.revoke()) + button2.place(relx=0.01, rely=0.18 + y0, width=140, height=30) + button3 = tk.Button(frame_right, text="清除路径", command=lambda: self.clear_all_paths()) + button3.place(relx=0.52, rely=0.18 + y0, width=140, height=30) + button4 = tk.Button(frame_right, text="判断路径是否为贪心路径", command=lambda: self.greedy_path()) + button4.place(relx=0.01, rely=0.26 + y0, width=290, height=30) + button5 = tk.Button(frame_right, text="判断路径是否为最短路径", command=lambda: self.shortest_path()) + button5.place(relx=0.01, rely=0.33 + y0, width=290, height=30) + + self.user_path_display() + def extract_values(self): + global entry1, entry2, default_value + global MARK_NODE + MARK_NODE = 2 + value1 = entry1.get() # 获取第一个输入框的值 + value2 = entry2.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + E = 0 + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + for item in d.Edges: + if str_route == item[5]: + if item[6] < 100 and item[7] < 100: + if d.Edges[G.node_edge(value1-1, value2-1)][4]: + E = 2 + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + else: + if self.user_way[-1] == (value1-1): + E = 1 + global MODE + MODE = 1 + self.user_way.append(value2 - 1) + default_value.set(value2) + d.Edges[G.node_edge(value1-1, value2-1)][4] = True + entry1.config(textvariable=default_value) # 更新entry1的默认值 + entry2.delete(0, tk.END) + else: + E = 3 + tk.messagebox.askokcancel(title='错误', message='未连贯!') + break + print(E) + if E == 1: + G.draw() + self.user_path_display() + elif E == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def extract_ligature(self, n1, n2): + global MARK_NODE + MARK_NODE = 2 + no = G.way_node_edge(n1, n2) + flage = False + if d.Edges[no][4]: + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + elif d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + else: + if self.user_way[-1] == int(d.Nodes[n2][1]) - 1: + nx = n1 + n1 = n2 + n2 = nx + if self.user_way[-1] == int(d.Nodes[n1][1])-1: + global MODE + MODE = 1 + self.user_way.append(int(d.Nodes[n2][1]) - 1) + d.Edges[no][4] = True + flage = True + else: + tk.messagebox.askokcancel(title='错误', message='未连贯!') + if flage: + G.draw() + self.user_path_display() + + def revoke(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1+1}-{value2+1}' + else: + str_route = f'{value2+1}-{value1+1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 1 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2+1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + G.draw() + self.user_path_display() + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def clear_all_paths(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + for i in range(len(self.user_way)-1): + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1 + 1}-{value2 + 1}' + else: + str_route = f'{value2 + 1}-{value1 + 1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 3 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2 + 1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + for i in range(len(d.Edges)): + d.Edges[i][4] = False + G.draw() + self.user_path_display() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def greedy_path(self): + g = self.check_user_greey() + # print(g) + # print(self.user_way) + if g == -1: # 如果用户路径是贪心路径则显示 + lab1 = tk.Label(frame_right, text="用户路径是贪心路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.8, width=180, height=30) + elif g: + self.servey_greedy_path() + def servey_greedy_path(self): + lab1 = tk.Label(frame_right, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + lab2 = tk.Label(frame_right, text=f"{g_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def shortest_path(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_right, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.8, width=180, height=30) + else: # 否则说明用户路径不是最短路径 + self.servey_path_display() + + # 用户路径展示 + def user_path_display(self): + lab1 = tk.Label(frame_right, text="用户路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.55, width=80, height=30) + # self.auto_ways() + way = "" + # d_way = [] + # for i in d.Nodes: + # d_way.append(i[1]) + for i in range(len(self.user_way)): + if len(self.user_way)-i > 1: + way += str(self.user_way[i]+1) + way += "->" + else: + way += str(self.user_way[i]+1) + lab2 = tk.Label(frame_right, text=f"{way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.6, width=200, height=100) + + def way_operate(self): + global MARK_NODE + for i in range(len(d.Edges)): + d.Edges[i][4] = False + MARK_NODE = 0 + # E.newedge_del_all(1) + for w in frame_right.winfo_children(): + w.destroy() + # 清除右侧组件 + self.user_way = [0] + lab = tk.Label(frame_right, text="路径增加与删除", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + label1 = tk.Label(frame_right, text="节点1:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + label1.place(relx=0.00, rely=0.02 + y0, width=80, height=30) + global entry3, entry4 + # 创建第一个输入框 + entry3 = tk.Entry(frame_right) + entry3.place(relx=0.3, rely=0.02 + y0, width=50, height=30) + + label2 = tk.Label(frame_right, text="节点2:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + label2.place(relx=0.48, rely=0.02 + y0, width=100, height=30) + # 创建第二个输入框 + entry4 = tk.Entry(frame_right) + entry4.place(relx=0.82, rely=0.02 + y0, width=50, height=30) + + label3 = tk.Label(frame_right, text="是否进行两节点之间操作:", bg=RED, fg=BLACK, font=('微软雅黑', 14)) + label3.place(relx=0.1, rely=0.1 + y0, width=240, height=30) + + button = tk.Button(frame_right, text="线路增加", command=lambda: self.way_add()) + button.place(relx=0.25, rely=0.18 + y0, width=160, height=30) + button2 = tk.Button(frame_right, text="线路修改", command=lambda: self.way_change()) + button2.place(relx=0.25, rely=0.28 + y0, width=160, height=30) + button3 = tk.Button(frame_right, text="线路删除", command=lambda: self.way_delete()) + button3.place(relx=0.25, rely=0.38 + y0, width=160, height=30) + + def way_add(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + if d.drop.count(ways_list.index(value1)) > 4 or d.drop.count(ways_list.index(value2)) > 4: + print(d.drop) + tk.messagebox.askokcancel(title='提醒', message='当前节点已经到达最大连接数!') + return + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 1: + tk.messagebox.askokcancel(title='提醒', message='当前路线已存在!') + return + top = creat_window(str_route+'路径参数配置') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", 101) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", 101) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1,result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][3] = 1 + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_change(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + top = creat_window(str_route + '路径参数修改') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1, result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_delete(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + d.Edges[no][3] = 0 + d.Edges[no][6] = 111 + d.Edges[no][7] = 111 + G.draw() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + +class Node: + # 标记节点按钮 + def node_mark_display(self): + global MARK_NODE,Specify_Nodes + W.user_way = [0] + MARK_NODE = 1 + for w in frame_right.winfo_children(): + w.destroy() + # 清除右侧组件 + curx = -1 + y0 = 0.1 + for i in d.Nodes: + if i[2] == 1: + curx = i[0] + lab = tk.Label(frame_right, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + lab.place(relx=0.18, rely=0.02, width=200, height=30) + lab1 = tk.Label(frame_right, text=f"当前共有{len(d.Nodes)}个节点", bg=RED, fg=BLACK, font=('微软雅黑', 15)) + lab1.place(relx=0.1, rely=0.0 + y0, width=200, height=30) + lab1 = tk.Label(frame_right, text="当前节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + if curx == -1: + text = "未选择" + else: + text = f"{d.Nodes[curx][1]}" + Specify_Nodes = str(d.Nodes[curx][1]) + lab2 = tk.Label(frame_right, text=text, bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.05 + y0, width=80, height=30) + lab2.place(relx=0.4, rely=0.05 + y0, width=80, height=30) + B = tk.Button(frame_right, text="删除当前节点", command=lambda: self.node_del_exact()) + B.place(relx=0.1, rely=0.1 + y0, width=150, height=30) + B = tk.Button(frame_right, text="增加一个节点", command=lambda: self.node_add()) + B.place(relx=0.1, rely=0.25 + y0, width=150, height=30) + B = tk.Button(frame_right, text="减少一个节点", command=lambda: self.node_del()) + B.place(relx=0.1, rely=0.3 + y0, width=150, height=30) + + def node_add(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + # 增加一个节点 + nodes = len(d.Nodes) + 1 + # 如果节点数目大于五,则将连接详细信息改为不显示 + if nodes > 5: + d.edgeinfo = 0 + if nodes > 10: + tk.messagebox.askokcancel(title='错误', message='已达到最大节点') + return + d = Data(1, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display() + + + def node_del(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + if d.nodes_num <= 2: + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(2, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display() + + def node_del_exact(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = d.Edges + if len(Specify_Nodes) == 3: + tk.messagebox.askokcancel(title='错误', message='未选择节点') + return + if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告 + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(3, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display() + + +def refresh(): + node = random.randint(4, 10) + global d, MODE,MARK_NODE + MARK_NODE = 0 + MODE = 0 + d = Data(0, node) + print(node) + W.user_way=[0] + G.draw() + W.way_route_markings() +def graph_init(): + global MODE,MARK_NODE + MARK_NODE = 0 + MODE = 0 + G.draw() + W.way_route_markings() + +def empty(): + global MARK_NODE + MARK_NODE = 0 + G.delete_all() + +def auto_opera_mu(menu: tk.Menu): + """TSP问题自动生成与自动求解相关""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init()) + menu_node.add_command(label="重新生成", command=lambda: refresh()) + menu_node.add_command(label="清空", command=lambda: empty()) + menu_node.add_command(label="自动求解最优路径-dfs遍历", command=lambda: s.node_dfs()) + menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: s.node_greedy()) + menu_node.add_command(label="自动求解优化路径-回溯", command=lambda: s.tsp_backtracking()) + menu_node.add_command(label="检查是否存在路径", command=lambda: s.check()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node) + +# 创建弹出窗口 +def creat_window(title): + top = tk.Toplevel(root) + top.geometry("300x350") + top.title(title) + return top + +# 输入框 +def create_input_box(top, text, value): + box_label = tk.Label(top, text=text) + box_label.pack(padx=10, pady=10) + box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3 + box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象 + box_size_entry.pack(padx=20, pady=20) + return box_size_entry + +def dir2(x, y): # 计算两点间距离 + return (x[0] - y[0]) * (x[0] - y[0]) + (x[1] - y[1]) * (x[1] - y[1]) + +def left1(event): + # 查找鼠标左键按下时位置是否在某个节点内 + n = -1 + for node in d.Nodes: + if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: + n = node[0] + # n为点击的节点,若没有则为-1 + m = mode.get() # 查看现在操作类型 + if m == 1 and not n == -1: + # 节点操作 + # N.node_mark(n) + if n >= len(d.Nodes): + # 弹出对话框 + tk.messagebox.askokcancel(title='错误', message='该节点不存在') + return + for node in d.Nodes: + if node[0] == n: + node[2] = 1 + else: + node[2] = 0 + G.draw() + if MARK_NODE == 1: # 限制节点操作 + N.node_mark_display() + elif MARK_NODE == 2: # 若是MARK_NODE==2,则可以进行路线画图操作 + global CLICK + if CLICK[2] == 0: + CLICK[0] = n + elif CLICK[2] == 1: + CLICK[1] = n + W.extract_ligature(CLICK[0], CLICK[1]) + CLICK[2] = -1 + CLICK[2] += 1 + print(n) +def node_opera_mu(menu: tk.Menu): + """节点操作""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="添加一个节点", command=lambda: N.node_add()) + menu_node.add_command(label="删除一个节点", command=lambda: N.node_del()) + menu_node.add_command(label="选择一个节点", command=lambda: N.node_mark_display()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="节点操作", menu=menu_node) + +def path_opera_mu(menu: tk.Menu): + """路径操作""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="路径选择", command=lambda: W.way_route_markings()) + menu_node.add_command(label="路径修改", command=lambda: W.way_operate()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="路径操作", menu=menu_node, ) + +if __name__ == '__main__': + G = Graph() + mode = tk.IntVar(value=1) + main_menu = tk.Menu(root) + root.config(menu=main_menu) + cv.bind('', left1) + E = Edge() + W = Ways() + s = Solve() + N = Node() + auto_opera_mu(main_menu) + node_opera_mu(main_menu) + path_opera_mu(main_menu) + W.way_route_markings() + root.mainloop() diff --git a/X4_1.py b/X4_1.py new file mode 100644 index 0000000..9317b5a --- /dev/null +++ b/X4_1.py @@ -0,0 +1,1459 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/7 17:03 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X4_1.py + +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X4.py +from tkinter import Tk +import random +import math +import time +import tkinter as tk +from tkinter import * +from tkinter import messagebox +import numpy as np + +MODE = 0 +BLUE = "#0080FF" +BLACK = "#000000" +RED = "#FFAAAA" +YELLOW = "#FFFACD" +LINE = '#c8d2c8' +GREY = '#070b19' +GREEN = '#5ba585' +NODE = '#33a8cd' +ZERO = 'gold' +MARK_NODE = 0 +Specify_Nodes = '' +Edges_list = [] +NAME = [] + +class Data: + def __init__(self, source: int, num: int): + global NAME + if num <= 0 or num > 10: + raise ValueError("num must be a positive integer") + self.edgeinfo = 1 # 是否显示详情 + self.nodes_num = num # 节点个数 + self.ang = 360 / self.nodes_num # 圆心顶点角度 + self.R = 300 # 外接圆半径 + self.bc = 2 * self.R * math.sin(math.pi / self.nodes_num) # 节点之间的正多边形距离 + self.canvas_len = int(2 * self.R + 80) # 画布边长 + self.center = (self.canvas_len // 2, self.canvas_len // 2) # 画布中心点坐标 + self.drop = [] + self.index = 0 + if source == 3 or source == 2: # 指定删除 + # name = [str(i) for i in range(1, num + 2)] + # print(name) + if source == 2: + NAME.pop() + else: + # print(Specify_Nodes) + NAME.remove(Specify_Nodes) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + print(self.Nodes) + self.Edges = self.edges_delete(source) # 创建第一条连接 + else: + if source == 1: + name = str(int(NAME[-1])+1) + # print(name) + NAME.append(name) + else: + NAME = [str(i) for i in range(1, num + 1)] + # print(NAME) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + print(self.Nodes) + self.Edges = self.edges_creat(source) # 创建第一条连接 + self.edge_add(2, source) # 创建第2条连接 + self.edge_add(3, source) # 创建第3条连接 + + def nodes_creat(self, n_sum=None): + if n_sum == None: + n_sum = self.nodes_num + nodes = [] # 初始化node表 + # 设置画布中心点坐标x0,y0 + x0 = self.center[0] + y0 = self.center[1] + for i in range(n_sum): # 通过几何运算得到多边形各个顶点坐标 + rad = math.radians(i * self.ang) # 计算第i个点的弧度 + x = int(math.cos(rad) * self.R) # 计算第i个顶点x坐标 + y = int(math.sin(rad) * self.R) # 计算第i个顶点y坐标 + # name = '' + str(i + 1) # 给第i个顶点命名 + name = '' + self.name[i] + mark = 0 # 节点为未标记 + dominator = 1 # 设置为必经节点 + nodes.append([i, name, mark, (x0 + x, y0 + y), dominator]) # 将当前节点加入node中 + return nodes + + def edges_delete(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 + # print(str(int(Specify_Nodes) + 1)) + for list_edge in Edges_list: + # print(list_edge) + edges.append([ser, list_edge[1], list_edge[2],list_edge[3], list_edge[4],list_edge[5], list_edge[6], + list_edge[7], list_edge[8]]) # 将连接加入到边的集合中 + nox = [int(num) for num in edges[ser][5].split('-')] + # print(nox) + if len(nox) == 2: + ss = edges[ser][5] + else: + ss = edges[ser][5][:-2] + # print(ss) + if source == 2: + if str(len(self.Nodes)+1) in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + else: + if Specify_Nodes in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + ser += 1 + return edges + + def edges_creat(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 # 初始化链接对象编号 + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + # print(nodes1) + self.drop = [] + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + # print(nodes1) + for node1 in nodes1: # 遍历删除后的节点信息 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + if self.drop.count(n2) > 3 or self.drop.count(n1) > 3: + dist_c = random.randint(101, 120) # 随机生成距离成本 + time_c = random.randint(101, 120) # 随机生成链接的时间 + else: + dist_c = random.randint(10, 120) # 随机生成距离成本 + time_c = random.randint(10, 120) # 随机生成链接的时间 + seq = 1 # 链接序号 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}" # 链接标签 + enable = 1 # 连接是否可用 + if dist_c >= 100 or time_c >= 100: # 设置某些节点为不显示 + enable = 0 + else: + self.drop.append(n1) + self.drop.append(n2) + if source == 1: + # print(n2, len(self.Nodes)) + if n2 == len(self.Nodes)-1: + # print('进来了') + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + else: + # print(self.index) + edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5],Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + self.index += 1 + else: + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + ser += 1 # 计数加一 + return edges + + def edge_add(self, no, source): + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + for node1 in nodes1: # 遍历删除后的节点信息 + ser = len(self.Edges) # 链接序号 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + show = 0 # 设置为不显示 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}-{no}" # 链接标签 + dist_c = random.randint(30, 100) # 距离成本 + time_c = random.randint(1, 30) # 链接的时间 + enable = no # 连接是否可用 + if source == 1: + if n2 == len(self.Nodes)-1: + # print('进来了') + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) # 将连接加入到边的集合中 + else: + # print(no ,self.index) + self.Edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5], Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + + self.index += 1 + else: + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) + + def coord_creat(self): + '''返回每个节点的坐标''' + coordinate = [] + x0 = self.center[0] + y0 = self.center[1] + for i in range(self.nodes_num): + rad = math.radians(i * self.ang) + x = int(math.cos(rad) * self.R) + y = int(math.sin(rad) * self.R) + coordinate.append((x0 + x, y0 + y)) + return coordinate + + def node_co(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[3] + + def node_no(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[0] +# G = Graph() +# print(1) +nodes = 6 # 设置初始节点数量 +d = Data(0, nodes) +# print(1) +root = Tk() # 设置主界面 +root.title("旅行商问题") # 设置标题 +root.geometry(f"{d.canvas_len + 700}x{d.canvas_len + 100}") # 设置大小 +root.resizable(0, 0) # 设置不能调整显示边框 + +frame = tk.Frame(root, padx=20, pady=20, width=d.canvas_len + 340, height=d.canvas_len + 50) +frame.grid() # 绘制显示框 + +frame_top = tk.Frame(frame, width=d.canvas_len, height=50) +frame_top.grid(row=0, column=0) # 绘制信息输出栏 + +cv = canvas = tk.Canvas(frame, bg='white', bd=2, relief="sunken", width=d.canvas_len + 330, height=d.canvas_len) +canvas.grid(row=1, column=0) # 放置绘图Canvas画布 + +frame_right = tk.Frame(root, bg=RED, width=300, height=d.canvas_len + 50) # 右边栏 +frame_right.grid(row=0, column=1) # 右边栏 + +class Graph: + # Graph定义 + def edge_info(self, cv: tk.Canvas, info: str, n1: tuple, n2: tuple): # 显示路径信息 + if d.edgeinfo == 0: # 若设置为不显示则直接返回 + return + # print(n1) + # print(n2) + x = (n1[0] - n2[0]) // 2 + n2[0] # 计算中点x坐标 + y = (n1[1] - n2[1]) // 2 + n2[1] # 计算中点y坐标 + if (n2[1] - n1[1]) != 0: # 斜率为非0度时的显示 + reat = math.atan((n2[0] - n1[0]) / (n2[1] - n1[1])) # 根据斜率计算弧度 + ang = round(math.degrees(reat)) + 90 # 根据弧度转角度 + if ang > 89: # 斜度大于90度调整坐标 + text_x, text_y = x + 15, y + else: # 斜度大于90度调整坐标 + text_x, text_y = x, y + 15 + else: # 斜率为0度时的显示 + ang = round(math.degrees(0)) # 根据弧度转角度 + text_x, text_y = x - 30, y + 10 + # print(info.split(' ')[-1]) + cv.create_text(text_x, text_y, text=f'{info.split()[-2]}', fill="black", font=('微软雅黑', 12), angle=ang) # 根据信息显示文字 + + def draw(self): + cv.delete('all') # 清空画布 + dis, tim = self.edges_show(cv, d.Edges) # 绘制边 + self.lab_show(frame_top, len(d.Nodes), tim, dis) # 显示边信息 + self.nodes_show(cv) # 绘制点 + cv.update() # 更新画布 + + + def lab_show(self, frame: tk.Frame, nodes: int, time: int, distance: int): + labs = ["节点数目", "时间", "距离(成本)"] + x0 = d.canvas_len // 3 + for i in range(len(labs)): + lab1 = tk.Label(frame, text=labs[i], fg=BLACK, font=('微软雅黑', 12)) + if i == 0: + lab2 = tk.Label(frame, text=f"{nodes}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + elif i == 1: + lab2 = tk.Label(frame, text=f"{time}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + else: + lab2 = tk.Label(frame, text=f"{distance}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=10 + x0 * i, y=10, width=80, height=30) + lab2.place(x=10 + x0 * i + 80, y=10, width=80, height=30) + lab1 = tk.Label(frame, text='分钟', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=170 + x0, y=10, width=50, height=30) + lab1 = tk.Label(frame, text='千米', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=170 + x0 * 2, y=10, width=50, height=30) + + def nodes_show(self, cv: tk.Canvas): + COL = NODE # 初始化节点颜色 + for i in d.Nodes: # 遍历每一个节点 + x, y = i[3] # 记录该节点坐标 + if i[2] == 1 or i[2] == 2: # 如果节点被标记,则将节点标为红色 + COL = RED + elif i[0] == 0: # 如果节点为起始节点,则将节点标记为特殊颜色 + COL = ZERO + elif i[2] == 5: # 如果节点已经被访问,则将节点标记(用户路径功能) + COL = 'gold' + else: # 否则则使用初始颜色 + COL = NODE + if i[4] == 1: # 如果是必经节点,则加黑色边框 + w = 1 + else: # 如果是非必经节点,则没有边框 + w = 0 + # 根据坐标,圆的颜色与是否有边框绘制圆圈来表示节点 + cv.create_oval(x - 20, y - 20, x + 20, y + 20, fill=COL, width=w, ) # outline='#ee28a3') + cv.create_text(x, y, text=f'{i[1]}', fill=GREY, font=('华文楷体', 18)) + cv.update() + + def edges_show(self, cv: tk.Canvas, edges: d.Edges): + """ + 显示链接 + :param cv: tkcanvas对象 + :return: + """ + distance = 0 # 初始化被标记的线的总路径 + time1 = 0 # 初始化被标记的线的总时间 + for edge in edges: + node_1 = d.node_co(edge[1]) # 链接线编号对应的坐标1 + node_2 = d.node_co(edge[2]) # 链接线编号对应的坐标2 + # print(node_1,node_2) + if edge[8] == 2 and edge[3] == 1: # 若该链接为两点间第二条连线 + self.more_edge_show(2, edge) + elif edge[8] == 3 and edge[3] == 1: # 若为两点间第3条连线 + self.more_edge_show(3, edge) + if edge[8] == 1 and edge[3] == 1: # 若为两点间第1条连线且该连接存在 + self.edge_info(cv, f'{edge[5]} {edge[6]} {edge[7]}', node_1, node_2) + point = [node_1, node_2] + if edge[4] == 1 and edge[8] == 1 and edge[3] == 1: # 如果路径被标记,则用绿色绘制 + if MODE == 1: + cv.create_line(point, fill=RED, width=4) + elif MODE == 2: + cv.create_line(point, fill=BLUE, width=4) + else: + cv.create_line(point, fill=GREEN, width=4) + distance += edge[6] # 更新总路径 + time1 += edge[7] # 更新总时间 + elif edge[8] == 1 and edge[3] == 1: # 如果是节点之间的第一条线,则用正常颜色标记 + cv.create_line(point, fill=LINE, width=2) + if d.Nodes[d.node_no(edge[2])][2] + d.Nodes[d.node_no(edge[2])][2] == 3: # 若该线被用户选中,则用蓝色虚线标记 + cv.create_line(point, fill=BLUE, width=5, dash=(4, 14)) + return (distance, time1) # 返回标记路径的总时间与路程 + + # def node_edge(self, a, b): # 根据点的编号找边的编号 + # if a > b: + # x = a + # a = b + # b = x + # return int(a * (2 * len(d.Nodes) - a - 3) / 2 + b - 1) + + def node_edge(self, a, b): # 根据点的编号找边的编号 + for edge in d.Edges: + if a == edge[1] and b == edge[2]: + return edge[0] + if b == edge[1] and a == edge[2]: + return edge[0] + + def way_node_edge(self, a, b): # 根据点的编号找边的编号 + way = [] + for node in d.Nodes: + way.append(int(node[1])) + # print(way) + # print(a, b) + x, y = way[a]-1, way[b]-1 + for edge in d.Edges: + if x == edge[1] and y == edge[2]: + return edge[0] + if y == edge[1] and x == edge[2]: + return edge[0] + + def delete_all(self): + cv.delete('all') # 清空画布 + self.lab_show(frame_top, 0, 0, 0) # 显示边信息 + W.user_way = [0] + W.way_route_markings() + +class Edge: + def __init__(self): + self.mark = [] + # 记录已标记的节点数目(连接操作) + self.user_cur = 0 + # 记录用户所在点 + + def node_edge(self, no, n1, n2): # 根据节点返回编号 + if n1 > n2: + n = n1 + n1 = n2 + n2 = n + for e in d.Edges: + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 + def edge_del_all(self, mode): + # mode = 1:优先最短路径 + # mode = 2:优先最短时间 + # cur1 = node_edge() + if mode == 1: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_cost(e[1], e[2]) + else: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_time(e[1], e[2]) + G.draw() + + def edge_merge_cost(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][6]), (cur2, d.Edges[cur2][6]), (cur3, d.Edges[cur3][6])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + def edge_merge_time(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][7]), (cur2, d.Edges[cur2][7]), (cur3, d.Edges[cur3][7])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + +class Solve: + def __init__(self): + self.ans = [] # 记录答案 + self.mutians = [] # 若有多个最短路,则保存在mutians中 + self.way_sum = 0 # 记录遍历的路径数目 + self.minn = float("inf") # 初始化最小值为正无穷 + self.start = 0 # 起始点 + self.end = 0 # 终点 + self.mark = [] # 记录该点是否已经经过 + self.user_cur = 0 # 记录用户所在点 + self.cura = -1 # 记录用户选择的第一个点 + self.curb = -1 # 记录用户选择的第二个点 + self.con = 0 # 记录必经的节点数目 + + def node_greedy(self): # 贪心 + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + self.start = time.time() # 记录开始时间 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + ladder_sum = 0 + for j in range(1, l + 1): + ladder_sum += j - 1 + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + i += 1 + # print(j) + if i > ladder_sum: + break + # print(1) + else: + if j[6] < 99 and j[7] < 99: + distances[d.node_no(j[1])][d.node_no(j[2])] = j[6] + distances[d.node_no(j[2])][d.node_no(j[1])] = j[6] + ways = self.greedy(distances) + self.ans = ways.copy() + # 如果找到的路径不合法,则表示贪心法无法求解得到合法路径 + # print(ways) + # for i in distances: + # print(i) + if len(ways) != l+1 : + tk.messagebox.askokcancel(title='结果', message='该图不存在贪心算法路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + G.draw() + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + # print(ways[len(ways) - 1], i) + # d.Edges[G.way_node_edge(ways[len(ways) - 1], i)][4] = True # 标记起点终点形成环线 + # print(ways) + tk.messagebox.askokcancel(title='结果', message='贪心算法路径') + self.end = time.time() # 记录结束时间 + G.draw() + def greedy(self,distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + next_city = city + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + # print(path) + return path + + def tsp_backtracking(self,distances): + num_cities = len(distances) + path = [0] # 起始城市为0 + visited = [False] * num_cities + visited[0] = True + min_distance = float('inf') + shortest_path = None + + def backtrack(curr_city, curr_distance, visited, path): + nonlocal min_distance, shortest_path + if len(path) == num_cities: + # 到达所有城市,更新最短路径 + if curr_distance + distances[curr_city][0] < min_distance: + min_distance = curr_distance + distances[curr_city][0] + shortest_path = path + [0] + else: + for next_city in range(num_cities): + if not visited[next_city]: + # 选择下一个未访问的城市 + visited[next_city] = True + path.append(next_city) + new_distance = curr_distance + distances[curr_city][next_city] + # 剪枝条件:当前路径已经大于最短路径,不继续搜索 + if new_distance < min_distance: + backtrack(next_city, new_distance, visited, path) + # 回溯 + visited[next_city] = False + path.pop() + + backtrack(0, 0, visited, path) + return shortest_path + def node_dfs(self): # 遍历 深度优先算法 + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + self.start = time.time() # 记录开始时间 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + ladder_sum = 0 + for j in range(1, l + 1): + ladder_sum += j - 1 + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + i += 1 + # print(j) + if i > ladder_sum: + break + # print(1) + else: + if j[6] < 99 and j[7] < 99: + distances[d.node_no(j[1])][d.node_no(j[2])] = j[6] + distances[d.node_no(j[2])][d.node_no(j[1])] = j[6] + cost, ways = self.dfs(distances) + self.ans = ways.copy() + if len(ways) != l + 1: + tk.messagebox.askokcancel(title='结果', message='该图不存在dfs路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + G.draw() + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='dfs算法路径') + self.end = time.time() # 记录结束时间 + G.draw() + + def dfs_tsp(self,graph, start, current, path, visited, cost, min_cost, min_path): + if len(path) == len(graph) and graph[current][start] != np.inf: + path.append(start) + cost += graph[current][start] + if cost < min_cost[0]: + min_cost[0] = cost + min_path[0] = path.copy() + path.pop() + cost -= graph[current][start] + return + + for next_node in range(len(graph)): + if graph[current][next_node] != np.inf and not visited[next_node]: + visited[next_node] = True + path.append(next_node) + cost += graph[current][next_node] + self.dfs_tsp(graph, start, next_node, path, visited, cost, min_cost, min_path) + visited[next_node] = False + path.pop() + cost -= graph[current][next_node] + + def dfs(self,graph): + n = len(graph) + min_cost = [float('inf')] + min_path = [[]] + + for start_node in range(n): + visited = [False] * n + path = [start_node] + cost = 0 + visited[start_node] = True + self.dfs_tsp(graph, start_node, start_node, path, visited, cost, min_cost, min_path) + return min_cost[0], min_path[0] + + def graph_check(self, a, b): # 检查A点B点是否互通 + find = [] # 使用并查集来检测a,b点是否互通 + for i in range(len(d.Nodes)): # 初始化并查集 + find.append(i) # 使得find[i]=i + for edge in d.Edges: # 根据已有的边来更新并查集 + if (edge[8] == 1 and edge[3] == 0): + continue + x = edge[1] + y = edge[2] + while not x == find[x]: + x = find[x] + while not y == find[y]: + y = find[y] + if x < y: + find[y] = x + else: + find[x] = y + x = a + y = b + while not x == find[x]: # 查询a点所在集合 + x = find[x] + while not y == find[y]: # 查询b点所在集合 + y = find[y] + if find[x] == find[y]: # 输出检查结果 + messagebox.askokcancel(title='结果', message='节点 ' + str(a + 1) + '与节点 ' + str(b + 1) + '互通') + else: + messagebox.askokcancel(title='结果', message='节点 ' + str(a + 1) + '与节点 ' + str(b + 1) + '不互通') + + def check(self): # 检查是否存在最短路 + global MARK_NODE + MARK_NODE = 0 + find = [] + for i in range(len(d.Nodes)): + find.append(i) + for edge in d.Edges: + if edge[8] == 1 and edge[3] == 0: + continue + x = edge[1] + y = edge[2] + while not x == find[x]: + x = find[x] + while not y == find[y]: + y = find[y] + if x < y: + find[y] = x + else: + find[x] = y + flag = 1 + for i in range(len(d.Nodes)): + if not find[i] == find[0]: + flag = 0 + if flag: + tk.messagebox.askokcancel(title='结果', message='该图存在最短路') + else: + tk.messagebox.askokcancel(title='结果', message='该图不能完全互通') + +def node_edge(n1, n2, no=1): # 根据点的编号找边的编号 + if n1 > n2: # 为了防止充分编号,让n1小于n2 + n1, n2 = n2, n1 + for e in d.Edges: # 遍历所有路径,找到符合要求的返回 + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 # 若找不到,则返回-1 + +class Ways (): + def __init__(self): + self.user_way = [0] # 记录用户路径 + self.user_cur = 0 # 记录用户所在点 + self.user_flag = set() # 记录用户已经过的点 + def auto_ways(self): # 标记用户路径 + for w in d.Edges: # 清空路径标记 + w[4] = 0 + for N in d.Nodes: # 清空节点标记 + N[2] = 0 + for w in self.user_way: # 标记用户已选的点 + d.Nodes[w][2] = 5 + # 标记用户经过的路径 + for w in range(len(self.user_way) - 1): + d.Edges[G.node_edge(self.user_way[w],self.user_way[w + 1])][4] = 1 + # 如果已经访问所有节点,自动回到初始节点 + if len(set(self.user_way)) == len(d.Nodes): + d.Edges[G.node_edge(self.user_way[len(self.user_way) - 1], 0)][4] = 1 + G.draw() # 按照标记重新绘制图形 + + def check_user_best(self): # 检查用户路径是不是最佳路径 + # global user_way + # global ans + s.node_dfs() + # 长度不一样肯定不匹配 + if not len(self.user_way) == len(s.ans): + return -1,0 + dis_user = 0 + dis_ans = 0 + # print(user_way) + # print(ans) + num = len(self.user_way) + for i in range(num - 1): + dis_user += d.Edges[G.node_edge(self.user_way[i]+1, self.user_way[i+1] + 1)][6] + dis_ans += d.Edges[G.way_node_edge(s.ans[i], s.ans[i + 1])][6] + return dis_ans, dis_user + # if dis_ans == dis_user: # 匹配成功,用户路径是最短路 + # return 1 + # # 匹配失败,不是最短路 + # return 0 + + def show_best_ans(self, top, best, user): + lab1 = tk.Label(top, text="最短路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + lab2 = tk.Label(top, text=f"最佳路径总成本:{best}\n用户路径总成本:{user}", bg=BLUE, fg=BLACK, + font=('微软雅黑', 12), wraplength=200, justify='left') + lab2.place(relx=0.4, rely=0.7 + y0, width=200, height=100) + + def show_BEST(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_right, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.8, width=80, height=30) + else: # 否则说明用户路径不是最短路径 + self.servey_path_display() + def servey_path_display(self): + lab1 = tk.Label(frame_right, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + lab2 = tk.Label(frame_right, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def check_user_greey(self): # 检查用户路径是不是贪心路径,不是的话返回错误点 + s.node_greedy()# 计算贪心路径 + if not len(self.user_way) == len(s.ans):# 路径长度不一样肯定不匹配,直接返回 + self.auto_ways() + return + num = len(self.user_way) + for i in range(num):#逐个节点进行匹配 + if not self.user_way[i] == s.ans[i]:# 若第i位匹配失败则返回i + return i + return -1 #-1表示是贪心路径 + + + def show_greddy_ans(self, top): + lab1 = tk.Label(top, text="贪心路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + def show_GREDDY(self): + top = Toplevel(root, width = 500, height = 500) + top.title('判断路径是否为贪心路径')# 新建一个窗口来显示判断路径是否为贪心路径的结果 + lab1 = tk.Label(top, text="用户路径:", fg=BLACK, font=('微软雅黑', 20)) + u_way = ""# 显示用户路径 + for a in self.user_way: + u_way += d.Nodes[a][1] + u_way += "->" + u_way += d.Nodes[0][1] + # print(u_way) + y0 = 0.05 + lab2 = tk.Label(top, text=f"{u_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.10 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.05 + y0, width=200, height=100) + # 逐个节点比较用户路径是否为贪心路径 + g = self.check_user_greey() + print(g) + print(self.user_way) + if g == -1:#如果用户路径是贪心路径则显示 + result = '用户路径是贪心路径' + elif g: + result = '用户路径不是贪心路径' + result2 = '用户路径: ' + u_way + if len(s.ans) == 7: + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way += d.Nodes[0][1] + result2 += '\n贪心路径:' + g_way + else: + result2 += '\n贪心路径不存在' + lab = tk.Label(top, text=result2, bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab.place(relx=0.1, rely=0.5 + y0, width=250, height=100) + else:# 若用户路径不是贪心路径,则显示在第几步时选择出错,并将该次选择的正确选择与用户实际选择显示出来 + result = '用户路径不是贪心路径' + result2 = '用户路径:'+u_way + if len(s.ans) == 7: + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way += d.Nodes[0][1] + result2 += '\n贪心路径:' + g_way + else: + result2 += '\n贪心路径不存在' + lab = tk.Label(top, text=result2, bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab.place(relx=0.1, rely=0.5 + y0, width=250, height=100) + lab = tk.Label(top, text=result, bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=250, justify='left') + lab.place(relx=0.1, rely=0.3 + y0, width=200, height=50) + self.auto_ways()#将结果输出到输出框 + + # 对用户路径进行标记与记录 + def way_route_markings(self): + global MARK_NODE + MARK_NODE = 0 + # E.newedge_del_all(1) + for w in frame_right.winfo_children(): + w.destroy() + # 清除右侧组件 + self.user_way = [0] + lab = tk.Label(frame_right, text="路径操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + global entry1,entry2,default_value + # 定义默认值变量 + default_value = tk.StringVar() + default_value.set(self.user_way[-1]+1) + laba1 = tk.Label(frame_right, text="当前节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + laba1.place(relx=0.00, rely=0.02 + y0, width=80, height=30) + # 创建第一个输入框 + entry1 = tk.Entry(frame_right, textvariable=default_value) + entry1.place(relx=0.3, rely=0.02 + y0, width=50, height=30) + labb1 = tk.Label(frame_right, text="当前下一节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + labb1.place(relx=0.48, rely=0.02 + y0, width=100, height=30) + # 创建第二个输入框 + entry2 = tk.Entry(frame_right) + entry2.place(relx=0.82, rely=0.02 + y0, width=50, height=30) + + labb3 = tk.Label(frame_right, text="是否进行连接:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + labb3.place(relx=0.2, rely=0.08 + y0, width=120, height=30) + + button = tk.Button(frame_right, text="确认", command=lambda: self.extract_values()) + button.place(relx=0.7, rely=0.08 + y0, width=80, height=30) + button2 = tk.Button(frame_right, text="撤销", command=lambda: self.revoke()) + button2.place(relx=0.01, rely=0.18 + y0, width=140, height=30) + button3 = tk.Button(frame_right, text="清除路径", command=lambda: self.clear_all_paths()) + button3.place(relx=0.52, rely=0.18 + y0, width=140, height=30) + button4 = tk.Button(frame_right, text="判断路径是否为贪心路径", command=lambda: self.greedy_path()) + button4.place(relx=0.01, rely=0.26 + y0, width=290, height=30) + button5 = tk.Button(frame_right, text="判断路径是否为最短路径", command=lambda: self.shortest_path()) + button5.place(relx=0.01, rely=0.33 + y0, width=290, height=30) + + self.user_path_display() + def extract_values(self): + global entry1, entry2, default_value + value1 = entry1.get() # 获取第一个输入框的值 + value2 = entry2.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + E = 0 + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + for item in d.Edges: + if str_route == item[5]: + if item[6] < 100 and item[7] < 100: + if d.Edges[G.node_edge(value1-1, value2-1)][4]: + E = 2 + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + else: + if self.user_way[-1] == (value1-1): + E = 1 + global MODE + MODE = 1 + self.user_way.append(value2 - 1) + default_value.set(value2) + d.Edges[G.node_edge(value1-1, value2-1)][4] = True + entry1.config(textvariable=default_value) # 更新entry1的默认值 + entry2.delete(0, tk.END) + else: + E = 3 + tk.messagebox.askokcancel(title='错误', message='未连贯!') + break + print(E) + if E == 1: + G.draw() + self.user_path_display() + elif E == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def extract_ligature(self, n1, n2): + no = G.way_node_edge(n1, n2) + if d.Edges[no][4]: + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + else: + if self.user_way[-1] == int(d.Nodes[n1][1])-1: + global MODE + MODE = 1 + self.user_way.append(int(d.Nodes[n2][1]) - 1) + d.Edges[no][4] = True + else: + tk.messagebox.askokcancel(title='错误', message='未连贯!') + G.draw() + self.user_path_display() + def revoke(self): + try: + if len(self.user_way) > 1: + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1+1}-{value2+1}' + else: + str_route = f'{value2+1}-{value1+1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 1 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2+1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + G.draw() + self.user_path_display() + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def clear_all_paths(self): + try: + if len(self.user_way) > 1: + for i in range(len(self.user_way)-1): + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1 + 1}-{value2 + 1}' + else: + str_route = f'{value2 + 1}-{value1 + 1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 3 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2 + 1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + for i in range(len(d.Edges)): + d.Edges[i][4] = False + G.draw() + self.user_path_display() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def greedy_path(self): + g = self.check_user_greey() + # print(g) + # print(self.user_way) + if g == -1: # 如果用户路径是贪心路径则显示 + lab1 = tk.Label(frame_right, text="用户路径是贪心路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.8, width=180, height=30) + elif g: + self.servey_greedy_path() + def servey_greedy_path(self): + lab1 = tk.Label(frame_right, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way += d.Nodes[0][1] + lab2 = tk.Label(frame_right, text=f"{g_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def shortest_path(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_right, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.8, width=180, height=30) + else: # 否则说明用户路径不是最短路径 + self.servey_path_display() + + # 用户路径展示 + def user_path_display(self): + lab1 = tk.Label(frame_right, text="用户路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.55, width=80, height=30) + # self.auto_ways() + way = "" + # d_way = [] + # for i in d.Nodes: + # d_way.append(i[1]) + for i in range(len(self.user_way)): + if len(self.user_way)-i > 1: + way += str(self.user_way[i]+1) + way += "->" + else: + way += str(self.user_way[i]+1) + lab2 = tk.Label(frame_right, text=f"{way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.6, width=200, height=100) + + def way_operate(self): + global MARK_NODE + MARK_NODE = 0 + # E.newedge_del_all(1) + for w in frame_right.winfo_children(): + w.destroy() + # 清除右侧组件 + self.user_way = [0] + lab = tk.Label(frame_right, text="路径增加与删除", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + label1 = tk.Label(frame_right, text="节点1:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + label1.place(relx=0.00, rely=0.02 + y0, width=80, height=30) + global entry3, entry4 + # 创建第一个输入框 + entry3 = tk.Entry(frame_right) + entry3.place(relx=0.3, rely=0.02 + y0, width=50, height=30) + + label2 = tk.Label(frame_right, text="节点2:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + label2.place(relx=0.48, rely=0.02 + y0, width=100, height=30) + # 创建第二个输入框 + entry4 = tk.Entry(frame_right) + entry4.place(relx=0.82, rely=0.02 + y0, width=50, height=30) + + label3 = tk.Label(frame_right, text="是否进行两节点之间操作:", bg=RED, fg=BLACK, font=('微软雅黑', 14)) + label3.place(relx=0.1, rely=0.1 + y0, width=240, height=30) + + button = tk.Button(frame_right, text="线路增加", command=lambda: self.way_add()) + button.place(relx=0.25, rely=0.18 + y0, width=160, height=30) + button2 = tk.Button(frame_right, text="线路修改", command=lambda: self.way_change()) + button2.place(relx=0.25, rely=0.28 + y0, width=160, height=30) + button3 = tk.Button(frame_right, text="线路删除", command=lambda: self.way_delete()) + button3.place(relx=0.25, rely=0.38 + y0, width=160, height=30) + + def way_add(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + if d.drop.count(ways_list.index(value1)) > 4 or d.drop.count(ways_list.index(value2)) > 4: + print(d.drop) + tk.messagebox.askokcancel(title='提醒', message='当前节点已经到达最大连接数!') + return + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 1: + tk.messagebox.askokcancel(title='提醒', message='当前路线已存在!') + return + top = creat_window(str_route+'路径参数配置') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", 101) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", 101) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1,result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][3] = 1 + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_change(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + top = creat_window(str_route + '路径参数修改') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1, result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_delete(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + d.Edges[no][3] = 0 + d.Edges[no][6] = 111 + d.Edges[no][7] = 111 + G.draw() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + +class Node: + # 标记节点按钮 + def node_mark_display(self): + global MARK_NODE,Specify_Nodes + W.user_way = [0] + MARK_NODE = 1 + for w in frame_right.winfo_children(): + w.destroy() + # 清除右侧组件 + curx = -1 + y0 = 0.1 + for i in d.Nodes: + if i[2] == 1: + curx = i[0] + lab = tk.Label(frame_right, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + lab.place(relx=0.18, rely=0.02, width=200, height=30) + lab1 = tk.Label(frame_right, text=f"当前共有{len(d.Nodes)}个节点", bg=RED, fg=BLACK, font=('微软雅黑', 15)) + lab1.place(relx=0.1, rely=0.0 + y0, width=200, height=30) + lab1 = tk.Label(frame_right, text="当前节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + if curx == -1: + text = "未选择" + else: + text = f"{d.Nodes[curx][1]}" + Specify_Nodes = str(d.Nodes[curx][1]) + lab2 = tk.Label(frame_right, text=text, bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.05 + y0, width=80, height=30) + lab2.place(relx=0.4, rely=0.05 + y0, width=80, height=30) + B = tk.Button(frame_right, text="删除当前节点", command=lambda: self.node_del_exact()) + B.place(relx=0.1, rely=0.1 + y0, width=150, height=30) + B = tk.Button(frame_right, text="增加一个节点", command=lambda: self.node_add()) + B.place(relx=0.1, rely=0.25 + y0, width=150, height=30) + B = tk.Button(frame_right, text="减少一个节点", command=lambda: self.node_del()) + B.place(relx=0.1, rely=0.3 + y0, width=150, height=30) + + def node_add(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + # 增加一个节点 + nodes = len(d.Nodes) + 1 + # 如果节点数目大于五,则将连接详细信息改为不显示 + if nodes > 5: + d.edgeinfo = 0 + if nodes > 10: + tk.messagebox.askokcancel(title='错误', message='已达到最大节点') + return + d = Data(1, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display() + + + def node_del(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + if d.nodes_num <= 2: + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(2, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display() + + def node_del_exact(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = d.Edges + if len(Specify_Nodes) == 3: + tk.messagebox.askokcancel(title='错误', message='未选择节点') + return + if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告 + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(3, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display() + + +def refresh(): + node = random.randint(4, 10) + global d, MODE,MARK_NODE + MARK_NODE = 0 + MODE = 0 + d = Data(0, node) + print(node) + W.user_way=[0] + G.draw() + W.way_route_markings() +def graph_init(): + global MODE,MARK_NODE + MARK_NODE = 0 + MODE = 0 + G.draw() + W.way_route_markings() + +def empty(): + global MARK_NODE + MARK_NODE = 0 + G.delete_all() + +def auto_opera_mu(menu: tk.Menu): + """TSP问题自动生成与自动求解相关""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init()) + menu_node.add_command(label="重新生成", command=lambda: refresh()) + menu_node.add_command(label="清空", command=lambda: empty()) + menu_node.add_command(label="自动求解最优路径-dfs遍历", command=lambda: s.node_dfs()) + menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: s.node_greedy()) + menu_node.add_command(label="检查是否存在路径", command=lambda: s.check()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node) + +# 创建弹出窗口 +def creat_window(title): + top = tk.Toplevel(root) + top.geometry("300x350") + top.title(title) + return top + +# 输入框 +def create_input_box(top, text, value): + box_label = tk.Label(top, text=text) + box_label.pack(padx=10, pady=10) + box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3 + box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象 + box_size_entry.pack(padx=20, pady=20) + return box_size_entry + +def dir2(x, y): # 计算两点间距离 + return (x[0] - y[0]) * (x[0] - y[0]) + (x[1] - y[1]) * (x[1] - y[1]) + +def left1(event): + # 查找鼠标左键按下时位置是否在某个节点内 + n = -1 + for node in d.Nodes: + if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: + n = node[0] + + # n为点击的节点,若没有则为-1 + m = mode.get() # 查看现在操作类型 + if m == 1 and not n == -1: + # 节点操作 + # N.node_mark(n) + if n >= len(d.Nodes): + # 弹出对话框 + tk.messagebox.askokcancel(title='错误', message='该节点不存在') + return + for node in d.Nodes: + if node[0] == n: + node[2] = 1 + else: + node[2] = 0 + G.draw() + if MARK_NODE == 1: + N.node_mark_display() + elif MARK_NODE == 2: + pass + print(n) +def node_opera_mu(menu: tk.Menu): + """节点操作""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="添加一个节点", command=lambda: N.node_add()) + menu_node.add_command(label="删除一个节点", command=lambda: N.node_del()) + menu_node.add_command(label="选择一个节点", command=lambda: N.node_mark_display()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="节点操作", menu=menu_node) + +def path_opera_mu(menu: tk.Menu): + """路径操作""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="路径选择", command=lambda: W.way_route_markings()) + menu_node.add_command(label="路径修改", command=lambda: W.way_operate()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="路径操作", menu=menu_node, ) + +if __name__ == '__main__': + G = Graph() + mode = tk.IntVar(value=1) + main_menu = tk.Menu(root) + root.config(menu=main_menu) + cv.bind('', left1) + E = Edge() + W = Ways() + s = Solve() + N = Node() + auto_opera_mu(main_menu) + node_opera_mu(main_menu) + path_opera_mu(main_menu) + W.way_route_markings() + root.mainloop() diff --git a/X4_2.py b/X4_2.py new file mode 100644 index 0000000..0a35fed --- /dev/null +++ b/X4_2.py @@ -0,0 +1,1615 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X4.py +from tkinter import Tk +import random +import math +import time +import tkinter as tk +from tkinter import * +from tkinter import ttk +from tkinter import messagebox +import numpy as np +import networkx as nx +from PIL import Image, ImageTk + +MODE = 0 +BLUE = "#0080FF" +BLACK = "#000000" +RED = "#FFAAAA" +YELLOW = "#FFFACD" +LINE = '#c8d2c8' +GREY = '#070b19' +GREEN = '#5ba585' +NODE = '#33a8cd' +ZERO = 'gold' +MARK_NODE = 0 +Specify_Nodes = '' +Edges_list = [] +NAME = [] +CLICK = [0, 0, 0] + +List_Image = [] + +class Data: + def __init__(self, source: int, num: int): + global NAME + if num <= 0 or num > 10: + raise ValueError("num must be a positive integer") + self.edgeinfo = 1 # 是否显示详情 + self.nodes_num = num # 节点个数 + self.ang = 360 / self.nodes_num # 圆心顶点角度 + self.R = 300 # 外接圆半径 + self.bc = 2 * self.R * math.sin(math.pi / self.nodes_num) # 节点之间的正多边形距离 + self.canvas_len = int(2 * self.R + 80) # 画布边长 + self.center = ((self.canvas_len+220) // 2, (self.canvas_len+230) // 2) # 画布中心点坐标 + self.drop = [] + self.index = 0 + if source == 3 or source == 2: # 指定删除 + # name = [str(i) for i in range(1, num + 2)] + # print(name) + if source == 2: + NAME.pop() + else: + # print(Specify_Nodes) + NAME.remove(Specify_Nodes) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + # print(self.Nodes) + self.Edges = self.edges_delete(source) # 创建第一条连接 + else: + if source == 1: + name = str(int(NAME[-1])+1) + # print(name) + NAME.append(name) + else: + NAME = [str(i) for i in range(1, num + 1)] + # print(NAME) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + # print(self.Nodes) + self.Edges = self.edges_creat(source) # 创建第一条连接 + self.edge_add(2, source) # 创建第2条连接 + self.edge_add(3, source) # 创建第3条连接 + + def nodes_creat(self, n_sum=None): + if n_sum == None: + n_sum = self.nodes_num + nodes = [] # 初始化node表 + # 设置画布中心点坐标x0,y0 + x0 = self.center[0] + y0 = self.center[1] + for i in range(n_sum): # 通过几何运算得到多边形各个顶点坐标 + rad = math.radians(i * self.ang) # 计算第i个点的弧度 + x = int(math.cos(rad) * self.R) # 计算第i个顶点x坐标 + y = int(math.sin(rad) * self.R) # 计算第i个顶点y坐标 + # name = '' + str(i + 1) # 给第i个顶点命名 + name = '' + self.name[i] + mark = 0 # 节点为未标记 + dominator = 1 # 设置为必经节点 + nodes.append([i, name, mark, (x0 + x, y0 + y), dominator]) # 将当前节点加入node中 + return nodes + + def edges_delete(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 + # print(str(int(Specify_Nodes) + 1)) + for list_edge in Edges_list: + # print(list_edge) + edges.append([ser, list_edge[1], list_edge[2],list_edge[3], list_edge[4],list_edge[5], list_edge[6], + list_edge[7], list_edge[8]]) # 将连接加入到边的集合中 + nox = [int(num) for num in edges[ser][5].split('-')] + # print(nox) + if len(nox) == 2: + ss = edges[ser][5] + else: + ss = edges[ser][5][:-2] + # print(ss) + if source == 2: + if str(len(self.Nodes)+1) in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + else: + if Specify_Nodes in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + ser += 1 + return edges + + def edges_creat(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 # 初始化链接对象编号 + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + # print(nodes1) + self.drop = [] + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + # print(nodes1) + for node1 in nodes1: # 遍历删除后的节点信息 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + if self.drop.count(n2) > 3 or self.drop.count(n1) > 3: + dist_c = random.randint(101, 120) # 随机生成距离成本 + time_c = random.randint(101, 120) # 随机生成链接的时间 + else: + dist_c = random.randint(10, 120) # 随机生成距离成本 + time_c = random.randint(10, 120) # 随机生成链接的时间 + seq = 1 # 链接序号 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}" # 链接标签 + enable = 1 # 连接是否可用 + if dist_c >= 100 or time_c >= 100: # 设置某些节点为不显示 + enable = 0 + else: + self.drop.append(n1) + self.drop.append(n2) + if source == 1: + # print(n2, len(self.Nodes)) + if n2 == len(self.Nodes)-1: + # print('进来了') + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + else: + # print(self.index) + edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5],Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + self.index += 1 + else: + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + ser += 1 # 计数加一 + return edges + + def edge_add(self, no, source): + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + for node1 in nodes1: # 遍历删除后的节点信息 + ser = len(self.Edges) # 链接序号 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + show = 0 # 设置为不显示 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}-{no}" # 链接标签 + dist_c = random.randint(30, 100) # 距离成本 + time_c = random.randint(1, 30) # 链接的时间 + enable = no # 连接是否可用 + if source == 1: + if n2 == len(self.Nodes)-1: + # print('进来了') + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) # 将连接加入到边的集合中 + else: + # print(no ,self.index) + self.Edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5], Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + + self.index += 1 + else: + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) + + def coord_creat(self): + '''返回每个节点的坐标''' + coordinate = [] + x0 = self.center[0] + y0 = self.center[1] + for i in range(self.nodes_num): + rad = math.radians(i * self.ang) + x = int(math.cos(rad) * self.R) + y = int(math.sin(rad) * self.R) + coordinate.append((x0 + x, y0 + y)) + return coordinate + + def node_co(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[3] + + def node_no(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[0] + def node_coordinate(self, n): # 通过节点编号返回矩阵的坐标 + result = [] + for i in range(n): + for j in range(i + 1, n): + result.append((i, j)) + return result + +# G = Graph() +# print(1) +nodes = 6 # 设置初始节点数量 +d = Data(0, nodes) + +class Graph: + # Graph定义 + def edge_info(self, cv: tk.Canvas, info: str, n1: tuple, n2: tuple): # 显示路径信息 + if d.edgeinfo == 0: # 若设置为不显示则直接返回 + return + # print(n1) + # print(n2) + x = (n1[0] - n2[0]) // 2 + n2[0] # 计算中点x坐标 + y = (n1[1] - n2[1]) // 2 + n2[1] # 计算中点y坐标 + if (n2[1] - n1[1]) != 0: # 斜率为非0度时的显示 + reat = math.atan((n2[0] - n1[0]) / (n2[1] - n1[1])) # 根据斜率计算弧度 + ang = round(math.degrees(reat)) + 90 # 根据弧度转角度 + if ang > 89: # 斜度大于90度调整坐标 + text_x, text_y = x + 15, y + else: # 斜度大于90度调整坐标 + text_x, text_y = x, y + 15 + else: # 斜率为0度时的显示 + ang = round(math.degrees(0)) # 根据弧度转角度 + text_x, text_y = x - 30, y + 10 + # print(info.split(' ')[-1]) + cv.create_text(text_x, text_y, text=f'{info.split()[-2]}', fill="black", font=('微软雅黑', 12), angle=ang) # 根据信息显示文字 + + def draw(self): + cv.delete('all') # 清空画布 + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) + dis, tim = self.edges_show(cv, d.Edges) # 绘制边 + self.lab_show(frame_top, len(d.Nodes), tim, dis) # 显示边信息 + self.nodes_show(cv) # 绘制点 + cv.update() # 更新画布 + + + def lab_show(self, frame: tk.Frame, nodes: int, time: int, distance: int): + labs = ["节点数目", "时间", "距离(成本)"] + x0 = d.canvas_len // 3 + for i in range(len(labs)): + lab1 = tk.Label(frame, text=labs[i], fg=BLACK, bg='white', font=('微软雅黑', 12)) + if i == 0: + lab2 = tk.Label(frame, text=f"{nodes}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + elif i == 1: + lab2 = tk.Label(frame, text=f"{time}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + else: + lab2 = tk.Label(frame, text=f"{distance}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=10 + x0 * i, y=10, width=80, height=30) + lab2.place(x=10 + x0 * i + 80, y=10, width=80, height=30) + lab1 = tk.Label(frame, text='分钟', fg=BLACK, bg='white', font=('微软雅黑', 12)) + lab1.place(x=170 + x0, y=10, width=50, height=30) + lab1 = tk.Label(frame, text='千米', fg=BLACK, bg='white', font=('微软雅黑', 12)) + lab1.place(x=170 + x0 * 2, y=10, width=50, height=30) + + def nodes_show(self, cv: tk.Canvas): + COL = NODE # 初始化节点颜色 + for i in d.Nodes: # 遍历每一个节点 + x, y = i[3] # 记录该节点坐标 + if i[2] == 1 or i[2] == 2: # 如果节点被标记,则将节点标为红色 + COL = RED + elif i[0] == 0: # 如果节点为起始节点,则将节点标记为特殊颜色 + COL = ZERO + elif i[2] == 5: # 如果节点已经被访问,则将节点标记(用户路径功能) + COL = 'gold' + else: # 否则则使用初始颜色 + COL = NODE + if i[4] == 1: # 如果是必经节点,则加黑色边框 + w = 1 + else: # 如果是非必经节点,则没有边框 + w = 0 + # 根据坐标,圆的颜色与是否有边框绘制圆圈来表示节点 + cv.create_oval(x - 20, y - 20, x + 20, y + 20, fill=COL, width=w, ) # outline='#ee28a3') + cv.create_text(x, y, text=f'{i[1]}', fill=GREY, font=('华文楷体', 18)) + cv.update() + + def edges_show(self, cv: tk.Canvas, edges: d.Edges): + """ + 显示链接 + :param cv: tkcanvas对象 + :return: + """ + distance = 0 # 初始化被标记的线的总路径 + time1 = 0 # 初始化被标记的线的总时间 + for edge in edges: + node_1 = d.node_co(edge[1]) # 链接线编号对应的坐标1 + node_2 = d.node_co(edge[2]) # 链接线编号对应的坐标2 + # print(node_1,node_2) + if edge[8] == 2 and edge[3] == 1: # 若该链接为两点间第二条连线 + self.more_edge_show(2, edge) + elif edge[8] == 3 and edge[3] == 1: # 若为两点间第3条连线 + self.more_edge_show(3, edge) + if edge[8] == 1 and edge[3] == 1: # 若为两点间第1条连线且该连接存在 + self.edge_info(cv, f'{edge[5]} {edge[6]} {edge[7]}', node_1, node_2) + point = [node_1, node_2] + if edge[4] == 1 and edge[8] == 1 and edge[3] == 1: # 如果路径被标记,则用绿色绘制 + if MODE == 1: + cv.create_line(point, fill=RED, width=4) + elif MODE == 2: + cv.create_line(point, fill=BLUE, width=4) + else: + cv.create_line(point, fill=GREEN, width=4) + distance += edge[6] # 更新总路径 + time1 += edge[7] # 更新总时间 + elif edge[8] == 1 and edge[3] == 1: # 如果是节点之间的第一条线,则用正常颜色标记 + cv.create_line(point, fill=LINE, width=2) + if d.Nodes[d.node_no(edge[2])][2] + d.Nodes[d.node_no(edge[2])][2] == 3: # 若该线被用户选中,则用蓝色虚线标记 + cv.create_line(point, fill=BLUE, width=5, dash=(4, 14)) + return (distance, time1) # 返回标记路径的总时间与路程 + + # def node_edge(self, a, b): # 根据点的编号找边的编号 + # if a > b: + # x = a + # a = b + # b = x + # return int(a * (2 * len(d.Nodes) - a - 3) / 2 + b - 1) + + def node_edge(self, a, b): # 根据点的编号找边的编号 + for edge in d.Edges: + if a == edge[1] and b == edge[2]: + return edge[0] + if b == edge[1] and a == edge[2]: + return edge[0] + + def way_node_edge(self, a, b): # 根据点的编号找边的编号 + way = [] + for node in d.Nodes: + way.append(int(node[1])) + # print(way) + # print(a, b) + x, y = way[a]-1, way[b]-1 + for edge in d.Edges: + if x == edge[1] and y == edge[2]: + return edge[0] + if y == edge[1] and x == edge[2]: + return edge[0] + + def delete_all(self): + cv.delete('all') # 清空画布 + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) + self.lab_show(frame_top, 0, 0, 0) # 显示边信息 + W.user_way = [0] + window(2) + +class Edge: + def __init__(self): + self.mark = [] + # 记录已标记的节点数目(连接操作) + self.user_cur = 0 + # 记录用户所在点 + + def node_edge(self, no, n1, n2): # 根据节点返回编号 + if n1 > n2: + n = n1 + n1 = n2 + n2 = n + for e in d.Edges: + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 + def edge_del_all(self, mode): + # mode = 1:优先最短路径 + # mode = 2:优先最短时间 + # cur1 = node_edge() + if mode == 1: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_cost(e[1], e[2]) + else: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_time(e[1], e[2]) + G.draw() + + def edge_merge_cost(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][6]), (cur2, d.Edges[cur2][6]), (cur3, d.Edges[cur3][6])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + def edge_merge_time(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][7]), (cur2, d.Edges[cur2][7]), (cur3, d.Edges[cur3][7])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + +class Solve: + def __init__(self): + self.ans = [] # 记录答案 + self.mutians = [] # 若有多个最短路,则保存在mutians中 + self.way_sum = 0 # 记录遍历的路径数目 + self.minn = float("inf") # 初始化最小值为正无穷 + self.start = 0 # 起始点 + self.end = 0 # 终点 + self.mark = [] # 记录该点是否已经经过 + self.user_cur = 0 # 记录用户所在点 + self.cura = -1 # 记录用户选择的第一个点 + self.curb = -1 # 记录用户选择的第二个点 + self.con = 0 # 记录必经的节点数目 + + def node_greedy(self): # 贪心 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.greedy(distances) + self.ans = ways.copy() + # 如果找到的路径不合法,则表示贪心法无法求解得到合法路径 + print(self.way_sum) + # for i in distances: + # print(i) + if len(ways) != l+1 : + tk.messagebox.askokcancel(title='结果', message='该图不存在贪心算法路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + self.ans.clear() + self.end = time.time() # 记录结束时间 + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + # print(ways[len(ways) - 1], i) + # d.Edges[G.way_node_edge(ways[len(ways) - 1], i)][4] = True # 标记起点终点形成环线 + # print(ways) + tk.messagebox.askokcancel(title='结果', message='贪心算法路径') + self.end = time.time() # 记录结束时间 + def greedy(self,distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + next_city = city + self.way_sum += 1 # 遍历路径的数目加一 + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + # print(path) + return path + + def tsp_backtracking(self): + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.backtracking(distances) + print(self.way_sum) + + if ways: # 判断路径是否存在 + self.ans = ways.copy() + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='回溯算法路径') + self.end = time.time() # 记录结束时间 + else: + tk.messagebox.askokcancel(title='结果', message='该图不存在回溯算法路径') + self.end = time.time() # 记录结束时间 + return -1 + + def backtracking(self, distances): + num_cities = len(distances) + path = [0] # 起始城市为0 + visited = [False] * num_cities + visited[0] = True + min_distance = float('inf') + shortest_path = None + + def backtrack(curr_city, curr_distance, visited, path): + nonlocal min_distance, shortest_path + if len(path) == num_cities: + # 到达所有城市,更新最短路径 + if curr_distance + distances[curr_city][0] < min_distance: + min_distance = curr_distance + distances[curr_city][0] + shortest_path = path + [0] + else: + for next_city in range(num_cities): + if not visited[next_city]: + # 选择下一个未访问的城市 + visited[next_city] = True + path.append(next_city) + new_distance = curr_distance + distances[curr_city][next_city] + self.way_sum += 1 # 遍历路径的数目加一 + # 剪枝条件:当前路径已经大于最短路径,不继续搜索 + if new_distance < min_distance: + backtrack(next_city, new_distance, visited, path) + # 回溯 + visited[next_city] = False + path.pop() + + backtrack(0, 0, visited, path) + return shortest_path + + def auto_solve(self, number): + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + if number == 1: # 回溯 + self.tsp_backtracking() + elif number == 2: + self.node_greedy() # 贪心 + else: # 深度优先算法 + self.node_dfs() + window(1) + G.draw() + + def node_dfs(self): # 遍历 深度优先算法 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + cost, ways = self.dfs(distances) + print(self.way_sum) + self.ans = ways.copy() + if len(ways) != l + 1: + tk.messagebox.askokcancel(title='结果', message='该图不存在dfs路径') + # print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + self.end = time.time() # 记录结束时间 + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='dfs算法路径') + self.end = time.time() # 记录结束时间 + + + def dfs_tsp(self,graph, start, current, path, + visited, cost, min_cost, min_path): + if len(path) == len(graph) and graph[ + current][start] != np.inf: + path.append(start) + cost += graph[current][start] + if cost < min_cost[0]: + min_cost[0] = cost + min_path[0] = path.copy() + path.pop() + cost -= graph[current][start] + return + for next_node in range(len(graph)): + if graph[current][next_node] != np.inf and \ + not visited[next_node]: + visited[next_node] = True + path.append(next_node) + cost += graph[current][next_node] + self.way_sum += 1 # 遍历路径的数目加一 + self.dfs_tsp(graph, start, next_node, path, + visited, cost, min_cost, min_path) + visited[next_node] = False + path.pop() + cost -= graph[current][next_node] + def dfs(self, graph): + n = len(graph) + min_cost = [float('inf')] + min_path = [[]] + + for start_node in range(n): + visited = [False] * n + path = [start_node] + cost = 0 + visited[start_node] = True + self.dfs_tsp(graph, start_node, start_node, path, + visited, cost, min_cost, min_path) + return min_cost[0], min_path[0] + + def find_hamiltonian_cycles(self,start, distances): + n = len(distances) + path = [start] + cycles = [] + + def is_valid(node, position): + if distances[path[position - 1]][node] == np.inf: + return False + if node in path: + return False + return True + def find_paths(node): + for next_node in range(n): + if is_valid(next_node, len(path)): + path.append(next_node) + if len(path) < n: + find_paths(next_node) + elif len(path) == n and distances[path[-1]][start] != np.inf: + cycles.append(path + [start]) + path.pop() + + find_paths(start) + return cycles + def check(self): + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 将路径中的 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # for i in distances: + # print(i) + all_hamiltonian_cycles = self.find_hamiltonian_cycles(0, np.array(distances)) + + if all_hamiltonian_cycles: + tk.messagebox.askokcancel(title='结果', message=f'该图存在 {len(all_hamiltonian_cycles)} 条路径\n') + else: + tk.messagebox.askokcancel(title='结果', message='该图不能完全互通') + +def node_edge(n1, n2, no=1): # 根据点的编号找边的编号 + if n1 > n2: # 为了防止充分编号,让n1小于n2 + n1, n2 = n2, n1 + for e in d.Edges: # 遍历所有路径,找到符合要求的返回 + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 # 若找不到,则返回-1 + +class Ways (): + def __init__(self): + self.user_way = [0] # 记录用户路径 + self.user_cur = 0 # 记录用户所在点 + self.user_flag = set() # 记录用户已经过的点 + def auto_ways(self): # 标记用户路径 + for w in d.Edges: # 清空路径标记 + w[4] = 0 + for N in d.Nodes: # 清空节点标记 + N[2] = 0 + for w in self.user_way: # 标记用户已选的点 + d.Nodes[w][2] = 5 + # 标记用户经过的路径 + for w in range(len(self.user_way) - 1): + d.Edges[G.node_edge(self.user_way[w],self.user_way[w + 1])][4] = 1 + # 如果已经访问所有节点,自动回到初始节点 + if len(set(self.user_way)) == len(d.Nodes): + d.Edges[G.node_edge(self.user_way[len(self.user_way) - 1], 0)][4] = 1 + G.draw() # 按照标记重新绘制图形 + + def check_user_best(self): # 检查用户路径是不是最佳路径 + # global user_way + # global ans + s.node_dfs() + if not len(s.ans): + return -1, 0 + # 长度不一样肯定不匹配 + if not len(self.user_way) == len(s.ans): + return 0, 0 + dis_user = 0 + dis_ans = 0 + # print(user_way) + # print(ans) + num = len(self.user_way) + # print(self.user_way) + # print(s.ans) + for i in range(num - 1): + dis_user += d.Edges[G.node_edge(self.user_way[i], self.user_way[i+1])][6] + dis_ans += d.Edges[G.way_node_edge(s.ans[i], s.ans[i + 1])][6] + return dis_ans, dis_user + # if dis_ans == dis_user: # 匹配成功,用户路径是最短路 + # return 1 + # # 匹配失败,不是最短路 + # return 0 + + def show_best_ans(self, top, best, user): + lab1 = tk.Label(top, text="最短路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + # print(s.ans) + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + lab2 = tk.Label(top, text=f"最佳路径总成本:{best}\n用户路径总成本:{user}", bg=BLUE, fg=BLACK, + font=('微软雅黑', 12), wraplength=200, justify='left') + lab2.place(relx=0.4, rely=0.7 + y0, width=200, height=100) + + def show_BEST(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + + if not best: + lab1 = tk.Label(frame_left, text="不存在最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + else: + lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + # else: # 否则说明用户路径不是最短路径 + # self.servey_path_display() + def servey_path_display(self): + lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way = b_way[:-2] + lab2 = tk.Label(frame_left, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def check_user_greey(self): # 检查用户路径是不是贪心路径,不是的话返回错误点 + s.node_greedy()# 计算贪心路径 + if not len(s.ans): + return -2 + # 路径长度不一样肯定不匹配,直接返回 + if not len(self.user_way) == len(s.ans): + # self.auto_ways() + return 0 + way = [(int(node[1])-1) for node in d.Nodes] + result1 = list(map(str, self.user_way)) + result1 = ' '.join(result1) + ways = [way[an] for an in s.ans] + result2 = list(map(str, ways)) + result3 = ' '.join(result2) + result2.reverse() # 倒序排列列表元素 + result4 = ' '.join(result2) + if result4 == result1 or result3 == result1: + return -1 #-1表示是贪心路径 + return 1 + + + def show_greddy_ans(self, top): + lab1 = tk.Label(top, text="贪心路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + # 对用户路径进行标记与记录 + def way_route_markings(self, event): + global MARK_NODE + MARK_NODE = 2 + # E.newedge_del_all(1) + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + + left_window() + self.user_way = [0] + # lab = tk.Label(frame_left, text="路径操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + global entry1,entry2,default_value + # 定义默认值变量 + default_value = tk.StringVar() + default_value.set(self.user_way[-1]+1) + laba1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + laba1.place(relx=0.2, rely=0.28 + y0, width=140, height=30) + # 创建第一个输入框 + entry1 = tk.Entry(frame_left, textvariable=default_value, bg="#F5F5F5") + entry1.place(relx=0.6, rely=0.28 + y0, width=50, height=30) + labb1 = tk.Label(frame_left, text="当前下一节点:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + labb1.place(relx=0.2, rely=0.33 + y0, width=140, height=30) + # 创建第二个输入框 + entry2 = tk.Entry(frame_left, bg="#F5F5F5") + entry2.place(relx=0.6, rely=0.33 + y0, width=50, height=30) + + labb3 = tk.Label(frame_left, text="是否进行连接:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + labb3.place(relx=0.2, rely=0.4 + y0, width=140, height=30) + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[38], bg='white', command=lambda: self.extract_values()) # 确定 + button.place(relx=0.55, rely=0.38 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[36], bg='white', command=lambda: self.revoke()) # 撤销 + button2.place(relx=0.18, rely=0.46 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[37], bg='white', command=lambda: self.clear_all_paths()) # 清除路径 + button3.place(relx=0.5, rely=0.46 + y0) + button4 = tk.Button(frame_left, relief="flat", image=List_Image[42], bg='white', command=lambda: self.greedy_path()) # 判断路径是否为贪心路径 + button4.place(relx=0.18, rely=0.55 + y0) + button5 = tk.Button(frame_left, relief="flat", image=List_Image[43], bg='white', command=lambda: self.shortest_path()) # 判断路径是否为最短路径 + button5.place(relx=0.18, rely=0.62 + y0) + + self.user_path_display() + def extract_values(self): + global entry1, entry2, default_value + global MARK_NODE + MARK_NODE = 2 + value1 = entry1.get() # 获取第一个输入框的值 + value2 = entry2.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + E = 0 + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + for item in d.Edges: + if str_route == item[5]: + if item[6] < 100 and item[7] < 100: + if d.Edges[G.node_edge(value1-1, value2-1)][4]: + E = 2 + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + else: + if self.user_way[-1] == (value1-1): + E = 1 + global MODE + MODE = 1 + self.user_way.append(value2 - 1) + default_value.set(value2) + d.Edges[G.node_edge(value1-1, value2-1)][4] = True + entry1.config(textvariable=default_value) # 更新entry1的默认值 + entry2.delete(0, tk.END) + else: + E = 3 + tk.messagebox.askokcancel(title='错误', message='未连贯!') + break + print(E) + if E == 1: + G.draw() + self.user_path_display() + elif E == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def extract_ligature(self, n1, n2): + global MARK_NODE + MARK_NODE = 2 + no = G.way_node_edge(n1, n2) + flage = False + if d.Edges[no][4]: + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + elif d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + else: + if self.user_way[-1] == int(d.Nodes[n2][1]) - 1: + nx = n1 + n1 = n2 + n2 = nx + if self.user_way[-1] == int(d.Nodes[n1][1])-1: + global MODE + MODE = 1 + self.user_way.append(int(d.Nodes[n2][1]) - 1) + d.Edges[no][4] = True + flage = True + else: + tk.messagebox.askokcancel(title='错误', message='未连贯!') + if flage: + G.draw() + self.user_path_display() + + def revoke(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1+1}-{value2+1}' + else: + str_route = f'{value2+1}-{value1+1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 1 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2+1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + G.draw() + self.user_path_display() + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def clear_all_paths(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + for i in range(len(self.user_way)-1): + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1 + 1}-{value2 + 1}' + else: + str_route = f'{value2 + 1}-{value1 + 1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 3 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2 + 1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + for i in range(len(d.Edges)): + d.Edges[i][4] = False + G.draw() + self.user_path_display() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def greedy_path(self): + g = self.check_user_greey() + if g == -2: + # cvx.create_text(0, 0, text="不存在贪心路径") + lab1 = tk.Label(frame_left, text="不存在贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + elif g == -1: # 如果用户路径是贪心路径则显示 + # cvx.create_text(0, 0, text="用户路径是贪心路径") + lab1 = tk.Label(frame_left, text="用户路径是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + # elif g: + # self.servey_greedy_path() + else: + # cvx.create_text(0, 0, text="用户路径不是是贪心路径") + lab1 = tk.Label(frame_left, text="用户路径不是是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + def servey_greedy_path(self): + lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + lab2 = tk.Label(frame_left, text=f"{g_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + + + def shortest_path(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if not best: + lab1 = tk.Label(frame_left, text="不存在最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + else: + lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + # if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + # lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + # lab1.place(relx=0.1, rely=0.8, width=180, height=30) + # else: # 否则说明用户路径不是最短路径 + # self.servey_path_display() + + # 用户路径展示 + def user_path_display(self): + lab1 = tk.Label(frame_left, text="用户路径:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + lab1.place(relx=0.15, rely=0.8, width=80, height=30) + # self.auto_ways() + way = "" + # d_way = [] + # for i in d.Nodes: + # d_way.append(i[1]) + for i in range(len(self.user_way)): + if len(self.user_way)-i > 1: + way += str(self.user_way[i]+1) + way += "->" + else: + way += str(self.user_way[i]+1) + lab2 = tk.Label(frame_left, text=f"{way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.18, rely=0.84, width=300, height=100) + + def way_operate(self, event): + global MARK_NODE + for i in range(len(d.Edges)): + d.Edges[i][4] = False + MARK_NODE = 5 + # E.newedge_del_all(1) + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + self.user_way = [0] + # lab = tk.Label(frame_left, text="路径增加与删除", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + label1 = tk.Label(frame_left, text="节点1:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + label1.place(relx=0.15, rely=0.32 + y0, width=100, height=30) + global entry3, entry4 + # 创建第一个输入框 + entry3 = tk.Entry(frame_left, bg='#F5F5F5') + entry3.place(relx=0.6, rely=0.32 + y0, width=50, height=30) + + label2 = tk.Label(frame_left, text="节点2:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + label2.place(relx=0.15, rely=0.42 + y0, width=100, height=30) + # 创建第二个输入框 + entry4 = tk.Entry(frame_left, bg='#F5F5F5') + entry4.place(relx=0.6, rely=0.42 + y0, width=50, height=30) + + label3 = tk.Label(frame_left, text="是否进行两节点之间操作:", bg='white', fg=BLACK, font=('微软雅黑', 14)) + label3.place(relx=0.15, rely=0.5 + y0, width=240, height=30) + + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[33], bg='white', + command=lambda: self.way_add()) # 线路增加 + button.place(relx=0.2, rely=0.56 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[34], bg='white', + command=lambda: self.way_change()) # 线路修改 + button2.place(relx=0.2, rely=0.66 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[35], bg='white', + command=lambda: self.way_delete()) # 线路删除 + button3.place(relx=0.2, rely=0.76 + y0) + + def way_add(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + if d.drop.count(ways_list.index(value1)) > 4 or d.drop.count(ways_list.index(value2)) > 4: + print(d.drop) + tk.messagebox.askokcancel(title='提醒', message='当前节点已经到达最大连接数!') + return + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 1: + tk.messagebox.askokcancel(title='提醒', message='当前路线已存在!') + return + top = creat_window(str_route+'路径参数配置') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", 101) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", 101) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1,result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][3] = 1 + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_change(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + top = creat_window(str_route + '路径参数修改') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1, result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_delete(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + d.Edges[no][3] = 0 + d.Edges[no][6] = 111 + d.Edges[no][7] = 111 + G.draw() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + +class Node: + # 标记节点按钮 + def node_mark_display(self, event): + global MARK_NODE, Specify_Nodes + W.user_way = [0] + MARK_NODE = 1 + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + curx = -1 + y0 = 0.1 + for i in d.Nodes: + if i[2] == 1: + curx = i[0] + # lab = tk.Label(frame_left, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.32, width=200, height=30) + labx1 = tk.Label(frame_left, text=f"当前共有{len(d.Nodes)}个节点", bg='white', fg=BLACK, font=('微软雅黑', 20)) + labx1.place(relx=0.15, rely=0.3 + y0, width=260, height=30) + lab1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 20)) + if curx == -1: + text = "未选择" + else: + text = f"{d.Nodes[curx][1]}" + Specify_Nodes = str(d.Nodes[curx][1]) + lab2 = tk.Label(frame_left, text=text, bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 20)) + lab1.place(relx=0.15, rely=0.4 + y0, width=160, height=50) + lab2.place(relx=0.6, rely=0.4 + y0, width=100, height=50) + + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[30], bg='white', + command=lambda: self.node_del_exact()) # 删除当前节点 + button.place(relx=0.2, rely=0.5 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[32], bg='white', + command=lambda: self.node_add()) # 增加一个节点 + button2.place(relx=0.2, rely=0.6 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[31], bg='white', + command=lambda: self.node_del()) # 减少一个节点 + button3.place(relx=0.2, rely=0.7 + y0) + + def node_add(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + # 增加一个节点 + nodes = len(d.Nodes) + 1 + # 如果节点数目大于五,则将连接详细信息改为不显示 + if nodes > 5: + d.edgeinfo = 0 + if nodes > 10: + tk.messagebox.askokcancel(title='错误', message='已达到最大节点') + return + d = Data(1, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + + def node_del(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + if d.nodes_num <= 2: + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(2, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + def node_del_exact(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = d.Edges + if len(Specify_Nodes) == 3: + tk.messagebox.askokcancel(title='错误', message='未选择节点') + return + if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告 + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(3, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + +def refresh(): + node = random.randint(4, 10) + global d, MODE,MARK_NODE + MARK_NODE = 0 + MODE = 0 + d = Data(0, node) + print(node) + W.user_way=[0] + G.draw() + window(2) + +def graph_init(): + global MODE,MARK_NODE + MARK_NODE = 0 + MODE = 0 + G.draw() + window(2) + +def empty(): + global MARK_NODE + MARK_NODE = 0 + G.delete_all() + +def auto_opera_mu(menu: tk.Menu): + """TSP问题自动生成与自动求解相关""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init()) + menu_node.add_command(label="重新生成", command=lambda: refresh()) + menu_node.add_command(label="清空", command=lambda: empty()) + menu_node.add_command(label="自动求解最优路径-dfs遍历", command=lambda: s.auto_solve(3)) + menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: s.auto_solve(2)) + menu_node.add_command(label="自动求解优化路径-回溯", command=lambda: s.auto_solve(1)) + menu_node.add_command(label="检查是否存在路径", command=lambda: s.check()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node) + +# 创建弹出窗口 +def creat_window(title): + top = tk.Toplevel(root) + top.geometry("300x350") + top.title(title) + return top + +# 输入框 +def create_input_box(top, text, value): + box_label = tk.Label(top, text=text) + box_label.pack(padx=10, pady=10) + box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3 + box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象 + box_size_entry.pack(padx=20, pady=20) + return box_size_entry + +def dir2(x, y): # 计算两点间距离 + return (x[0] - y[0]) * (x[0] - y[0]) + (x[1] - y[1]) * (x[1] - y[1]) + +def left1(event): + global entry1, entry2, default_value + # 查找鼠标左键按下时位置是否在某个节点内 + n = -1 + for node in d.Nodes: + if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: + n = node[0] + # n为点击的节点,若没有则为-1 + m = mode.get() # 查看现在操作类型 + if m == 1 and not n == -1: + # 节点操作 + # N.node_mark(n) + if n >= len(d.Nodes): + # 弹出对话框 + tk.messagebox.askokcancel(title='错误', message='该节点不存在') + return + for node in d.Nodes: + if node[0] == n: + node[2] = 1 + else: + node[2] = 0 + G.draw() + if MARK_NODE == 1: # 限制节点操作 + N.node_mark_display(1) + elif MARK_NODE == 2: # 若是MARK_NODE==2,则可以进行路线画图操作 + global CLICK + print(14) + if CLICK[2] == 0: + CLICK[0] = n + default_value.set(d.Nodes[n][1]) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + print(15) + elif CLICK[2] == 1: + CLICK[1] = n + W.extract_ligature(CLICK[0], CLICK[1]) + CLICK[2] = -1 + CLICK[2] += 1 + print(n) + +def element_reading(): + global List_Image + folder_path = 'K:/work/traveler/background/' + image_path = ['1_红.png', '1_绿.png', '1_蓝.png', '2_红.png', '2_绿.png', '2_蓝.png', '3_红.png', '3_绿.png', '3_蓝.png', + '4_红.png', '4_绿.png', '4_蓝.png', '5_红.png', '5_绿.png', '5_蓝.png', '6_红.png', '6_绿.png', '6_蓝.png', + '7_红.png', '7_绿.png', '7_蓝.png', '8_红.png', '8_绿.png', '8_蓝.png', '9_红.png', '9_绿.png', '9_蓝.png', + '10_红.png', '10_绿.png', '10_蓝.png', '删除节点.png', '减少节点.png', '增加节点.png', '线路增加.png', + '线路修改.png', '线路删除.png', '撤销.png', '清除路径.png', '确认.png', '编组9.png', '背景.png', + '矩形.png', '判断路径是否为贪心路径.png', '判断路径是否为最短路径.png', '矩形2.png'] + for path in image_path[:30]: # 1-10球加载 + List_Image.append(element(folder_path+path, 50, 50)) + for path in image_path[30:36]: # 节点操作与连线操作功能按钮 + List_Image.append(element(folder_path + path, 280, 50)) + List_Image.append(element(folder_path + image_path[36], 140, 50)) # 撤销 + List_Image.append(element(folder_path + image_path[37], 140, 50)) # 清除路径 + List_Image.append(element(folder_path + image_path[38], 120, 50)) # 确认 + List_Image.append(element(folder_path + image_path[39], 380, 100)) # 编组_9 + List_Image.append(element(folder_path + image_path[40], 900, 900)) # 背景 + List_Image.append(element(folder_path + image_path[41], 380, 140)) # 小矩形 + List_Image.append(element(folder_path + image_path[42], 300, 50)) # 判断路径是否为贪心路径 + List_Image.append(element(folder_path + image_path[43], 300, 50)) # 判断路径是否为最短路径 + List_Image.append(element(folder_path + image_path[44], 380, 530)) # 大矩形 + +def element(path, width, height): + # 加载图元对应的图片文件 + img = Image.open(path) + # 使用resize方法调整图片 + img = img.resize((width, height)) + # 把Image对象转换成PhotoImage对象 + img = ImageTk.PhotoImage(img) + # 保存图片的引用,防止被垃圾回收 + root.img = img + return img + +def window(event): + global MARK_NODE + MARK_NODE = 0 + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + Label_1 = tk.Label(frame_left, text="遍历节点数目", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_1.place(x=50, y=330, width=180, height=40) + Label_2 = tk.Label(frame_left, text="求解花费时间", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_2.place(x=50, y=450, width=180, height=40) + Label_3 = tk.Label(frame_left, text="最短路径", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_3.place(x=50, y=570, width=140, height=40) + if event == 2: + lab1 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab1.place(x=80, y=370, width=300, height=60) + lab2 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(x=80, y=490, width=300, height=60) + lab3 = tk.Label(frame_left, text=f"{1}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab3.place(x=80, y=610, width=300, height=140) + else: + lab1 = tk.Label(frame_left, text=f"{s.way_sum}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab1.place(x=80, y=370, width=300, height=60) + t = round(s.end - s.start, 4) + lab2 = tk.Label(frame_left, text=f"{t}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(x=80, y=490, width=300, height=60) + g_way = '' + if len(s.ans) > 0: + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + else: + g_way='1' + lab3 = tk.Label(frame_left, text=f"{g_way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab3.place(x=80, y=610, width=300, height=140) + +def left_window(): + background_button = tk.Label(frame_left, image=List_Image[39], bg='#FFFAF0') # 编组 + background_button.place(x=50, y=20, width=380, height=100) + background_button.bind("", window) + # # 创建 Label 组件来显示背景图片 + background_on = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[41]) # 小矩形背景 + background_on.place(x=50, y=140, width=380, height=140) + + if MARK_NODE == 1: + background_color = [BLACK, '#DCDCDC', '#DCDCDC'] + elif MARK_NODE == 5: + background_color = ['#DCDCDC', BLACK, '#DCDCDC'] + elif MARK_NODE == 2: + background_color = ['#DCDCDC', '#DCDCDC', BLACK] + else: + background_color = ['#DCDCDC', '#DCDCDC', '#DCDCDC'] + Label_button1 = tk.Label(frame_left, text="节点操作", bg='white', fg=background_color[0], font=('微软雅黑', 15)) + Label_button1.place(x=60, y=180, width=80, height=50) + Label_button1.bind("", N.node_mark_display) + Label_button2 = tk.Label(frame_left, text="连接操作", bg='white', fg=background_color[1], font=('微软雅黑', 15)) + Label_button2.place(x=200, y=180, width=80, height=50) + Label_button2.bind("", W.way_operate) + Label_button3 = tk.Label(frame_left, text="路径操作", bg='white', fg=background_color[2], font=('微软雅黑', 15)) + Label_button3.place(x=340, y=180, width=80, height=50) + Label_button3.bind("", W.way_route_markings) + + background_below = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[44]) # 大矩形背景 + background_below.place(x=50, y=300, width=380, height=530) + +if __name__ == '__main__': + root = Tk() # 设置主界面 + root.title("旅行商问题") # 设置标题 + root.geometry("1400x900") # 设置大小 + + root.resizable(0, 0) # 设置不能调整显示边框 + element_reading() + frame_top = tk.Frame(root, bg='white', width=1400, height=50, highlightthickness=0) + frame_top.place(x=0, y=0) # 绘制信息输出栏 + + frame_left = tk.Frame(root, bg='#FFFAF0', highlightthickness=0, width=500, height=850) # 左边栏 + frame_left.place(x=0, y=50) # 左边栏 + + cv = tk.Canvas(root, bg='#FFFAF0', bd=0, relief="flat", width=900, height=850, highlightthickness=0) + cv.place(x=500, y=50) # 放置绘图Canvas画布 + + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) # 背景 + + G = Graph() + mode = tk.IntVar(value=1) + main_menu = tk.Menu(root) + root.config(menu=main_menu) + cv.bind('', left1) + E = Edge() + W = Ways() + s = Solve() + N = Node() + auto_opera_mu(main_menu) + window(2) + G.lab_show(frame_top, 0, 0, 0) # 显示边信息 + root.mainloop() diff --git a/X4_3.py b/X4_3.py new file mode 100644 index 0000000..e73b935 --- /dev/null +++ b/X4_3.py @@ -0,0 +1,1730 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X4.py +from tkinter import Tk +import random +import math +import time +import tkinter as tk +from tkinter import * +from tkinter import ttk +from tkinter import messagebox +import numpy as np +import networkx as nx +from PIL import Image, ImageTk + +MODE = 0 +BLUE = "#0080FF" +BLACK = "#000000" +RED = "#FFAAAA" +YELLOW = "#FFFACD" +LINE = '#c8d2c8' +GREY = '#070b19' +GREEN = '#5ba585' +NODE = '#33a8cd' +ZERO = 'gold' +MARK_NODE = 0 +Specify_Nodes = '' +Edges_list = [] +NAME = [] +CLICK = [0, 0, 0] + +List_Image = [] + +class Data: + def __init__(self, source: int, num: int, mold: int): + global NAME + if num <= 0 or num > 10: + raise ValueError("num must be a positive integer") + self.styple = mold + self.edgeinfo = 1 # 是否显示详情 + self.nodes_num = num # 节点个数 + self.ang = 360 / self.nodes_num # 圆心顶点角度 + self.R = 300 # 外接圆半径 + self.bc = 2 * self.R * math.sin(math.pi / self.nodes_num) # 节点之间的正多边形距离 + self.canvas_len = int(2 * self.R + 80) # 画布边长 + self.center = ((self.canvas_len+220) // 2, (self.canvas_len+230) // 2) # 画布中心点坐标 + self.drop = [] + self.index = 0 + if source == 3 or source == 2: # 指定删除 + # name = [str(i) for i in range(1, num + 2)] + # print(name) + if source == 2: + NAME.pop() + else: + # print(Specify_Nodes) + NAME.remove(Specify_Nodes) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + # print(self.Nodes) + self.Edges = self.edges_delete(source) # 创建第一条连接 + else: + if source == 1: + name = str(int(NAME[-1])+1) + # print(name) + NAME.append(name) + else: + NAME = [str(i) for i in range(1, num + 1)] + # print(NAME) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + # print(self.Nodes) + self.Edges = self.edges_creat(source) # 创建第一条连接 + self.edge_add(2, source) # 创建第2条连接 + self.edge_add(3, source) # 创建第3条连接 + + def nodes_creat(self, n_sum=None): + if n_sum == None: + n_sum = self.nodes_num + nodes = [] # 初始化node表 + # 设置画布中心点坐标x0,y0 + x0 = self.center[0] + y0 = self.center[1] + for i in range(n_sum): # 通过几何运算得到多边形各个顶点坐标 + rad = math.radians(i * self.ang) # 计算第i个点的弧度 + x = int(math.cos(rad) * self.R) # 计算第i个顶点x坐标 + y = int(math.sin(rad) * self.R) # 计算第i个顶点y坐标 + # name = '' + str(i + 1) # 给第i个顶点命名 + name = '' + self.name[i] + mark = 0 # 节点为未标记 + dominator = 1 # 设置为必经节点 + nodes.append([i, name, mark, (x0 + x, y0 + y), dominator]) # 将当前节点加入node中 + return nodes + + def edges_delete(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 + # print(str(int(Specify_Nodes) + 1)) + for list_edge in Edges_list: + # print(list_edge) + edges.append([ser, list_edge[1], list_edge[2],list_edge[3], list_edge[4],list_edge[5], list_edge[6], + list_edge[7], list_edge[8]]) # 将连接加入到边的集合中 + nox = [int(num) for num in edges[ser][5].split('-')] + # print(nox) + if len(nox) == 2: + ss = edges[ser][5] + else: + ss = edges[ser][5][:-2] + # print(ss) + if source == 2: + if str(len(self.Nodes)+1) in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + else: + if Specify_Nodes in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + ser += 1 + return edges + + def edges_creat(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 # 初始化链接对象编号 + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + # print(nodes1) + self.drop = [] + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + # print(nodes1) + for node1 in nodes1: # 遍历删除后的节点信息 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + if self.styple == 1: + dist_c = random.randint(1, 99) # 随机生成距离成本 + time_c = random.randint(1, 99) # 随机生成链接的时间 + else: + if self.drop.count(n2) > 3 or self.drop.count(n1) > 3: + dist_c = random.randint(101, 120) # 随机生成距离成本 + time_c = random.randint(101, 120) # 随机生成链接的时间 + else: + dist_c = random.randint(10, 120) # 随机生成距离成本 + time_c = random.randint(10, 120) # 随机生成链接的时间 + seq = 1 # 链接序号 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}" # 链接标签 + enable = 1 # 连接是否可用 + if dist_c >= 100 or time_c >= 100: # 设置某些节点为不显示 + enable = 0 + else: + self.drop.append(n1) + self.drop.append(n2) + if source == 1: + # print(n2, len(self.Nodes)) + if n2 == len(self.Nodes)-1: + # print('进来了') + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + else: + # print(self.index) + edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5],Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + self.index += 1 + else: + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + ser += 1 # 计数加一 + return edges + + def edge_add(self, no, source): + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + for node1 in nodes1: # 遍历删除后的节点信息 + ser = len(self.Edges) # 链接序号 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + show = 0 # 设置为不显示 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}-{no}" # 链接标签 + dist_c = random.randint(30, 100) # 距离成本 + time_c = random.randint(1, 30) # 链接的时间 + enable = no # 连接是否可用 + if source == 1: + if n2 == len(self.Nodes)-1: + # print('进来了') + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) # 将连接加入到边的集合中 + else: + # print(no ,self.index) + self.Edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5], Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + + self.index += 1 + else: + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) + + def coord_creat(self): + '''返回每个节点的坐标''' + coordinate = [] + x0 = self.center[0] + y0 = self.center[1] + for i in range(self.nodes_num): + rad = math.radians(i * self.ang) + x = int(math.cos(rad) * self.R) + y = int(math.sin(rad) * self.R) + coordinate.append((x0 + x, y0 + y)) + return coordinate + + def node_co(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[3] + + def node_no(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[0] + def node_coordinate(self, n): # 通过节点编号返回矩阵的坐标 + result = [] + for i in range(n): + for j in range(i + 1, n): + result.append((i, j)) + return result + +# G = Graph() +# print(1) +nodes = 6 # 设置初始节点数量 +d = Data(0, nodes, 0) + +class Graph: + # Graph定义 + def edge_info(self, cv: tk.Canvas, info: str, n1: tuple, n2: tuple): # 显示路径信息 + if d.edgeinfo == 0: # 若设置为不显示则直接返回 + return + # print(n1) + # print(n2) + x = (n1[0] - n2[0]) // 2 + n2[0] # 计算中点x坐标 + y = (n1[1] - n2[1]) // 2 + n2[1] # 计算中点y坐标 + if (n2[1] - n1[1]) != 0: # 斜率为非0度时的显示 + reat = math.atan((n2[0] - n1[0]) / (n2[1] - n1[1])) # 根据斜率计算弧度 + ang = round(math.degrees(reat)) + 90 # 根据弧度转角度 + if ang > 89: # 斜度大于90度调整坐标 + text_x, text_y = x + 15, y + else: # 斜度大于90度调整坐标 + text_x, text_y = x, y + 15 + else: # 斜率为0度时的显示 + ang = round(math.degrees(0)) # 根据弧度转角度 + text_x, text_y = x - 30, y + 10 + # print(info.split(' ')[-1]) + cv.create_text(text_x, text_y, text=f'{info.split()[-2]}', fill="black", font=('微软雅黑', 12), angle=ang) # 根据信息显示文字 + + def draw(self): + cv.delete('all') # 清空画布 + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) + dis, tim = self.edges_show(cv, d.Edges) # 绘制边 + self.lab_show(frame_top, len(d.Nodes), tim, dis) # 显示边信息 + self.nodes_show(cv) # 绘制点 + cv.update() # 更新画布 + + + def lab_show(self, frame: tk.Frame, nodes: int, time: int, distance: int): + labs = ["节点数目", "时间", "距离(成本)"] + x0 = d.canvas_len // 3 + for i in range(len(labs)): + lab1 = tk.Label(frame, text=labs[i], fg=BLACK, bg='white', font=('微软雅黑', 12)) + if i == 0: + lab2 = tk.Label(frame, text=f"{nodes}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + elif i == 1: + lab2 = tk.Label(frame, text=f"{time}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + else: + lab2 = tk.Label(frame, text=f"{distance}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=10 + x0 * i, y=10, width=80, height=30) + lab2.place(x=10 + x0 * i + 80, y=10, width=80, height=30) + lab1 = tk.Label(frame, text='分钟', fg=BLACK, bg='white', font=('微软雅黑', 12)) + lab1.place(x=170 + x0, y=10, width=50, height=30) + lab1 = tk.Label(frame, text='千米', fg=BLACK, bg='white', font=('微软雅黑', 12)) + lab1.place(x=170 + x0 * 2, y=10, width=50, height=30) + + def nodes_show(self, cv: tk.Canvas): + COL = NODE # 初始化节点颜色 + for i in d.Nodes: # 遍历每一个节点 + x, y = i[3] # 记录该节点坐标 + if i[2] == 1 or i[2] == 2: # 如果节点被标记,则将节点标为红色 + COL = RED + elif i[0] == 0: # 如果节点为起始节点,则将节点标记为特殊颜色 + COL = ZERO + elif i[2] == 5: # 如果节点已经被访问,则将节点标记(用户路径功能) + COL = 'gold' + else: # 否则则使用初始颜色 + COL = NODE + if i[4] == 1: # 如果是必经节点,则加黑色边框 + w = 1 + else: # 如果是非必经节点,则没有边框 + w = 0 + # 根据坐标,圆的颜色与是否有边框绘制圆圈来表示节点 + cv.create_oval(x - 20, y - 20, x + 20, y + 20, fill=COL, width=w, ) # outline='#ee28a3') + cv.create_text(x, y, text=f'{i[1]}', fill=GREY, font=('华文楷体', 18)) + cv.update() + + def edges_show(self, cv: tk.Canvas, edges: d.Edges): + """ + 显示链接 + :param cv: tkcanvas对象 + :return: + """ + distance = 0 # 初始化被标记的线的总路径 + time1 = 0 # 初始化被标记的线的总时间 + for edge in edges: + node_1 = d.node_co(edge[1]) # 链接线编号对应的坐标1 + node_2 = d.node_co(edge[2]) # 链接线编号对应的坐标2 + # print(node_1,node_2) + if edge[8] == 2 and edge[3] == 1: # 若该链接为两点间第二条连线 + self.more_edge_show(2, edge) + elif edge[8] == 3 and edge[3] == 1: # 若为两点间第3条连线 + self.more_edge_show(3, edge) + if edge[8] == 1 and edge[3] == 1: # 若为两点间第1条连线且该连接存在 + self.edge_info(cv, f'{edge[5]} {edge[6]} {edge[7]}', node_1, node_2) + point = [node_1, node_2] + if edge[4] == 1 and edge[8] == 1 and edge[3] == 1: # 如果路径被标记,则用绿色绘制 + if MODE == 1: + cv.create_line(point, fill=RED, width=4) + elif MODE == 2: + cv.create_line(point, fill=BLUE, width=4) + else: + cv.create_line(point, fill=GREEN, width=4) + distance += edge[6] # 更新总路径 + time1 += edge[7] # 更新总时间 + elif edge[8] == 1 and edge[3] == 1: # 如果是节点之间的第一条线,则用正常颜色标记 + cv.create_line(point, fill=LINE, width=2) + if d.Nodes[d.node_no(edge[2])][2] + d.Nodes[d.node_no(edge[2])][2] == 3: # 若该线被用户选中,则用蓝色虚线标记 + cv.create_line(point, fill=BLUE, width=5, dash=(4, 14)) + return (distance, time1) # 返回标记路径的总时间与路程 + + # def node_edge(self, a, b): # 根据点的编号找边的编号 + # if a > b: + # x = a + # a = b + # b = x + # return int(a * (2 * len(d.Nodes) - a - 3) / 2 + b - 1) + + def node_edge(self, a, b): # 根据点的编号找边的编号 + for edge in d.Edges: + if a == edge[1] and b == edge[2]: + return edge[0] + if b == edge[1] and a == edge[2]: + return edge[0] + + def way_node_edge(self, a, b): # 根据点的编号找边的编号 + way = [] + for node in d.Nodes: + way.append(int(node[1])) + # print(way) + # print(a, b) + x, y = way[a]-1, way[b]-1 + for edge in d.Edges: + if x == edge[1] and y == edge[2]: + return edge[0] + if y == edge[1] and x == edge[2]: + return edge[0] + + def delete_all(self): + cv.delete('all') # 清空画布 + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) + self.lab_show(frame_top, 0, 0, 0) # 显示边信息 + W.user_way = [0] + window(2) + +class Edge: + def __init__(self): + self.mark = [] + # 记录已标记的节点数目(连接操作) + self.user_cur = 0 + # 记录用户所在点 + + def node_edge(self, no, n1, n2): # 根据节点返回编号 + if n1 > n2: + n = n1 + n1 = n2 + n2 = n + for e in d.Edges: + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 + def edge_del_all(self, mode): + # mode = 1:优先最短路径 + # mode = 2:优先最短时间 + # cur1 = node_edge() + if mode == 1: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_cost(e[1], e[2]) + else: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_time(e[1], e[2]) + G.draw() + + def edge_merge_cost(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][6]), (cur2, d.Edges[cur2][6]), (cur3, d.Edges[cur3][6])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + def edge_merge_time(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][7]), (cur2, d.Edges[cur2][7]), (cur3, d.Edges[cur3][7])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + +class Solve: + def __init__(self): + self.ans = [] # 记录答案 + self.mutians = [] # 若有多个最短路,则保存在mutians中 + self.way_sum = 0 # 记录遍历的路径数目 + self.minn = float("inf") # 初始化最小值为正无穷 + self.start = 0 # 起始点 + self.end = 0 # 终点 + self.mark = [] # 记录该点是否已经经过 + self.user_cur = 0 # 记录用户所在点 + self.cura = -1 # 记录用户选择的第一个点 + self.curb = -1 # 记录用户选择的第二个点 + self.con = 0 # 记录必经的节点数目 + + def node_greedy(self): # 贪心 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[3] == 1: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.greedy(distances) + self.ans = ways.copy() + # 如果找到的路径不合法,则表示贪心法无法求解得到合法路径 + print(self.way_sum) + # for i in distances: + # print(i) + if len(ways) != l+1 : + tk.messagebox.askokcancel(title='结果', message='该图不存在贪心算法路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + self.ans.clear() + self.end = time.time() # 记录结束时间 + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + # print(ways[len(ways) - 1], i) + # d.Edges[G.way_node_edge(ways[len(ways) - 1], i)][4] = True # 标记起点终点形成环线 + # print(ways) + tk.messagebox.askokcancel(title='结果', message='贪心算法路径') + self.end = time.time() # 记录结束时间 + def greedy(self,distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + next_city = city + self.way_sum += 1 # 遍历路径的数目加一 + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + # print(path) + return path + + def tsp_backtracking(self): + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[3] == 1: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.backtracking(distances) + print(self.way_sum) + + if ways: # 判断路径是否存在 + self.ans = ways.copy() + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='回溯算法路径') + self.end = time.time() # 记录结束时间 + else: + tk.messagebox.askokcancel(title='结果', message='该图不存在回溯算法路径') + self.end = time.time() # 记录结束时间 + return -1 + + def backtracking(self, distances): + num_cities = len(distances) + path = [0] # 起始城市为0 + visited = [False] * num_cities + visited[0] = True + min_distance = float('inf') + shortest_path = None + + def backtrack(curr_city, curr_distance, visited, path): + nonlocal min_distance, shortest_path + if len(path) == num_cities: + # 到达所有城市,更新最短路径 + if curr_distance + distances[curr_city][0] < min_distance: + min_distance = curr_distance + distances[curr_city][0] + shortest_path = path + [0] + else: + for next_city in range(num_cities): + if not visited[next_city]: + # 选择下一个未访问的城市 + visited[next_city] = True + path.append(next_city) + new_distance = curr_distance + distances[curr_city][next_city] + self.way_sum += 1 # 遍历路径的数目加一 + # 剪枝条件:当前路径已经大于最短路径,不继续搜索 + if new_distance < min_distance: + backtrack(next_city, new_distance, visited, path) + # 回溯 + visited[next_city] = False + path.pop() + + backtrack(0, 0, visited, path) + return shortest_path + + def auto_solve(self, number): + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + if number == 1: # 回溯 + self.tsp_backtracking() + elif number == 2: + self.node_greedy() # 贪心 + else: # 深度优先算法 + self.node_dfs() + window(1) + G.draw() + + def node_dfs(self): # 遍历 深度优先算法 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[3] == 1: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + cost, ways = self.dfs(distances) + print(self.way_sum) + self.ans = ways.copy() + if len(ways) != l + 1: + tk.messagebox.askokcancel(title='结果', message='该图不存在dfs路径') + # print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + self.end = time.time() # 记录结束时间 + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='dfs算法路径') + self.end = time.time() # 记录结束时间 + + + def dfs_tsp(self,graph, start, current, path, + visited, cost, min_cost, min_path): + if len(path) == len(graph) and graph[ + current][start] != np.inf: + path.append(start) + cost += graph[current][start] + if cost < min_cost[0]: + min_cost[0] = cost + min_path[0] = path.copy() + path.pop() + cost -= graph[current][start] + return + for next_node in range(len(graph)): + if graph[current][next_node] != np.inf and \ + not visited[next_node]: + visited[next_node] = True + path.append(next_node) + cost += graph[current][next_node] + self.way_sum += 1 # 遍历路径的数目加一 + self.dfs_tsp(graph, start, next_node, path, + visited, cost, min_cost, min_path) + visited[next_node] = False + path.pop() + cost -= graph[current][next_node] + def dfs(self, graph): + n = len(graph) + min_cost = [float('inf')] + min_path = [[]] + + for start_node in range(n): + visited = [False] * n + path = [start_node] + cost = 0 + visited[start_node] = True + self.dfs_tsp(graph, start_node, start_node, path, + visited, cost, min_cost, min_path) + return min_cost[0], min_path[0] + + def find_hamiltonian_cycles(self,start, distances): + n = len(distances) + path = [start] + cycles = [] + + def is_valid(node, position): + if distances[path[position - 1]][node] == np.inf: + return False + if node in path: + return False + return True + def find_paths(node): + for next_node in range(n): + if is_valid(next_node, len(path)): + path.append(next_node) + if len(path) < n: + find_paths(next_node) + elif len(path) == n and distances[path[-1]][start] != np.inf: + cycles.append(path + [start]) + path.pop() + find_paths(start) + return cycles + def check(self): + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 将路径中的 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + all_hamiltonian_cycles = self.find_hamiltonian_cycles(0, np.array(distances)) + l = len(all_hamiltonian_cycles) + if l: + for cycles in all_hamiltonian_cycles: + dis_ans = 0 + for i in range(d.nodes_num): + dis_ans += d.Edges[G.way_node_edge(cycles[i], cycles[i + 1])][6] + print(cycles,dis_ans) + if all_hamiltonian_cycles: + tk.messagebox.askokcancel(title='结果', message=f'该图存在 {len(all_hamiltonian_cycles)} 条路径\n') + else: + tk.messagebox.askokcancel(title='结果', message='该图不能完全互通') + +def node_edge(n1, n2, no=1): # 根据点的编号找边的编号 + if n1 > n2: # 为了防止充分编号,让n1小于n2 + n1, n2 = n2, n1 + for e in d.Edges: # 遍历所有路径,找到符合要求的返回 + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 # 若找不到,则返回-1 + +class Ways(): + def __init__(self): + self.user_way = [0] # 记录用户路径 + self.user_cur = 0 # 记录用户所在点 + self.user_flag = set() # 记录用户已经过的点 + def auto_ways(self): # 标记用户路径 + for w in d.Edges: # 清空路径标记 + w[4] = 0 + for N in d.Nodes: # 清空节点标记 + N[2] = 0 + for w in self.user_way: # 标记用户已选的点 + d.Nodes[w][2] = 5 + # 标记用户经过的路径 + for w in range(len(self.user_way) - 1): + d.Edges[G.node_edge(self.user_way[w],self.user_way[w + 1])][4] = 1 + # 如果已经访问所有节点,自动回到初始节点 + if len(set(self.user_way)) == len(d.Nodes): + d.Edges[G.node_edge(self.user_way[len(self.user_way) - 1], 0)][4] = 1 + G.draw() # 按照标记重新绘制图形 + + def check_user_best(self): # 检查用户路径是不是最佳路径 + s.node_dfs() + if not len(s.ans): + return -1, 0 + # 长度不一样肯定不匹配 + if not len(self.user_way) == len(s.ans): + return 0, 0 + dis_user = 0 + dis_ans = 0 + num = len(self.user_way) + for i in range(num - 1): + dis_user += d.Edges[G.node_edge(self.user_way[i], self.user_way[i+1])][6] + dis_ans += d.Edges[G.way_node_edge(s.ans[i], s.ans[i + 1])][6] + return dis_ans, dis_user + + def show_best_ans(self, top, best, user): + lab1 = tk.Label(top, text="最短路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + # print(s.ans) + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + lab2 = tk.Label(top, text=f"最佳路径总成本:{best}\n用户路径总成本:{user}", bg=BLUE, fg=BLACK, + font=('微软雅黑', 12), wraplength=200, justify='left') + lab2.place(relx=0.4, rely=0.7 + y0, width=200, height=100) + + def show_BEST(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + + if not best: + lab1 = tk.Label(frame_left, text="不存在最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + else: + lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + # else: # 否则说明用户路径不是最短路径 + # self.servey_path_display() + def servey_path_display(self): + lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way = b_way[:-2] + lab2 = tk.Label(frame_left, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def check_user_greey(self): # 检查用户路径是不是贪心路径,不是的话返回错误点 + s.node_greedy()# 计算贪心路径 + if not len(s.ans): + return -2 + # 路径长度不一样肯定不匹配,直接返回 + if not len(self.user_way) == len(s.ans): + # self.auto_ways() + return 0 + way = [(int(node[1])-1) for node in d.Nodes] + result1 = list(map(str, self.user_way)) + result1 = ' '.join(result1) + ways = [way[an] for an in s.ans] + result2 = list(map(str, ways)) + result3 = ' '.join(result2) + result2.reverse() # 倒序排列列表元素 + result4 = ' '.join(result2) + if result4 == result1 or result3 == result1: + return -1 #-1表示是贪心路径 + else: + x_1 = self.find_longest(result1, result4) + x_2 = self.find_longest(result1, result3) + return max(x_1, x_2) + + def find_longest(self, list1, list2): + index = 0 + while index < len(list1) and index < len(list2): + if list1[index] != list2[index]: + break + index += 1 + return index + + + def show_greddy_ans(self, top): + lab1 = tk.Label(top, text="贪心路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + # 对用户路径进行标记与记录 + def way_route_markings(self, event): + global MARK_NODE + MARK_NODE = 2 + # E.newedge_del_all(1) + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + + left_window() + self.user_way = [0] + # lab = tk.Label(frame_left, text="路径操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + global entry1,entry2,default_value + # 定义默认值变量 + default_value = tk.StringVar() + default_value.set(self.user_way[-1]+1) + laba1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + laba1.place(relx=0.2, rely=0.28 + y0, width=140, height=30) + # 创建第一个输入框 + entry1 = tk.Entry(frame_left, textvariable=default_value, bg="#F5F5F5") + entry1.place(relx=0.6, rely=0.28 + y0, width=50, height=30) + labb1 = tk.Label(frame_left, text="当前下一节点:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + labb1.place(relx=0.2, rely=0.33 + y0, width=140, height=30) + # 创建第二个输入框 + entry2 = tk.Entry(frame_left, bg="#F5F5F5") + entry2.place(relx=0.6, rely=0.33 + y0, width=50, height=30) + + labb3 = tk.Label(frame_left, text="是否进行连接:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + labb3.place(relx=0.2, rely=0.4 + y0, width=140, height=30) + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[38], bg='white', command=lambda: self.extract_values()) # 确定 + button.place(relx=0.55, rely=0.38 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[36], bg='white', command=lambda: self.revoke()) # 撤销 + button2.place(relx=0.18, rely=0.46 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[37], bg='white', command=lambda: self.clear_all_paths()) # 清除路径 + button3.place(relx=0.5, rely=0.46 + y0) + button4 = tk.Button(frame_left, relief="flat", image=List_Image[42], bg='white', command=lambda: self.greedy_path()) # 判断路径是否为贪心路径 + button4.place(relx=0.18, rely=0.55 + y0) + button5 = tk.Button(frame_left, relief="flat", image=List_Image[43], bg='white', command=lambda: self.shortest_path()) # 判断路径是否为最短路径 + button5.place(relx=0.18, rely=0.62 + y0) + + self.user_path_display() + def extract_values(self): + global entry1, entry2, default_value + global MARK_NODE + MARK_NODE = 2 + value1 = entry1.get() # 获取第一个输入框的值 + value2 = entry2.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + E = 0 + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + for item in d.Edges: + if str_route == item[5]: + if item[6] < 100 and item[7] < 100: + if d.Edges[G.node_edge(value1-1, value2-1)][4]: + E = 2 + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + else: + if self.user_way[-1] == (value1-1): + E = 1 + global MODE + MODE = 1 + self.user_way.append(value2 - 1) + default_value.set(value2) + d.Edges[G.node_edge(value1-1, value2-1)][4] = True + entry1.config(textvariable=default_value) # 更新entry1的默认值 + entry2.delete(0, tk.END) + else: + E = 3 + tk.messagebox.askokcancel(title='错误', message='未连贯!') + break + print(E) + if E == 1: + G.draw() + self.user_path_display() + elif E == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def extract_ligature(self, n1, n2): + global MARK_NODE + MARK_NODE = 2 + no = G.way_node_edge(n1, n2) + flage = False + if d.Edges[no][4]: + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + elif d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + else: + if self.user_way[-1] == int(d.Nodes[n2][1]) - 1: + nx = n1 + n1 = n2 + n2 = nx + if self.user_way[-1] == int(d.Nodes[n1][1])-1: + global MODE + MODE = 1 + self.user_way.append(int(d.Nodes[n2][1]) - 1) + d.Edges[no][4] = True + flage = True + else: + tk.messagebox.askokcancel(title='错误', message='未连贯!') + if flage: + G.draw() + self.user_path_display() + + def revoke(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1+1}-{value2+1}' + else: + str_route = f'{value2+1}-{value1+1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 1 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2+1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + G.draw() + self.user_path_display() + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def clear_all_paths(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + for i in range(len(self.user_way)-1): + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1 + 1}-{value2 + 1}' + else: + str_route = f'{value2 + 1}-{value1 + 1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 3 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2 + 1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + for i in range(len(d.Edges)): + d.Edges[i][4] = False + G.draw() + self.user_path_display() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def greedy_path(self): + g = self.check_user_greey() + if g == -2: + # cvx.create_text(0, 0, text="不存在贪心路径") + lab1 = tk.Label(frame_left, text="不存在贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + elif g == -1: # 如果用户路径是贪心路径则显示 + # cvx.create_text(0, 0, text="用户路径是贪心路径") + lab1 = tk.Label(frame_left, text="用户路径是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + # elif g: + # self.servey_greedy_path() + else: + # print(self.user_way) + # print(self.user_way[0:g]) + # cvx.create_text(0, 0, text="用户路径不是是贪心路径") + lab1 = tk.Label(frame_left, text="用户路径不是是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + + def servey_greedy_path(self): + lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + lab2 = tk.Label(frame_left, text=f"{g_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + + + def shortest_path(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if not best: + lab1 = tk.Label(frame_left, text="不存在最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + else: + lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + # if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + # lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + # lab1.place(relx=0.1, rely=0.8, width=180, height=30) + # else: # 否则说明用户路径不是最短路径 + # self.servey_path_display() + + # 用户路径展示 + def user_path_display(self): + lab1 = tk.Label(frame_left, text="用户路径:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + lab1.place(relx=0.15, rely=0.8, width=80, height=30) + # self.auto_ways() + way = "" + # d_way = [] + # for i in d.Nodes: + # d_way.append(i[1]) + for i in range(len(self.user_way)): + if len(self.user_way)-i > 1: + way += str(self.user_way[i]+1) + way += "->" + else: + way += str(self.user_way[i]+1) + lab2 = tk.Label(frame_left, text=f"{way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.18, rely=0.84, width=300, height=100) + + def way_operate(self, event): + global MARK_NODE + for i in range(len(d.Edges)): + d.Edges[i][4] = False + MARK_NODE = 5 + # E.newedge_del_all(1) + for w in frame_left.winfo_children(): + w.destroy() + # 清除左侧组件 + left_window() + self.user_way = [0] + y0 = 0.1 + label1 = tk.Label(frame_left, text="节点1:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + label1.place(relx=0.15, rely=0.32 + y0, width=100, height=30) + global entry3, entry4 + # 创建第一个输入框 + entry3 = tk.Entry(frame_left, bg='#F5F5F5') + entry3.place(relx=0.6, rely=0.32 + y0, width=50, height=30) + + label2 = tk.Label(frame_left, text="节点2:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + label2.place(relx=0.15, rely=0.37 + y0, width=100, height=30) + # 创建第二个输入框 + entry4 = tk.Entry(frame_left, bg='#F5F5F5') + entry4.place(relx=0.6, rely=0.37 + y0, width=50, height=30) + button1 = tk.Button(frame_left, text='完全图', relief="flat", bg='#00BFFF', fg=BLACK, + font=('微软雅黑', 15), command=lambda: self.complete_graph()) # 完全图 + button1.place(relx=0.2, rely=0.43 + y0, width=100, height=30) + button2 = tk.Button(frame_left, text='可达判断', relief="flat", bg='#00BFFF', fg=BLACK, + font=('微软雅黑', 15), command=lambda: self.reachable_judgment()) # 节点可达性判断 + button2.place(relx=0.6, rely=0.43 + y0, width=100, height=30) + + + label3 = tk.Label(frame_left, text="是否进行两节点之间操作:", bg='white', fg=BLACK, font=('微软雅黑', 14)) + label3.place(relx=0.15, rely=0.5 + y0, width=240, height=30) + # 按钮 + button3 = tk.Button(frame_left, relief="flat", image=List_Image[33], bg='white', + command=lambda: self.way_add()) # 线路增加 + button3.place(relx=0.2, rely=0.56 + y0) + button4 = tk.Button(frame_left, relief="flat", image=List_Image[34], bg='white', + command=lambda: self.way_change()) # 线路修改 + button4.place(relx=0.2, rely=0.66 + y0) + button5 = tk.Button(frame_left, relief="flat", image=List_Image[35], bg='white', + command=lambda: self.way_delete()) # 线路删除 + button5.place(relx=0.2, rely=0.76 + y0) + + def is_reachable(self, graph, start, target, visited): + if start == target: + return True + visited.add(start) + for neighbor in graph[start]: + if neighbor not in visited: + if self.is_reachable(graph, neighbor, target, visited): + return True + return False + + def complete_graph(self): + l = len(d.Nodes) + result = d.node_coordinate(l) + for index in range(len(result)): + if d.Edges[index][3] == 0: + dist_c = random.randint(10, 99) # 随机生成距离成本 + time_c = random.randint(10, 99) # 随机生成链接的时间 + d.Edges[index][3] = 1 + d.Edges[index][6] = dist_c + d.Edges[index][7] = time_c + G.draw() + + + def reachable_judgment(self): + try: + a = int(entry3.get()) # 获取第一个输入框的值 + b = int(entry4.get()) # 获取第二个输入框的值 + l = len(d.Nodes) + index_list = [] + for i in d.Nodes: + index_list.append(i[1]) + dictionary = {i: [] for i in range(1, l + 1)} + result = d.node_coordinate(l) + # 生成无向图 + for i in range(len(result)): + if d.Edges[i][3] == 1: + x = d.Edges[i][5].split('-')[0] + y = d.Edges[i][5].split('-')[-1] + dictionary[index_list.index(x)+1].append(index_list.index(y)+1) + dictionary[index_list.index(y)+1].append(index_list.index(x)+1) + start_node = index_list.index(str(a))+1 + target_node = index_list.index(str(b))+1 + visited_nodes = set() + print(dictionary) + flag = self.is_reachable(dictionary, start_node, target_node, visited_nodes) + if flag: + tk.messagebox.askokcancel(title='结果', message=f'{a}到{b}可达') + else: + tk.messagebox.askokcancel(title='结果', message=f'{a}到{b}不可达') + except ValueError: + tk.messagebox.askokcancel(title='结果', message='请输入节点') + + def way_add(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + if d.drop.count(ways_list.index(value1)) > 4 or d.drop.count(ways_list.index(value2)) > 4: + print(d.drop) + tk.messagebox.askokcancel(title='提醒', message='当前节点已经到达最大连接数!') + return + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 1: + tk.messagebox.askokcancel(title='提醒', message='当前路线已存在!') + return + top = creat_window(str_route+'路径参数配置') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", 101) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", 101) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1,result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][3] = 1 + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_change(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + top = creat_window(str_route + '路径参数修改') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1, result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_delete(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + d.Edges[no][3] = 0 + d.Edges[no][6] = 111 + d.Edges[no][7] = 111 + G.draw() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + +class Node: + # 标记节点按钮 + def node_mark_display(self, event): + global MARK_NODE, Specify_Nodes + W.user_way = [0] + MARK_NODE = 1 + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + curx = -1 + y0 = 0.1 + for i in d.Nodes: + if i[2] == 1: + curx = i[0] + # lab = tk.Label(frame_left, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.32, width=200, height=30) + labx1 = tk.Label(frame_left, text=f"当前共有{len(d.Nodes)}个节点", bg='white', fg=BLACK, font=('微软雅黑', 20)) + labx1.place(relx=0.15, rely=0.3 + y0, width=260, height=30) + lab1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 20)) + if curx == -1: + text = "未选择" + else: + text = f"{d.Nodes[curx][1]}" + Specify_Nodes = str(d.Nodes[curx][1]) + lab2 = tk.Label(frame_left, text=text, bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 20)) + lab1.place(relx=0.15, rely=0.4 + y0, width=160, height=50) + lab2.place(relx=0.6, rely=0.4 + y0, width=100, height=50) + + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[30], bg='white', + command=lambda: self.node_del_exact()) # 删除当前节点 + button.place(relx=0.2, rely=0.5 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[32], bg='white', + command=lambda: self.node_add()) # 增加一个节点 + button2.place(relx=0.2, rely=0.6 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[31], bg='white', + command=lambda: self.node_del()) # 减少一个节点 + button3.place(relx=0.2, rely=0.7 + y0) + + def node_add(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + # 增加一个节点 + nodes = len(d.Nodes) + 1 + # 如果节点数目大于五,则将连接详细信息改为不显示 + if nodes > 5: + d.edgeinfo = 0 + if nodes > 10: + tk.messagebox.askokcancel(title='错误', message='已达到最大节点') + return + d = Data(1, nodes, d.styple) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + + def node_del(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + if d.nodes_num <= 2: + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(2, nodes, d.styple) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + def node_del_exact(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = d.Edges + if len(Specify_Nodes) == 3: + tk.messagebox.askokcancel(title='错误', message='未选择节点') + return + if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告 + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(3, nodes, d.styple) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + +def refresh(): + top = creat_window('路径生成') # 创建弹出窗口 + distance_entry = create_input_box(top, "节点个数:(1-10)", 6) # 创建一个输入框,获取节点个数 + # 创建一个下拉框,用于选择生成图类型 + mode_combobox = create_dropdown_box(top, "生成图类型", ["TSP", "完全图"]) # 创建一个下拉框,用于选择生成图类型 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + # 从下拉框中获取池化类型 + result2 = mode_combobox.get() + print(result1, result2) + flag = 0 + if result2!=None: + if result2 == '完全图': + flag = 1 + if 10 > result1 > 0: + global d + if flag: + d = Data(0, result1, 1) + else: + d = Data(0, result1, 0) + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='节点数范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的节点数') + + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + +def graph_init(): + node = random.randint(4, 10) + global d, MODE, MARK_NODE + MARK_NODE = 0 + MODE = 0 + d = Data(0, node,0) + print(node) + W.user_way = [0] + G.draw() + window(2) + +def empty(): + global MARK_NODE + MARK_NODE = 0 + G.delete_all() + +def auto_opera_mu(menu: tk.Menu): + """TSP问题自动生成与自动求解相关""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init()) + menu_node.add_command(label="重新生成", command=lambda: refresh()) + menu_node.add_command(label="清空", command=lambda: empty()) + menu_node.add_command(label="自动求解最优路径-dfs遍历", command=lambda: s.auto_solve(3)) + menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: s.auto_solve(2)) + menu_node.add_command(label="自动求解优化路径-回溯", command=lambda: s.auto_solve(1)) + menu_node.add_command(label="检查是否存在路径", command=lambda: s.check()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node) + +# 创建弹出窗口 +def creat_window(title): + top = tk.Toplevel(root) + top.geometry("300x350") + top.title(title) + return top + +# 输入框 +def create_input_box(top, text, value): + box_label = tk.Label(top, text=text) + box_label.pack(padx=10, pady=10) + box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3 + box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象 + box_size_entry.pack(padx=20, pady=20) + return box_size_entry + +# 下拉框 +def create_dropdown_box(top, text, listvalues): + # 创建一个下拉框,用于选择 + box_mode_label = tk.Label(top, text=text) + box_mode_label.pack(padx=10, pady=10) + box_mode_combobox = ttk.Combobox(top) + box_mode_combobox["values"] = listvalues + box_mode_combobox.pack(padx=20, pady=15) + return box_mode_combobox + +def dir2(x, y): # 计算两点间距离 + return (x[0] - y[0]) * (x[0] - y[0]) + (x[1] - y[1]) * (x[1] - y[1]) + +def left1(event): + global entry1, entry2, default_value, CLICK, entry3, entry4 + # 查找鼠标左键按下时位置是否在某个节点内 + n = -1 + for node in d.Nodes: + if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: + n = node[0] + # n为点击的节点,若没有则为-1 + m = mode.get() # 查看现在操作类型 + if m == 1 and not n == -1: + # 节点操作 + # N.node_mark(n) + if n >= len(d.Nodes): + # 弹出对话框 + tk.messagebox.askokcancel(title='错误', message='该节点不存在') + return + for node in d.Nodes: + if node[0] == n: + node[2] = 1 + else: + node[2] = 0 + G.draw() + if MARK_NODE == 1: # 限制节点操作 + N.node_mark_display(1) + elif MARK_NODE == 2: # 若是MARK_NODE==2,则可以进行路线画图操作 + if CLICK[2] == 0: + CLICK[0] = n + default_value.set(d.Nodes[n][1]) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + elif CLICK[2] == 1: + CLICK[1] = n + W.extract_ligature(CLICK[0], CLICK[1]) + CLICK[2] = -1 + CLICK[2] += 1 + elif MARK_NODE == 5: # 若是MARK_NODE==5,则可以进行节点连接操作 + if CLICK[2] == 0: + CLICK[0] = n + entry3.delete(0, tk.END) + # 设置输入框的值为 节点 + entry3.insert(0, d.Nodes[n][1]) + elif CLICK[2] == 1: + CLICK[1] = n + # W.extract_ligature(CLICK[0], CLICK[1]) + entry4.delete(0, tk.END) + # 设置输入框的值为 节点 + entry4.insert(0, d.Nodes[n][1]) + CLICK[2] = -1 + CLICK[2] += 1 + print(n) + +def element_reading(): + global List_Image + folder_path = 'K:/work/traveler/background/' + image_path = ['1_红.png', '1_绿.png', '1_蓝.png', '2_红.png', '2_绿.png', '2_蓝.png', '3_红.png', '3_绿.png', '3_蓝.png', + '4_红.png', '4_绿.png', '4_蓝.png', '5_红.png', '5_绿.png', '5_蓝.png', '6_红.png', '6_绿.png', '6_蓝.png', + '7_红.png', '7_绿.png', '7_蓝.png', '8_红.png', '8_绿.png', '8_蓝.png', '9_红.png', '9_绿.png', '9_蓝.png', + '10_红.png', '10_绿.png', '10_蓝.png', '删除节点.png', '减少节点.png', '增加节点.png', '线路增加.png', + '线路修改.png', '线路删除.png', '撤销.png', '清除路径.png', '确认.png', '编组9.png', '背景.png', + '矩形.png', '判断路径是否为贪心路径.png', '判断路径是否为最短路径.png', '矩形2.png', '完全图.png', '可达判断.png'] + for path in image_path[:30]: # 1-10球加载 + List_Image.append(element(folder_path+path, 50, 50)) + for path in image_path[30:36]: # 节点操作与连线操作功能按钮 + List_Image.append(element(folder_path + path, 280, 50)) + List_Image.append(element(folder_path + image_path[36], 140, 50)) # 撤销 + List_Image.append(element(folder_path + image_path[37], 140, 50)) # 清除路径 + List_Image.append(element(folder_path + image_path[38], 120, 50)) # 确认 + List_Image.append(element(folder_path + image_path[39], 380, 100)) # 编组_9 + List_Image.append(element(folder_path + image_path[40], 900, 900)) # 背景 + List_Image.append(element(folder_path + image_path[41], 380, 140)) # 小矩形 + List_Image.append(element(folder_path + image_path[42], 300, 50)) # 判断路径是否为贪心路径 + List_Image.append(element(folder_path + image_path[43], 300, 50)) # 判断路径是否为最短路径 + List_Image.append(element(folder_path + image_path[44], 380, 530)) # 大矩形 + List_Image.append(element(folder_path + image_path[45], 140, 50)) # 完全图 + List_Image.append(element(folder_path + image_path[46], 140, 50)) # 可达判断 + +def element(path, width, height): + # 加载图元对应的图片文件 + img = Image.open(path) + # 使用resize方法调整图片 + img = img.resize((width, height)) + # 把Image对象转换成PhotoImage对象 + img = ImageTk.PhotoImage(img) + # 保存图片的引用,防止被垃圾回收 + root.img = img + return img + +def window(event): + global MARK_NODE + MARK_NODE = 0 + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + Label_1 = tk.Label(frame_left, text="遍历节点数目", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_1.place(x=50, y=330, width=180, height=40) + Label_2 = tk.Label(frame_left, text="求解花费时间", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_2.place(x=50, y=450, width=180, height=40) + Label_3 = tk.Label(frame_left, text="最短路径", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_3.place(x=50, y=570, width=140, height=40) + if event == 2: + lab1 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab1.place(x=80, y=370, width=300, height=60) + lab2 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(x=80, y=490, width=300, height=60) + lab3 = tk.Label(frame_left, text=f"{1}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab3.place(x=80, y=610, width=300, height=140) + else: + lab1 = tk.Label(frame_left, text=f"{s.way_sum}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab1.place(x=80, y=370, width=300, height=60) + t = round(s.end - s.start, 4) + lab2 = tk.Label(frame_left, text=f"{t}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(x=80, y=490, width=300, height=60) + g_way = '' + if len(s.ans) > 0: + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + else: + g_way='1' + lab3 = tk.Label(frame_left, text=f"{g_way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab3.place(x=80, y=610, width=300, height=140) + +def left_window(): + background_button = tk.Label(frame_left, image=List_Image[39], bg='#FFFAF0') # 编组 + background_button.place(x=50, y=20, width=380, height=100) + background_button.bind("", window) + # # 创建 Label 组件来显示背景图片 + background_on = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[41]) # 小矩形背景 + background_on.place(x=50, y=140, width=380, height=140) + + if MARK_NODE == 1: + background_color = [BLACK, '#DCDCDC', '#DCDCDC'] + elif MARK_NODE == 5: + background_color = ['#DCDCDC', BLACK, '#DCDCDC'] + elif MARK_NODE == 2: + background_color = ['#DCDCDC', '#DCDCDC', BLACK] + else: + background_color = ['#DCDCDC', '#DCDCDC', '#DCDCDC'] + Label_button1 = tk.Label(frame_left, text="节点操作", bg='white', fg=background_color[0], font=('微软雅黑', 15)) + Label_button1.place(x=60, y=180, width=80, height=50) + Label_button1.bind("", N.node_mark_display) + Label_button2 = tk.Label(frame_left, text="连接操作", bg='white', fg=background_color[1], font=('微软雅黑', 15)) + Label_button2.place(x=200, y=180, width=80, height=50) + Label_button2.bind("", W.way_operate) + Label_button3 = tk.Label(frame_left, text="路径操作", bg='white', fg=background_color[2], font=('微软雅黑', 15)) + Label_button3.place(x=340, y=180, width=80, height=50) + Label_button3.bind("", W.way_route_markings) + + background_below = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[44]) # 大矩形背景 + background_below.place(x=50, y=300, width=380, height=530) + +if __name__ == '__main__': + root = Tk() # 设置主界面 + root.title("旅行商问题") # 设置标题 + root.geometry("1400x900") # 设置大小 + + root.resizable(0, 0) # 设置不能调整显示边框 + element_reading() + frame_top = tk.Frame(root, bg='white', width=1400, height=50, highlightthickness=0) + frame_top.place(x=0, y=0) # 绘制信息输出栏 + + frame_left = tk.Frame(root, bg='#FFFAF0', highlightthickness=0, width=500, height=850) # 左边栏 + frame_left.place(x=0, y=50) # 左边栏 + + cv = tk.Canvas(root, bg='#FFFAF0', bd=0, relief="flat", width=900, height=850, highlightthickness=0) + cv.place(x=500, y=50) # 放置绘图Canvas画布 + + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) # 背景 + + G = Graph() + mode = tk.IntVar(value=1) + main_menu = tk.Menu(root) + root.config(menu=main_menu) + cv.bind('', left1) + E = Edge() + W = Ways() + s = Solve() + N = Node() + auto_opera_mu(main_menu) + window(2) + G.lab_show(frame_top, 0, 0, 0) # 显示边信息 + root.mainloop() diff --git a/X4_4.py b/X4_4.py new file mode 100644 index 0000000..82b0721 --- /dev/null +++ b/X4_4.py @@ -0,0 +1,1730 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X4.py +from tkinter import Tk +import random +import math +import time +import tkinter as tk +from tkinter import * +from tkinter import ttk +from tkinter import messagebox +import numpy as np +import networkx as nx +from PIL import Image, ImageTk + +MODE = 0 +BLUE = "#0080FF" +BLACK = "#000000" +RED = "#FFAAAA" +YELLOW = "#FFFACD" +LINE = '#c8d2c8' +GREY = '#070b19' +GREEN = '#5ba585' +NODE = '#33a8cd' +ZERO = 'gold' +MARK_NODE = 0 +Specify_Nodes = '' +Edges_list = [] +NAME = [] +CLICK = [0, 0, 0] + +List_Image = [] + +class Data: + def __init__(self, source: int, num: int, mold: int): + global NAME + if num <= 0 or num > 10: + raise ValueError("num must be a positive integer") + self.styple = mold + self.edgeinfo = 1 # 是否显示详情 + self.nodes_num = num # 节点个数 + self.ang = 360 / self.nodes_num # 圆心顶点角度 + self.R = 300 # 外接圆半径 + self.bc = 2 * self.R * math.sin(math.pi / self.nodes_num) # 节点之间的正多边形距离 + self.canvas_len = int(2 * self.R + 80) # 画布边长 + self.center = ((self.canvas_len+220) // 2, (self.canvas_len+230) // 2) # 画布中心点坐标 + self.drop = [] + self.index = 0 + if source == 3 or source == 2: # 指定删除 + # name = [str(i) for i in range(1, num + 2)] + # print(name) + if source == 2: + NAME.pop() + else: + # print(Specify_Nodes) + NAME.remove(Specify_Nodes) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + # print(self.Nodes) + self.Edges = self.edges_delete(source) # 创建第一条连接 + else: + if source == 1: + name = str(int(NAME[-1])+1) + # print(name) + NAME.append(name) + else: + NAME = [str(i) for i in range(1, num + 1)] + # print(NAME) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + # print(self.Nodes) + self.Edges = self.edges_creat(source) # 创建第一条连接 + self.edge_add(2, source) # 创建第2条连接 + self.edge_add(3, source) # 创建第3条连接 + + def nodes_creat(self, n_sum=None): + if n_sum == None: + n_sum = self.nodes_num + nodes = [] # 初始化node表 + # 设置画布中心点坐标x0,y0 + x0 = self.center[0] + y0 = self.center[1] + for i in range(n_sum): # 通过几何运算得到多边形各个顶点坐标 + rad = math.radians(i * self.ang) # 计算第i个点的弧度 + x = int(math.cos(rad) * self.R) # 计算第i个顶点x坐标 + y = int(math.sin(rad) * self.R) # 计算第i个顶点y坐标 + # name = '' + str(i + 1) # 给第i个顶点命名 + name = '' + self.name[i] + mark = 0 # 节点为未标记 + dominator = 1 # 设置为必经节点 + nodes.append([i, name, mark, (x0 + x, y0 + y), dominator]) # 将当前节点加入node中 + return nodes + + def edges_delete(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 + # print(str(int(Specify_Nodes) + 1)) + for list_edge in Edges_list: + # print(list_edge) + edges.append([ser, list_edge[1], list_edge[2],list_edge[3], list_edge[4],list_edge[5], list_edge[6], + list_edge[7], list_edge[8]]) # 将连接加入到边的集合中 + nox = [int(num) for num in edges[ser][5].split('-')] + # print(nox) + if len(nox) == 2: + ss = edges[ser][5] + else: + ss = edges[ser][5][:-2] + # print(ss) + if source == 2: + if str(len(self.Nodes)+1) in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + else: + if Specify_Nodes in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + ser += 1 + return edges + + def edges_creat(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 # 初始化链接对象编号 + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + # print(nodes1) + self.drop = [] + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + # print(nodes1) + for node1 in nodes1: # 遍历删除后的节点信息 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + if self.styple == 1: + dist_c = random.randint(1, 99) # 随机生成距离成本 + time_c = random.randint(1, 99) # 随机生成链接的时间 + else: + if self.drop.count(n2) > 3 or self.drop.count(n1) > 3: + dist_c = random.randint(101, 120) # 随机生成距离成本 + time_c = random.randint(101, 120) # 随机生成链接的时间 + else: + dist_c = random.randint(10, 120) # 随机生成距离成本 + time_c = random.randint(10, 120) # 随机生成链接的时间 + seq = 1 # 链接序号 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}" # 链接标签 + enable = 1 # 连接是否可用 + if dist_c >= 100 or time_c >= 100: # 设置某些节点为不显示 + enable = 0 + else: + self.drop.append(n1) + self.drop.append(n2) + if source == 1: + # print(n2, len(self.Nodes)) + if n2 == len(self.Nodes)-1: + # print('进来了') + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + else: + # print(self.index) + edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5],Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + self.index += 1 + else: + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + ser += 1 # 计数加一 + return edges + + def edge_add(self, no, source): + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + for node1 in nodes1: # 遍历删除后的节点信息 + ser = len(self.Edges) # 链接序号 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + show = 0 # 设置为不显示 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}-{no}" # 链接标签 + dist_c = random.randint(30, 100) # 距离成本 + time_c = random.randint(1, 30) # 链接的时间 + enable = no # 连接是否可用 + if source == 1: + if n2 == len(self.Nodes)-1: + # print('进来了') + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) # 将连接加入到边的集合中 + else: + # print(no ,self.index) + self.Edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5], Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + + self.index += 1 + else: + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) + + def coord_creat(self): + '''返回每个节点的坐标''' + coordinate = [] + x0 = self.center[0] + y0 = self.center[1] + for i in range(self.nodes_num): + rad = math.radians(i * self.ang) + x = int(math.cos(rad) * self.R) + y = int(math.sin(rad) * self.R) + coordinate.append((x0 + x, y0 + y)) + return coordinate + + def node_co(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[3] + + def node_no(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[0] + def node_coordinate(self, n): # 通过节点编号返回矩阵的坐标 + result = [] + for i in range(n): + for j in range(i + 1, n): + result.append((i, j)) + return result + +# G = Graph() +# print(1) +nodes = 6 # 设置初始节点数量 +d = Data(0, nodes, 0) + +class Graph: + # Graph定义 + def edge_info(self, cv: tk.Canvas, info: str, n1: tuple, n2: tuple): # 显示路径信息 + if d.edgeinfo == 0: # 若设置为不显示则直接返回 + return + # print(n1) + # print(n2) + x = (n1[0] - n2[0]) // 2 + n2[0] # 计算中点x坐标 + y = (n1[1] - n2[1]) // 2 + n2[1] # 计算中点y坐标 + if (n2[1] - n1[1]) != 0: # 斜率为非0度时的显示 + reat = math.atan((n2[0] - n1[0]) / (n2[1] - n1[1])) # 根据斜率计算弧度 + ang = round(math.degrees(reat)) + 90 # 根据弧度转角度 + if ang > 89: # 斜度大于90度调整坐标 + text_x, text_y = x + 15, y + else: # 斜度大于90度调整坐标 + text_x, text_y = x, y + 15 + else: # 斜率为0度时的显示 + ang = round(math.degrees(0)) # 根据弧度转角度 + text_x, text_y = x - 30, y + 10 + # print(info.split(' ')[-1]) + cv.create_text(text_x, text_y, text=f'{info.split()[-2]}', fill="black", font=('微软雅黑', 12), angle=ang) # 根据信息显示文字 + + def draw(self): + cv.delete('all') # 清空画布 + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) + dis, tim = self.edges_show(cv, d.Edges) # 绘制边 + self.lab_show(frame_top, len(d.Nodes), tim, dis) # 显示边信息 + self.nodes_show(cv) # 绘制点 + cv.update() # 更新画布 + + + def lab_show(self, frame: tk.Frame, nodes: int, time: int, distance: int): + labs = ["节点数目", "时间", "距离(成本)"] + x0 = d.canvas_len // 3 + for i in range(len(labs)): + lab1 = tk.Label(frame, text=labs[i], fg=BLACK, bg='white', font=('微软雅黑', 12)) + if i == 0: + lab2 = tk.Label(frame, text=f"{nodes}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + elif i == 1: + lab2 = tk.Label(frame, text=f"{time}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + else: + lab2 = tk.Label(frame, text=f"{distance}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=10 + x0 * i, y=10, width=80, height=30) + lab2.place(x=10 + x0 * i + 80, y=10, width=80, height=30) + lab1 = tk.Label(frame, text='分钟', fg=BLACK, bg='white', font=('微软雅黑', 12)) + lab1.place(x=170 + x0, y=10, width=50, height=30) + lab1 = tk.Label(frame, text='千米', fg=BLACK, bg='white', font=('微软雅黑', 12)) + lab1.place(x=170 + x0 * 2, y=10, width=50, height=30) + + def nodes_show(self, cv: tk.Canvas): + COL = NODE # 初始化节点颜色 + for i in d.Nodes: # 遍历每一个节点 + x, y = i[3] # 记录该节点坐标 + if i[2] == 1 or i[2] == 2: # 如果节点被标记,则将节点标为红色 + COL = RED + elif i[0] == 0: # 如果节点为起始节点,则将节点标记为特殊颜色 + COL = ZERO + elif i[2] == 5: # 如果节点已经被访问,则将节点标记(用户路径功能) + COL = 'gold' + else: # 否则则使用初始颜色 + COL = NODE + if i[4] == 1: # 如果是必经节点,则加黑色边框 + w = 1 + else: # 如果是非必经节点,则没有边框 + w = 0 + # 根据坐标,圆的颜色与是否有边框绘制圆圈来表示节点 + cv.create_oval(x - 20, y - 20, x + 20, y + 20, fill=COL, width=w, ) # outline='#ee28a3') + cv.create_text(x, y, text=f'{i[1]}', fill=GREY, font=('华文楷体', 18)) + cv.update() + + def edges_show(self, cv: tk.Canvas, edges: d.Edges): + """ + 显示链接 + :param cv: tkcanvas对象 + :return: + """ + distance = 0 # 初始化被标记的线的总路径 + time1 = 0 # 初始化被标记的线的总时间 + for edge in edges: + node_1 = d.node_co(edge[1]) # 链接线编号对应的坐标1 + node_2 = d.node_co(edge[2]) # 链接线编号对应的坐标2 + # print(node_1,node_2) + if edge[8] == 2 and edge[3] == 1: # 若该链接为两点间第二条连线 + self.more_edge_show(2, edge) + elif edge[8] == 3 and edge[3] == 1: # 若为两点间第3条连线 + self.more_edge_show(3, edge) + if edge[8] == 1 and edge[3] == 1: # 若为两点间第1条连线且该连接存在 + self.edge_info(cv, f'{edge[5]} {edge[6]} {edge[7]}', node_1, node_2) + point = [node_1, node_2] + if edge[4] == 1 and edge[8] == 1 and edge[3] == 1: # 如果路径被标记,则用绿色绘制 + if MODE == 1: + cv.create_line(point, fill=RED, width=4) + elif MODE == 2: + cv.create_line(point, fill=BLUE, width=4) + else: + cv.create_line(point, fill=GREEN, width=4) + distance += edge[6] # 更新总路径 + time1 += edge[7] # 更新总时间 + elif edge[8] == 1 and edge[3] == 1: # 如果是节点之间的第一条线,则用正常颜色标记 + cv.create_line(point, fill=LINE, width=2) + if d.Nodes[d.node_no(edge[2])][2] + d.Nodes[d.node_no(edge[2])][2] == 3: # 若该线被用户选中,则用蓝色虚线标记 + cv.create_line(point, fill=BLUE, width=5, dash=(4, 14)) + return (distance, time1) # 返回标记路径的总时间与路程 + + # def node_edge(self, a, b): # 根据点的编号找边的编号 + # if a > b: + # x = a + # a = b + # b = x + # return int(a * (2 * len(d.Nodes) - a - 3) / 2 + b - 1) + + def node_edge(self, a, b): # 根据点的编号找边的编号 + for edge in d.Edges: + if a == edge[1] and b == edge[2]: + return edge[0] + if b == edge[1] and a == edge[2]: + return edge[0] + + def way_node_edge(self, a, b): # 根据点的编号找边的编号 + way = [] + for node in d.Nodes: + way.append(int(node[1])) + # print(way) + # print(a, b) + x, y = way[a]-1, way[b]-1 + for edge in d.Edges: + if x == edge[1] and y == edge[2]: + return edge[0] + if y == edge[1] and x == edge[2]: + return edge[0] + + def delete_all(self): + cv.delete('all') # 清空画布 + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) + self.lab_show(frame_top, 0, 0, 0) # 显示边信息 + W.user_way = [0] + window(2) + +class Edge: + def __init__(self): + self.mark = [] + # 记录已标记的节点数目(连接操作) + self.user_cur = 0 + # 记录用户所在点 + + def node_edge(self, no, n1, n2): # 根据节点返回编号 + if n1 > n2: + n = n1 + n1 = n2 + n2 = n + for e in d.Edges: + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 + def edge_del_all(self, mode): + # mode = 1:优先最短路径 + # mode = 2:优先最短时间 + # cur1 = node_edge() + if mode == 1: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_cost(e[1], e[2]) + else: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_time(e[1], e[2]) + G.draw() + + def edge_merge_cost(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][6]), (cur2, d.Edges[cur2][6]), (cur3, d.Edges[cur3][6])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + def edge_merge_time(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][7]), (cur2, d.Edges[cur2][7]), (cur3, d.Edges[cur3][7])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + +class Solve: + def __init__(self): + self.ans = [] # 记录答案 + self.mutians = [] # 若有多个最短路,则保存在mutians中 + self.way_sum = 0 # 记录遍历的路径数目 + self.minn = float("inf") # 初始化最小值为正无穷 + self.start = 0 # 起始点 + self.end = 0 # 终点 + self.mark = [] # 记录该点是否已经经过 + self.user_cur = 0 # 记录用户所在点 + self.cura = -1 # 记录用户选择的第一个点 + self.curb = -1 # 记录用户选择的第二个点 + self.con = 0 # 记录必经的节点数目 + + def node_greedy(self): # 贪心 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[3] == 1: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.greedy(distances) + self.ans = ways.copy() + # 如果找到的路径不合法,则表示贪心法无法求解得到合法路径 + print(self.way_sum) + # for i in distances: + # print(i) + if len(ways) != l+1 : + tk.messagebox.askokcancel(title='结果', message='该图不存在贪心算法路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + self.ans.clear() + self.end = time.time() # 记录结束时间 + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + # print(ways[len(ways) - 1], i) + # d.Edges[G.way_node_edge(ways[len(ways) - 1], i)][4] = True # 标记起点终点形成环线 + # print(ways) + tk.messagebox.askokcancel(title='结果', message='贪心算法路径') + self.end = time.time() # 记录结束时间 + def greedy(self,distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + next_city = city + self.way_sum += 1 # 遍历路径的数目加一 + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + # print(path) + return path + + def tsp_backtracking(self): + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[3] == 1: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.backtracking(distances) + print(self.way_sum) + + if ways: # 判断路径是否存在 + self.ans = ways.copy() + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='回溯算法路径') + self.end = time.time() # 记录结束时间 + else: + tk.messagebox.askokcancel(title='结果', message='该图不存在回溯算法路径') + self.end = time.time() # 记录结束时间 + return -1 + + def backtracking(self, distances): + num_cities = len(distances) + path = [0] # 起始城市为0 + visited = [False] * num_cities + visited[0] = True + min_distance = float('inf') + shortest_path = None + + def backtrack(curr_city, curr_distance, visited, path): + nonlocal min_distance, shortest_path + if len(path) == num_cities: + # 到达所有城市,更新最短路径 + if curr_distance + distances[curr_city][0] < min_distance: + min_distance = curr_distance + distances[curr_city][0] + shortest_path = path + [0] + else: + for next_city in range(num_cities): + if not visited[next_city]: + # 选择下一个未访问的城市 + visited[next_city] = True + path.append(next_city) + new_distance = curr_distance + distances[curr_city][next_city] + self.way_sum += 1 # 遍历路径的数目加一 + # 剪枝条件:当前路径已经大于最短路径,不继续搜索 + if new_distance < min_distance: + backtrack(next_city, new_distance, visited, path) + # 回溯 + visited[next_city] = False + path.pop() + + backtrack(0, 0, visited, path) + return shortest_path + + def auto_solve(self, number): + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + if number == 1: # 回溯 + self.tsp_backtracking() + elif number == 2: + self.node_greedy() # 贪心 + else: # 深度优先算法 + self.node_dfs() + window(1) + G.draw() + + def node_dfs(self): # 遍历 深度优先算法 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[3] == 1: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + cost, ways = self.dfs(distances) + print(self.way_sum) + self.ans = ways.copy() + if len(ways) != l + 1: + tk.messagebox.askokcancel(title='结果', message='该图不存在dfs路径') + # print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + self.end = time.time() # 记录结束时间 + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='dfs算法路径') + self.end = time.time() # 记录结束时间 + + + def dfs_tsp(self,graph, start, current, path, + visited, cost, min_cost, min_path): + if len(path) == len(graph) and graph[ + current][start] != np.inf: + path.append(start) + cost += graph[current][start] + if cost < min_cost[0]: + min_cost[0] = cost + min_path[0] = path.copy() + path.pop() + cost -= graph[current][start] + return + for next_node in range(len(graph)): + if graph[current][next_node] != np.inf and \ + not visited[next_node]: + visited[next_node] = True + path.append(next_node) + cost += graph[current][next_node] + self.way_sum += 1 # 遍历路径的数目加一 + self.dfs_tsp(graph, start, next_node, path, + visited, cost, min_cost, min_path) + visited[next_node] = False + path.pop() + cost -= graph[current][next_node] + def dfs(self, graph): + n = len(graph) + min_cost = [float('inf')] + min_path = [[]] + + for start_node in range(n): + visited = [False] * n + path = [start_node] + cost = 0 + visited[start_node] = True + self.dfs_tsp(graph, start_node, start_node, path, + visited, cost, min_cost, min_path) + return min_cost[0], min_path[0] + + def find_hamiltonian_cycles(self,start, distances): + n = len(distances) + path = [start] + cycles = [] + + def is_valid(node, position): + if distances[path[position - 1]][node] == np.inf: + return False + if node in path: + return False + return True + def find_paths(node): + for next_node in range(n): + if is_valid(next_node, len(path)): + path.append(next_node) + if len(path) < n: + find_paths(next_node) + elif len(path) == n and distances[path[-1]][start] != np.inf: + cycles.append(path + [start]) + path.pop() + find_paths(start) + return cycles + def check(self): + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 将路径中的 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + all_hamiltonian_cycles = self.find_hamiltonian_cycles(0, np.array(distances)) + l = len(all_hamiltonian_cycles) + if l: + for cycles in all_hamiltonian_cycles: + dis_ans = 0 + for i in range(d.nodes_num): + dis_ans += d.Edges[G.way_node_edge(cycles[i], cycles[i + 1])][6] + print(cycles,dis_ans) + if all_hamiltonian_cycles: + tk.messagebox.askokcancel(title='结果', message=f'该图存在 {len(all_hamiltonian_cycles)} 条路径\n') + else: + tk.messagebox.askokcancel(title='结果', message='该图不能完全互通') + +def node_edge(n1, n2, no=1): # 根据点的编号找边的编号 + if n1 > n2: # 为了防止充分编号,让n1小于n2 + n1, n2 = n2, n1 + for e in d.Edges: # 遍历所有路径,找到符合要求的返回 + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 # 若找不到,则返回-1 + +class Ways(): + def __init__(self): + self.user_way = [0] # 记录用户路径 + self.user_cur = 0 # 记录用户所在点 + self.user_flag = set() # 记录用户已经过的点 + def auto_ways(self): # 标记用户路径 + for w in d.Edges: # 清空路径标记 + w[4] = 0 + for N in d.Nodes: # 清空节点标记 + N[2] = 0 + for w in self.user_way: # 标记用户已选的点 + d.Nodes[w][2] = 5 + # 标记用户经过的路径 + for w in range(len(self.user_way) - 1): + d.Edges[G.node_edge(self.user_way[w],self.user_way[w + 1])][4] = 1 + # 如果已经访问所有节点,自动回到初始节点 + if len(set(self.user_way)) == len(d.Nodes): + d.Edges[G.node_edge(self.user_way[len(self.user_way) - 1], 0)][4] = 1 + G.draw() # 按照标记重新绘制图形 + + def check_user_best(self): # 检查用户路径是不是最佳路径 + s.node_dfs() + if not len(s.ans): + return -1, 0 + # 长度不一样肯定不匹配 + if not len(self.user_way) == len(s.ans): + return 0, 0 + dis_user = 0 + dis_ans = 0 + num = len(self.user_way) + for i in range(num - 1): + dis_user += d.Edges[G.node_edge(self.user_way[i], self.user_way[i+1])][6] + dis_ans += d.Edges[G.way_node_edge(s.ans[i], s.ans[i + 1])][6] + return dis_ans, dis_user + + def show_best_ans(self, top, best, user): + lab1 = tk.Label(top, text="最短路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + # print(s.ans) + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + lab2 = tk.Label(top, text=f"最佳路径总成本:{best}\n用户路径总成本:{user}", bg=BLUE, fg=BLACK, + font=('微软雅黑', 12), wraplength=200, justify='left') + lab2.place(relx=0.4, rely=0.7 + y0, width=200, height=100) + + def show_BEST(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + + if not best: + lab1 = tk.Label(frame_left, text="不存在最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + else: + lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + # else: # 否则说明用户路径不是最短路径 + # self.servey_path_display() + def servey_path_display(self): + lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way = b_way[:-2] + lab2 = tk.Label(frame_left, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def check_user_greey(self): # 检查用户路径是不是贪心路径,不是的话返回错误点 + s.node_greedy()# 计算贪心路径 + if not len(s.ans): + return -2 + # 路径长度不一样肯定不匹配,直接返回 + if not len(self.user_way) == len(s.ans): + # self.auto_ways() + return 0 + way = [(int(node[1])-1) for node in d.Nodes] + result1 = list(map(str, self.user_way)) + result1 = ' '.join(result1) + ways = [way[an] for an in s.ans] + result2 = list(map(str, ways)) + result3 = ' '.join(result2) + result2.reverse() # 倒序排列列表元素 + result4 = ' '.join(result2) + if result4 == result1 or result3 == result1: + return -1 #-1表示是贪心路径 + else: + x_1 = self.find_longest(result1, result4) + x_2 = self.find_longest(result1, result3) + return max(x_1, x_2) + + def find_longest(self, list1, list2): + index = 0 + while index < len(list1) and index < len(list2): + if list1[index] != list2[index]: + break + index += 1 + return index + + + def show_greddy_ans(self, top): + lab1 = tk.Label(top, text="贪心路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + # 对用户路径进行标记与记录 + def way_route_markings(self, event): + global MARK_NODE + MARK_NODE = 2 + # E.newedge_del_all(1) + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + + left_window() + self.user_way = [0] + # lab = tk.Label(frame_left, text="路径操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + global entry1,entry2,default_value + # 定义默认值变量 + default_value = tk.StringVar() + default_value.set(self.user_way[-1]+1) + laba1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + laba1.place(relx=0.2, rely=0.28 + y0, width=140, height=30) + # 创建第一个输入框 + entry1 = tk.Entry(frame_left, textvariable=default_value, bg="#F5F5F5") + entry1.place(relx=0.6, rely=0.28 + y0, width=50, height=30) + labb1 = tk.Label(frame_left, text="当前下一节点:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + labb1.place(relx=0.2, rely=0.33 + y0, width=140, height=30) + # 创建第二个输入框 + entry2 = tk.Entry(frame_left, bg="#F5F5F5") + entry2.place(relx=0.6, rely=0.33 + y0, width=50, height=30) + + labb3 = tk.Label(frame_left, text="是否进行连接:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + labb3.place(relx=0.2, rely=0.4 + y0, width=140, height=30) + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[38], bg='white', command=lambda: self.extract_values()) # 确定 + button.place(relx=0.55, rely=0.38 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[36], bg='white', command=lambda: self.revoke()) # 撤销 + button2.place(relx=0.18, rely=0.46 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[37], bg='white', command=lambda: self.clear_all_paths()) # 清除路径 + button3.place(relx=0.5, rely=0.46 + y0) + button4 = tk.Button(frame_left, relief="flat", image=List_Image[42], bg='white', command=lambda: self.greedy_path()) # 判断路径是否为贪心路径 + button4.place(relx=0.18, rely=0.55 + y0) + button5 = tk.Button(frame_left, relief="flat", image=List_Image[43], bg='white', command=lambda: self.shortest_path()) # 判断路径是否为最短路径 + button5.place(relx=0.18, rely=0.62 + y0) + + self.user_path_display() + def extract_values(self): + global entry1, entry2, default_value + global MARK_NODE + MARK_NODE = 2 + value1 = entry1.get() # 获取第一个输入框的值 + value2 = entry2.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + E = 0 + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + for item in d.Edges: + if str_route == item[5]: + if item[6] < 100 and item[7] < 100: + if d.Edges[G.node_edge(value1-1, value2-1)][4]: + E = 2 + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + else: + if self.user_way[-1] == (value1-1): + E = 1 + global MODE + MODE = 1 + self.user_way.append(value2 - 1) + default_value.set(value2) + d.Edges[G.node_edge(value1-1, value2-1)][4] = True + entry1.config(textvariable=default_value) # 更新entry1的默认值 + entry2.delete(0, tk.END) + else: + E = 3 + tk.messagebox.askokcancel(title='错误', message='未连贯!') + break + print(E) + if E == 1: + G.draw() + self.user_path_display() + elif E == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def extract_ligature(self, n1, n2): + global MARK_NODE + MARK_NODE = 2 + no = G.way_node_edge(n1, n2) + flage = False + if d.Edges[no][4]: + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + elif d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + else: + if self.user_way[-1] == int(d.Nodes[n2][1]) - 1: + nx = n1 + n1 = n2 + n2 = nx + if self.user_way[-1] == int(d.Nodes[n1][1])-1: + global MODE + MODE = 1 + self.user_way.append(int(d.Nodes[n2][1]) - 1) + d.Edges[no][4] = True + flage = True + else: + tk.messagebox.askokcancel(title='错误', message='未连贯!') + if flage: + G.draw() + self.user_path_display() + + def revoke(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1+1}-{value2+1}' + else: + str_route = f'{value2+1}-{value1+1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 1 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2+1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + G.draw() + self.user_path_display() + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def clear_all_paths(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + for i in range(len(self.user_way)-1): + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1 + 1}-{value2 + 1}' + else: + str_route = f'{value2 + 1}-{value1 + 1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 3 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2 + 1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + for i in range(len(d.Edges)): + d.Edges[i][4] = False + G.draw() + self.user_path_display() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def greedy_path(self): + g = self.check_user_greey() + if g == -2: + # cvx.create_text(0, 0, text="不存在贪心路径") + lab1 = tk.Label(frame_left, text="不存在贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + elif g == -1: # 如果用户路径是贪心路径则显示 + # cvx.create_text(0, 0, text="用户路径是贪心路径") + lab1 = tk.Label(frame_left, text="用户路径是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + # elif g: + # self.servey_greedy_path() + else: + # print(self.user_way) + # print(self.user_way[0:g]) + # cvx.create_text(0, 0, text="用户路径不是是贪心路径") + lab1 = tk.Label(frame_left, text="用户路径不是是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + + def servey_greedy_path(self): + lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + lab2 = tk.Label(frame_left, text=f"{g_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + + + def shortest_path(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if not best: + lab1 = tk.Label(frame_left, text="不存在最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + else: + lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + # if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + # lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + # lab1.place(relx=0.1, rely=0.8, width=180, height=30) + # else: # 否则说明用户路径不是最短路径 + # self.servey_path_display() + + # 用户路径展示 + def user_path_display(self): + lab1 = tk.Label(frame_left, text="用户路径:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + lab1.place(relx=0.15, rely=0.8, width=80, height=30) + # self.auto_ways() + way = "" + # d_way = [] + # for i in d.Nodes: + # d_way.append(i[1]) + for i in range(len(self.user_way)): + if len(self.user_way)-i > 1: + way += str(self.user_way[i]+1) + way += "->" + else: + way += str(self.user_way[i]+1) + lab2 = tk.Label(frame_left, text=f"{way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.18, rely=0.84, width=300, height=100) + + def way_operate(self, event): + global MARK_NODE + for i in range(len(d.Edges)): + d.Edges[i][4] = False + MARK_NODE = 5 + # E.newedge_del_all(1) + for w in frame_left.winfo_children(): + w.destroy() + # 清除左侧组件 + left_window() + self.user_way = [0] + y0 = 0.1 + label1 = tk.Label(frame_left, text="节点1:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + label1.place(relx=0.15, rely=0.32 + y0, width=100, height=30) + global entry3, entry4 + # 创建第一个输入框 + entry3 = tk.Entry(frame_left, bg='#F5F5F5') + entry3.place(relx=0.6, rely=0.32 + y0, width=50, height=30) + + label2 = tk.Label(frame_left, text="节点2:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + label2.place(relx=0.15, rely=0.37 + y0, width=100, height=30) + # 创建第二个输入框 + entry4 = tk.Entry(frame_left, bg='#F5F5F5') + entry4.place(relx=0.6, rely=0.37 + y0, width=50, height=30) + button1 = tk.Button(frame_left, image=List_Image[45], relief="flat", bg='white', + command=lambda: self.complete_graph()) # 完全图 + button1.place(relx=0.15, rely=0.43 + y0) + button2 = tk.Button(frame_left, image=List_Image[46], relief="flat", bg='white', + command=lambda: self.reachable_judgment()) # 节点可达性判断 + button2.place(relx=0.5, rely=0.43 + y0) + + + label3 = tk.Label(frame_left, text="是否进行两节点之间操作:", bg='white', fg=BLACK, font=('微软雅黑', 14)) + label3.place(relx=0.15, rely=0.5 + y0, width=240, height=30) + # 按钮 + button3 = tk.Button(frame_left, relief="flat", image=List_Image[33], bg='white', + command=lambda: self.way_add()) # 线路增加 + button3.place(relx=0.2, rely=0.56 + y0) + button4 = tk.Button(frame_left, relief="flat", image=List_Image[34], bg='white', + command=lambda: self.way_change()) # 线路修改 + button4.place(relx=0.2, rely=0.66 + y0) + button5 = tk.Button(frame_left, relief="flat", image=List_Image[35], bg='white', + command=lambda: self.way_delete()) # 线路删除 + button5.place(relx=0.2, rely=0.76 + y0) + + def is_reachable(self, graph, start, target, visited): + if start == target: + return True + visited.add(start) + for neighbor in graph[start]: + if neighbor not in visited: + if self.is_reachable(graph, neighbor, target, visited): + return True + return False + + def complete_graph(self): + l = len(d.Nodes) + result = d.node_coordinate(l) + for index in range(len(result)): + if d.Edges[index][3] == 0: + dist_c = random.randint(10, 99) # 随机生成距离成本 + time_c = random.randint(10, 99) # 随机生成链接的时间 + d.Edges[index][3] = 1 + d.Edges[index][6] = dist_c + d.Edges[index][7] = time_c + G.draw() + + + def reachable_judgment(self): + try: + a = int(entry3.get()) # 获取第一个输入框的值 + b = int(entry4.get()) # 获取第二个输入框的值 + l = len(d.Nodes) + index_list = [] + for i in d.Nodes: + index_list.append(i[1]) + dictionary = {i: [] for i in range(1, l + 1)} + result = d.node_coordinate(l) + # 生成无向图 + for i in range(len(result)): + if d.Edges[i][3] == 1: + x = d.Edges[i][5].split('-')[0] + y = d.Edges[i][5].split('-')[-1] + dictionary[index_list.index(x)+1].append(index_list.index(y)+1) + dictionary[index_list.index(y)+1].append(index_list.index(x)+1) + start_node = index_list.index(str(a))+1 + target_node = index_list.index(str(b))+1 + visited_nodes = set() + print(dictionary) + flag = self.is_reachable(dictionary, start_node, target_node, visited_nodes) + if flag: + tk.messagebox.askokcancel(title='结果', message=f'{a}到{b}可达') + else: + tk.messagebox.askokcancel(title='结果', message=f'{a}到{b}不可达') + except ValueError: + tk.messagebox.askokcancel(title='结果', message='请输入节点') + + def way_add(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + if d.drop.count(ways_list.index(value1)) > 4 or d.drop.count(ways_list.index(value2)) > 4: + print(d.drop) + tk.messagebox.askokcancel(title='提醒', message='当前节点已经到达最大连接数!') + return + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 1: + tk.messagebox.askokcancel(title='提醒', message='当前路线已存在!') + return + top = creat_window(str_route+'路径参数配置') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", 101) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", 101) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1,result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][3] = 1 + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_change(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + top = creat_window(str_route + '路径参数修改') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1, result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_delete(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + d.Edges[no][3] = 0 + d.Edges[no][6] = 111 + d.Edges[no][7] = 111 + G.draw() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + +class Node: + # 标记节点按钮 + def node_mark_display(self, event): + global MARK_NODE, Specify_Nodes + W.user_way = [0] + MARK_NODE = 1 + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + curx = -1 + y0 = 0.1 + for i in d.Nodes: + if i[2] == 1: + curx = i[0] + # lab = tk.Label(frame_left, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.32, width=200, height=30) + labx1 = tk.Label(frame_left, text=f"当前共有{len(d.Nodes)}个节点", bg='white', fg=BLACK, font=('微软雅黑', 20)) + labx1.place(relx=0.15, rely=0.3 + y0, width=260, height=30) + lab1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 20)) + if curx == -1: + text = "未选择" + else: + text = f"{d.Nodes[curx][1]}" + Specify_Nodes = str(d.Nodes[curx][1]) + lab2 = tk.Label(frame_left, text=text, bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 20)) + lab1.place(relx=0.15, rely=0.4 + y0, width=160, height=50) + lab2.place(relx=0.6, rely=0.4 + y0, width=100, height=50) + + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[30], bg='white', + command=lambda: self.node_del_exact()) # 删除当前节点 + button.place(relx=0.2, rely=0.5 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[32], bg='white', + command=lambda: self.node_add()) # 增加一个节点 + button2.place(relx=0.2, rely=0.6 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[31], bg='white', + command=lambda: self.node_del()) # 减少一个节点 + button3.place(relx=0.2, rely=0.7 + y0) + + def node_add(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + # 增加一个节点 + nodes = len(d.Nodes) + 1 + # 如果节点数目大于五,则将连接详细信息改为不显示 + if nodes > 5: + d.edgeinfo = 0 + if nodes > 10: + tk.messagebox.askokcancel(title='错误', message='已达到最大节点') + return + d = Data(1, nodes, d.styple) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + + def node_del(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + if d.nodes_num <= 2: + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(2, nodes, d.styple) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + def node_del_exact(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = d.Edges + if len(Specify_Nodes) == 3: + tk.messagebox.askokcancel(title='错误', message='未选择节点') + return + if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告 + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(3, nodes, d.styple) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + +def refresh(): + top = creat_window('路径生成') # 创建弹出窗口 + distance_entry = create_input_box(top, "节点个数:(1-10)", 6) # 创建一个输入框,获取节点个数 + # 创建一个下拉框,用于选择生成图类型 + mode_combobox = create_dropdown_box(top, "生成图类型", ["TSP", "完全图"]) # 创建一个下拉框,用于选择生成图类型 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + # 从下拉框中获取池化类型 + result2 = mode_combobox.get() + print(result1, result2) + flag = 0 + if result2!=None: + if result2 == '完全图': + flag = 1 + if 10 > result1 > 0: + global d + if flag: + d = Data(0, result1, 1) + else: + d = Data(0, result1, 0) + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='节点数范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的节点数') + + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + +def graph_init(): + node = random.randint(4, 10) + global d, MODE, MARK_NODE + MARK_NODE = 0 + MODE = 0 + d = Data(0, node,0) + print(node) + W.user_way = [0] + G.draw() + window(2) + +def empty(): + global MARK_NODE + MARK_NODE = 0 + G.delete_all() + +def auto_opera_mu(menu: tk.Menu): + """TSP问题自动生成与自动求解相关""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init()) + menu_node.add_command(label="重新生成", command=lambda: refresh()) + menu_node.add_command(label="清空", command=lambda: empty()) + menu_node.add_command(label="自动求解最优路径-dfs遍历", command=lambda: s.auto_solve(3)) + menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: s.auto_solve(2)) + menu_node.add_command(label="自动求解优化路径-回溯", command=lambda: s.auto_solve(1)) + menu_node.add_command(label="检查是否存在路径", command=lambda: s.check()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node) + +# 创建弹出窗口 +def creat_window(title): + top = tk.Toplevel(root) + top.geometry("300x350") + top.title(title) + return top + +# 输入框 +def create_input_box(top, text, value): + box_label = tk.Label(top, text=text) + box_label.pack(padx=10, pady=10) + box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3 + box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象 + box_size_entry.pack(padx=20, pady=20) + return box_size_entry + +# 下拉框 +def create_dropdown_box(top, text, listvalues): + # 创建一个下拉框,用于选择 + box_mode_label = tk.Label(top, text=text) + box_mode_label.pack(padx=10, pady=10) + box_mode_combobox = ttk.Combobox(top) + box_mode_combobox["values"] = listvalues + box_mode_combobox.pack(padx=20, pady=15) + return box_mode_combobox + +def dir2(x, y): # 计算两点间距离 + return (x[0] - y[0]) * (x[0] - y[0]) + (x[1] - y[1]) * (x[1] - y[1]) + +def left1(event): + global entry1, entry2, default_value, CLICK, entry3, entry4 + # 查找鼠标左键按下时位置是否在某个节点内 + n = -1 + for node in d.Nodes: + if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: + n = node[0] + # n为点击的节点,若没有则为-1 + m = mode.get() # 查看现在操作类型 + if m == 1 and not n == -1: + # 节点操作 + # N.node_mark(n) + if n >= len(d.Nodes): + # 弹出对话框 + tk.messagebox.askokcancel(title='错误', message='该节点不存在') + return + for node in d.Nodes: + if node[0] == n: + node[2] = 1 + else: + node[2] = 0 + G.draw() + if MARK_NODE == 1: # 限制节点操作 + N.node_mark_display(1) + elif MARK_NODE == 2: # 若是MARK_NODE==2,则可以进行路线画图操作 + if CLICK[2] == 0: + CLICK[0] = n + default_value.set(d.Nodes[n][1]) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + elif CLICK[2] == 1: + CLICK[1] = n + W.extract_ligature(CLICK[0], CLICK[1]) + CLICK[2] = -1 + CLICK[2] += 1 + elif MARK_NODE == 5: # 若是MARK_NODE==5,则可以进行节点连接操作 + if CLICK[2] == 0: + CLICK[0] = n + entry3.delete(0, tk.END) + # 设置输入框的值为 节点 + entry3.insert(0, d.Nodes[n][1]) + elif CLICK[2] == 1: + CLICK[1] = n + # W.extract_ligature(CLICK[0], CLICK[1]) + entry4.delete(0, tk.END) + # 设置输入框的值为 节点 + entry4.insert(0, d.Nodes[n][1]) + CLICK[2] = -1 + CLICK[2] += 1 + print(n) + +def element_reading(): + global List_Image + folder_path = 'K:/work/traveler/background/' + image_path = ['1_红.png', '1_绿.png', '1_蓝.png', '2_红.png', '2_绿.png', '2_蓝.png', '3_红.png', '3_绿.png', '3_蓝.png', + '4_红.png', '4_绿.png', '4_蓝.png', '5_红.png', '5_绿.png', '5_蓝.png', '6_红.png', '6_绿.png', '6_蓝.png', + '7_红.png', '7_绿.png', '7_蓝.png', '8_红.png', '8_绿.png', '8_蓝.png', '9_红.png', '9_绿.png', '9_蓝.png', + '10_红.png', '10_绿.png', '10_蓝.png', '删除节点.png', '减少节点.png', '增加节点.png', '线路增加.png', + '线路修改.png', '线路删除.png', '撤销.png', '清除路径.png', '确认.png', '编组9.png', '背景.png', + '矩形.png', '判断路径是否为贪心路径.png', '判断路径是否为最短路径.png', '矩形2.png', '完全图.png', '可达判断.png'] + for path in image_path[:30]: # 1-10球加载 + List_Image.append(element(folder_path+path, 50, 50)) + for path in image_path[30:36]: # 节点操作与连线操作功能按钮 + List_Image.append(element(folder_path + path, 280, 50)) + List_Image.append(element(folder_path + image_path[36], 140, 50)) # 撤销 + List_Image.append(element(folder_path + image_path[37], 140, 50)) # 清除路径 + List_Image.append(element(folder_path + image_path[38], 120, 50)) # 确认 + List_Image.append(element(folder_path + image_path[39], 380, 100)) # 编组_9 + List_Image.append(element(folder_path + image_path[40], 900, 900)) # 背景 + List_Image.append(element(folder_path + image_path[41], 380, 140)) # 小矩形 + List_Image.append(element(folder_path + image_path[42], 300, 50)) # 判断路径是否为贪心路径 + List_Image.append(element(folder_path + image_path[43], 300, 50)) # 判断路径是否为最短路径 + List_Image.append(element(folder_path + image_path[44], 380, 530)) # 大矩形 + List_Image.append(element(folder_path + image_path[45], 140, 50)) # 完全图 + List_Image.append(element(folder_path + image_path[46], 140, 50)) # 可达判断 + +def element(path, width, height): + # 加载图元对应的图片文件 + img = Image.open(path) + # 使用resize方法调整图片 + img = img.resize((width, height)) + # 把Image对象转换成PhotoImage对象 + img = ImageTk.PhotoImage(img) + # 保存图片的引用,防止被垃圾回收 + root.img = img + return img + +def window(event): + global MARK_NODE + MARK_NODE = 0 + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + Label_1 = tk.Label(frame_left, text="遍历节点数目", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_1.place(x=50, y=330, width=180, height=40) + Label_2 = tk.Label(frame_left, text="求解花费时间", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_2.place(x=50, y=450, width=180, height=40) + Label_3 = tk.Label(frame_left, text="最短路径", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_3.place(x=50, y=570, width=140, height=40) + if event == 2: + lab1 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab1.place(x=80, y=370, width=300, height=60) + lab2 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(x=80, y=490, width=300, height=60) + lab3 = tk.Label(frame_left, text=f"{1}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab3.place(x=80, y=610, width=300, height=140) + else: + lab1 = tk.Label(frame_left, text=f"{s.way_sum}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab1.place(x=80, y=370, width=300, height=60) + t = round(s.end - s.start, 4) + lab2 = tk.Label(frame_left, text=f"{t}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(x=80, y=490, width=300, height=60) + g_way = '' + if len(s.ans) > 0: + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + else: + g_way='1' + lab3 = tk.Label(frame_left, text=f"{g_way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab3.place(x=80, y=610, width=300, height=140) + +def left_window(): + background_button = tk.Label(frame_left, image=List_Image[39], bg='#FFFAF0') # 编组 + background_button.place(x=50, y=20, width=380, height=100) + background_button.bind("", window) + # # 创建 Label 组件来显示背景图片 + background_on = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[41]) # 小矩形背景 + background_on.place(x=50, y=140, width=380, height=140) + + if MARK_NODE == 1: + background_color = [BLACK, '#DCDCDC', '#DCDCDC'] + elif MARK_NODE == 5: + background_color = ['#DCDCDC', BLACK, '#DCDCDC'] + elif MARK_NODE == 2: + background_color = ['#DCDCDC', '#DCDCDC', BLACK] + else: + background_color = ['#DCDCDC', '#DCDCDC', '#DCDCDC'] + Label_button1 = tk.Label(frame_left, text="节点操作", bg='white', fg=background_color[0], font=('微软雅黑', 15)) + Label_button1.place(x=60, y=180, width=80, height=50) + Label_button1.bind("", N.node_mark_display) + Label_button2 = tk.Label(frame_left, text="连接操作", bg='white', fg=background_color[1], font=('微软雅黑', 15)) + Label_button2.place(x=200, y=180, width=80, height=50) + Label_button2.bind("", W.way_operate) + Label_button3 = tk.Label(frame_left, text="路径操作", bg='white', fg=background_color[2], font=('微软雅黑', 15)) + Label_button3.place(x=340, y=180, width=80, height=50) + Label_button3.bind("", W.way_route_markings) + + background_below = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[44]) # 大矩形背景 + background_below.place(x=50, y=300, width=380, height=530) + +if __name__ == '__main__': + root = Tk() # 设置主界面 + root.title("旅行商问题") # 设置标题 + root.geometry("1400x900") # 设置大小 + + root.resizable(0, 0) # 设置不能调整显示边框 + element_reading() + frame_top = tk.Frame(root, bg='white', width=1400, height=50, highlightthickness=0) + frame_top.place(x=0, y=0) # 绘制信息输出栏 + + frame_left = tk.Frame(root, bg='#FFFAF0', highlightthickness=0, width=500, height=850) # 左边栏 + frame_left.place(x=0, y=50) # 左边栏 + + cv = tk.Canvas(root, bg='#FFFAF0', bd=0, relief="flat", width=900, height=850, highlightthickness=0) + cv.place(x=500, y=50) # 放置绘图Canvas画布 + + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) # 背景 + + G = Graph() + mode = tk.IntVar(value=1) + main_menu = tk.Menu(root) + root.config(menu=main_menu) + cv.bind('', left1) + E = Edge() + W = Ways() + s = Solve() + N = Node() + auto_opera_mu(main_menu) + window(2) + G.lab_show(frame_top, 0, 0, 0) # 显示边信息 + root.mainloop() diff --git a/X5.py b/X5.py new file mode 100644 index 0000000..de1c6cb --- /dev/null +++ b/X5.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X5.py diff --git a/background/10_红.png b/background/10_红.png new file mode 100644 index 0000000..fb37986 Binary files /dev/null and b/background/10_红.png differ diff --git a/background/10_绿.png b/background/10_绿.png new file mode 100644 index 0000000..666e2ea Binary files /dev/null and b/background/10_绿.png differ diff --git a/background/10_蓝.png b/background/10_蓝.png new file mode 100644 index 0000000..6b97d97 Binary files /dev/null and b/background/10_蓝.png differ diff --git a/background/1_红.png b/background/1_红.png new file mode 100644 index 0000000..93195e0 Binary files /dev/null and b/background/1_红.png differ diff --git a/background/1_绿.png b/background/1_绿.png new file mode 100644 index 0000000..1c00032 Binary files /dev/null and b/background/1_绿.png differ diff --git a/background/1_蓝.png b/background/1_蓝.png new file mode 100644 index 0000000..e51067b Binary files /dev/null and b/background/1_蓝.png differ diff --git a/background/2_红.png b/background/2_红.png new file mode 100644 index 0000000..2943ce3 Binary files /dev/null and b/background/2_红.png differ diff --git a/background/2_绿.png b/background/2_绿.png new file mode 100644 index 0000000..f2c3c94 Binary files /dev/null and b/background/2_绿.png differ diff --git a/background/2_蓝.png b/background/2_蓝.png new file mode 100644 index 0000000..3cc48f6 Binary files /dev/null and b/background/2_蓝.png differ diff --git a/background/3_红.png b/background/3_红.png new file mode 100644 index 0000000..1842e1f Binary files /dev/null and b/background/3_红.png differ diff --git a/background/3_绿.png b/background/3_绿.png new file mode 100644 index 0000000..1166baf Binary files /dev/null and b/background/3_绿.png differ diff --git a/background/3_蓝.png b/background/3_蓝.png new file mode 100644 index 0000000..c75e417 Binary files /dev/null and b/background/3_蓝.png differ diff --git a/background/4_红.png b/background/4_红.png new file mode 100644 index 0000000..d0287cb Binary files /dev/null and b/background/4_红.png differ diff --git a/background/4_绿.png b/background/4_绿.png new file mode 100644 index 0000000..27218e8 Binary files /dev/null and b/background/4_绿.png differ diff --git a/background/4_蓝.png b/background/4_蓝.png new file mode 100644 index 0000000..0190f5d Binary files /dev/null and b/background/4_蓝.png differ diff --git a/background/5_红.png b/background/5_红.png new file mode 100644 index 0000000..362f771 Binary files /dev/null and b/background/5_红.png differ diff --git a/background/5_绿.png b/background/5_绿.png new file mode 100644 index 0000000..3c9976b Binary files /dev/null and b/background/5_绿.png differ diff --git a/background/5_蓝.png b/background/5_蓝.png new file mode 100644 index 0000000..9b4ebc4 Binary files /dev/null and b/background/5_蓝.png differ diff --git a/background/6_红.png b/background/6_红.png new file mode 100644 index 0000000..0aab8f6 Binary files /dev/null and b/background/6_红.png differ diff --git a/background/6_绿.png b/background/6_绿.png new file mode 100644 index 0000000..669ac36 Binary files /dev/null and b/background/6_绿.png differ diff --git a/background/6_蓝.png b/background/6_蓝.png new file mode 100644 index 0000000..5c7d00e Binary files /dev/null and b/background/6_蓝.png differ diff --git a/background/7_红.png b/background/7_红.png new file mode 100644 index 0000000..bd9c5ac Binary files /dev/null and b/background/7_红.png differ diff --git a/background/7_绿.png b/background/7_绿.png new file mode 100644 index 0000000..80cbf59 Binary files /dev/null and b/background/7_绿.png differ diff --git a/background/7_蓝.png b/background/7_蓝.png new file mode 100644 index 0000000..1fc793c Binary files /dev/null and b/background/7_蓝.png differ diff --git a/background/8_红.png b/background/8_红.png new file mode 100644 index 0000000..1aaab5b Binary files /dev/null and b/background/8_红.png differ diff --git a/background/8_绿.png b/background/8_绿.png new file mode 100644 index 0000000..3fedf37 Binary files /dev/null and b/background/8_绿.png differ diff --git a/background/8_蓝.png b/background/8_蓝.png new file mode 100644 index 0000000..8db60b7 Binary files /dev/null and b/background/8_蓝.png differ diff --git a/background/9_红.png b/background/9_红.png new file mode 100644 index 0000000..6cea904 Binary files /dev/null and b/background/9_红.png differ diff --git a/background/9_绿.png b/background/9_绿.png new file mode 100644 index 0000000..345ddc2 Binary files /dev/null and b/background/9_绿.png differ diff --git a/background/9_蓝.png b/background/9_蓝.png new file mode 100644 index 0000000..ae10a71 Binary files /dev/null and b/background/9_蓝.png differ diff --git a/background/减少节点.png b/background/减少节点.png new file mode 100644 index 0000000..cd9420b Binary files /dev/null and b/background/减少节点.png differ diff --git a/background/删除节点.png b/background/删除节点.png new file mode 100644 index 0000000..1f06b8b Binary files /dev/null and b/background/删除节点.png differ diff --git a/background/判断路径是否为最短路径.png b/background/判断路径是否为最短路径.png new file mode 100644 index 0000000..7f404f4 Binary files /dev/null and b/background/判断路径是否为最短路径.png differ diff --git a/background/判断路径是否为贪心路径.png b/background/判断路径是否为贪心路径.png new file mode 100644 index 0000000..8510710 Binary files /dev/null and b/background/判断路径是否为贪心路径.png differ diff --git a/background/可达判断.png b/background/可达判断.png new file mode 100644 index 0000000..9828a91 Binary files /dev/null and b/background/可达判断.png differ diff --git a/background/增加节点.png b/background/增加节点.png new file mode 100644 index 0000000..8a81a38 Binary files /dev/null and b/background/增加节点.png differ diff --git a/background/完全图.png b/background/完全图.png new file mode 100644 index 0000000..a2844bc Binary files /dev/null and b/background/完全图.png differ diff --git a/background/撤销.png b/background/撤销.png new file mode 100644 index 0000000..64430b3 Binary files /dev/null and b/background/撤销.png differ diff --git a/background/未选择.png b/background/未选择.png new file mode 100644 index 0000000..8469c4e Binary files /dev/null and b/background/未选择.png differ diff --git a/background/清除路径.png b/background/清除路径.png new file mode 100644 index 0000000..4e11faf Binary files /dev/null and b/background/清除路径.png differ diff --git a/background/矩形 2.png b/background/矩形 2.png new file mode 100644 index 0000000..e4e669c Binary files /dev/null and b/background/矩形 2.png differ diff --git a/background/矩形.png b/background/矩形.png new file mode 100644 index 0000000..1fb46e0 Binary files /dev/null and b/background/矩形.png differ diff --git a/background/矩形2.png b/background/矩形2.png new file mode 100644 index 0000000..e4e669c Binary files /dev/null and b/background/矩形2.png differ diff --git a/background/确认.png b/background/确认.png new file mode 100644 index 0000000..0454121 Binary files /dev/null and b/background/确认.png differ diff --git a/background/线路修改.png b/background/线路修改.png new file mode 100644 index 0000000..1fd5848 Binary files /dev/null and b/background/线路修改.png differ diff --git a/background/线路删除.png b/background/线路删除.png new file mode 100644 index 0000000..9b3e382 Binary files /dev/null and b/background/线路删除.png differ diff --git a/background/线路增加.png b/background/线路增加.png new file mode 100644 index 0000000..bf46196 Binary files /dev/null and b/background/线路增加.png differ diff --git a/background/编组9.png b/background/编组9.png new file mode 100644 index 0000000..c053d6a Binary files /dev/null and b/background/编组9.png differ diff --git a/background/编组_9.png b/background/编组_9.png new file mode 100644 index 0000000..904529a Binary files /dev/null and b/background/编组_9.png differ diff --git a/background/背景.png b/background/背景.png new file mode 100644 index 0000000..baa75cd Binary files /dev/null and b/background/背景.png differ diff --git a/demo.py b/demo.py new file mode 100644 index 0000000..b101df7 --- /dev/null +++ b/demo.py @@ -0,0 +1,36 @@ +class Ways(): + def way_change(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + top = creat_window(str_route + '路径参数修改') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长 + def get_input(): # 创建 + ···与前面way_add里get_input相似 + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') \ No newline at end of file diff --git a/demo11.py b/demo11.py new file mode 100644 index 0000000..92d21cb --- /dev/null +++ b/demo11.py @@ -0,0 +1,21 @@ +# 导入aip模块,这是一个来自百度AI平台的Python SDK,可以用来调用百度NLP(自然语言处理)服务 +from aip import AipNlp +# 定义一个名为address的函数,它接受一个字符串参数addr,默认为空字符串 +def address(addr=''): + # 在这个函数中定义了APP_ID、API_KEY和SECRET_KEY三个变量,这些变量是你在百度AI平台上申请的凭证 + APP_ID = '41768293' + API_KEY = 'CAnsdV8e40GQtjhK4qHkucUO' + SECRET_KEY = 'jU1NbbnwrhTahngr2p3cz8p2EIAa8zWj' + + # 使用凭证创建一个AipNlp的客户端对象,这个对象会用来发送请求到百度AI平台 + client = AipNlp(APP_ID, API_KEY, SECRET_KEY) + """ 调用词法分析 """ + # 调用客户端对象的address方法,将参数addr发送到百度AI平台进行地址识别,返回的结果保存在变量re中 + re = client.address(addr) + # 返回识别的结果 + + return re + + # 调用address函数,参数为你要识别的具体地址,并将返回的结果打印出来 + +print(address('海南省三亚市吉阳区迎宾大道学院路三亚学院')) \ No newline at end of file diff --git a/main.py b/main.py index 2871150..dbd3a0e 100644 --- a/main.py +++ b/main.py @@ -1,248 +1,61 @@ -# -*- encoding: utf-8 -*- -from NODE import * -from SOLVE import * -from EDGE import * -from WAYS import * - -N = Node() -E = Edge() -W = Ways() - -mode = tk.IntVar(value=1) -# 目前的操作状态: -# 1为节点操作 -# 2为链接操作 -# 3为路径操作 -# 默认为1 - - -RIGHT = -1 -B = tk.Button(frame, text="修改", command=lambda: N.node_set()) -B.place(x=180, y=10, width=50, height=30) - - -def left1(event): - # 查找鼠标左键按下时位置是否在某个节点内 - n = -1 - for node in d.Nodes: - if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: - n = node[0] - - # n为点击的节点,若没有则为-1 - m = mode.get() # 查看现在操作类型 - if m == 1 and not n == -1: - # 节点操作 - - N.node_mark(n) - elif m == 2 and not n == -1: - # 链接操作 - if len(E.mark) == 0: - E.mark.append(n) - E.node_mark1(E.mark[0]) - elif len(E.mark) == 1: - E.mark.append(n) - E.node_mark1(E.mark[0]) - E.node_mark2(E.mark[1]) - elif not n == E.mark[1]: - n1 = E.mark[1] - E.mark.clear() - E.mark.append(n1) - E.mark.append(n) - E.node_mark1(E.mark[0]) - E.node_mark2(E.mark[1]) - # 路径操作 - elif m == 3 and not n == -1: - W.user_way_add(n) - # 自动判断操作 - elif m == 4 and not n == -1: - if len(solve.mark) == 0: - solve.mark.append(n) - solve.node_mark1(solve.mark[0]) - elif len(solve.mark) == 1: - solve.mark.append(n) - solve.node_mark1(solve.mark[0]) - solve.node_mark2(solve.mark[1]) - elif not n == solve.mark[1]: - n1 = solve.mark[1] - solve.mark.clear() - solve.mark.append(n1) - solve.mark.append(n) - solve.node_mark1(solve.mark[0]) - solve.node_mark2(solve.mark[1]) - - -def onRightButtonUp(event): - global RIGHT - # 查找鼠标右键按下时位置是否在某个节点内 - n = -1 - for node in d.Nodes: - if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: - n = node[0] - if n == -1: - menu.post(event.x_root, event.y_root) - else: - RIGHT = n - menu_node.post(event.x_root, event.y_root) - - -# 右键菜单 -def m_node(): - mode.set(1) - W.clear() - N.node_mark_display() - - -def m_edge(): - mode.set(2) - W.clear() - E.edge_mark_display() - - -def m_ways(): - mode.set(3) - W.user_way_clear() - W.way_mark_display() - - -def m_info(): - d.edgeinfo ^= 1 - G.draw() - - -def m_solve(): - W.clear() - mode.set(4) - solve.Solve_display() - - -def graph_init(): - nodes = random.randint(4, 10) - N.node_init(nodes) - G.draw() - N.node_mark_display() - - -def sourc_set(num): - W.clear() - d.source = num - N.node_init(nodes) - G.draw() - - -def node_del(num): - N.node_mark(num) - N.node_del_exact() - - -def node_name(num): - N.node_mark(num) - N.node_rename() - - -def node_condition(num): - d.Nodes[num][4] ^= 1 - G.draw() - - -menu = tk.Menu(root, tearoff=0) -menu.add_command(label='节点操作', command=m_node) -menu.add_command(label='连接操作', command=m_edge) -menu.add_command(label='路径操作', command=m_ways) -menu.add_command(label='显示/隐藏链接信息', command=m_info) - -menu.add_command(label='自动判断相关', command=m_solve) -fmenu1 = Menu(menu, tearoff=False) -fmenu1.add_command(label='使用模拟数据', command=lambda: sourc_set(1)) -fmenu1.add_command(label='使用黑龙江省数据', command=lambda: sourc_set(2)) -fmenu1.add_command(label='使用辽宁省数据', command=lambda: sourc_set(3)) -menu.add_cascade(label="切换数据源", menu=fmenu1) - -menu_node = tk.Menu(root, tearoff=0) -menu_node.add_command(label='删除节点', command=lambda: node_del(RIGHT)) -menu_node.add_command(label='切换必经/非必经', command=lambda: node_condition(RIGHT)) -menu_node.add_command(label='重命名节点', command=lambda: node_name(RIGHT)) -# menu_node.add_command(label='显示/隐藏链接信息', command=m_info) - -cv.bind('', left1) -cv.bind('', onRightButtonUp) - - -# 释放右键时打开菜单 - -def auto_opera_mu(menu: tk.Menu): - """TSP问题自动生成与自动求解相关""" - menu_node = Menu(menu, tearoff=False) - menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init()) - menu_node.add_command(label="重新生成", command=lambda: N.node_refresh()) - menu_node.add_command(label="自动求解最优路径-遍历", command=lambda: solve.dfs_panel()) - menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: solve.greedy_panel()) - menu_node.add_command(label="检查是否存在路径", command=lambda: solve.check()) - - # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 - menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node) - - -def node_opera_mu(menu: tk.Menu): - """节点操作""" - - # fmenu1 = Menu(menu, tearoff=False) - # for i in range(10): - # fmenu1.add_command(label='选择节点 ' + str(i + 1), command=lambda num=i: node_mark(num)) - - menu_node = Menu(menu, tearoff=False) - menu_node.add_command(label="添加一个节点", command=lambda: N.node_add()) - menu_node.add_command(label="删除一个节点", command=lambda: N.node_del()) - # menu_node.add_cascade(label="选择一个节点", menu=fmenu1) - menu_node.add_command(label="选择一个节点", command=lambda: N.node_mark_display()) - # menu_node.add_command(label="停止标记一个节点", command=lambda: node_mark_display1()) - # menu_node.add_command(label="设置节点标签", command=lambda:node_rename()) - - # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 - menu.add_cascade(label="节点操作", menu=menu_node) - - -def edge_opera_mu(menu: tk.Menu): - """连接操作""" - - # fmenu1 = Menu(menu, tearoff=False) - # for i in range(10): - # fmenu1.add_command(label='选择另一个节点 ' + str(i + 1), command=lambda num=i: node_mark2(num)) - # - menu_node = Menu(menu, tearoff=False) - # menu_node.add_cascade(label="选择另一个节点", menu=fmenu1) - - menu_node.add_command(label="选择一个连接", command=lambda: E.edge_mark_display()) - - # menu_node.add_command(label="建立一个连接",command=lambda :edge_add()) - menu_node.add_command(label="删除所有连接", command=lambda: E.edge_delall()) - menu_node.add_command(label="删除所有多余连接", command=lambda: E.newedge_del_all(1)) - # menu_node.add_command(label="设置连接标签", command=lambda: edge_rename()) - # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 - menu.add_cascade(label="连接操作", menu=menu_node) - - -def path_opera_mu(menu: tk.Menu): - """路径操作""" - menu_node = Menu(menu, tearoff=False) - menu_node.add_command(label="开始标记路径", command=lambda: W.way_mark_display()) - menu_node.add_command(label="撤销一步路径", command=lambda: W.user_way_del()) - menu_node.add_command(label="取消所有路径标记", command=lambda: W.user_way_clear()) - # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 - menu.add_cascade(label="路径操作", menu=menu_node, ) - - -auto_opera_mu(main_menu) -node_opera_mu(main_menu) -edge_opera_mu(main_menu) -path_opera_mu(main_menu) - - -def main(): - G = Graph() - G.draw() - sourc_set(2) - mainloop() - - -if __name__ == '__main__': - main() +import sys + +import numpy as np + + +def tsp_nearest_neighbor1(distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = sys.maxsize + nearest_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + nearest_city = city + + if nearest_city is not None: + path.append(nearest_city) + visited[nearest_city] = True + + path.append(0) # 回到起始城市形成闭合路径 + return path +def tsp_nearest_neighbor(distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = sys.maxsize + nearest_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + nearest_city = city + + if nearest_city is not None: + path.append(nearest_city) + visited[nearest_city] = True + + path.append(0) # 回到起始城市形成闭合路径 + return path +# 示例用法 +distances = np.array([ + [np.inf, 50, 99, np.inf, 64, np.inf], + [50, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +path = tsp_nearest_neighbor1(distances) +print(path) \ No newline at end of file diff --git a/test/DP.py b/test/DP.py new file mode 100644 index 0000000..55d25cc --- /dev/null +++ b/test/DP.py @@ -0,0 +1,49 @@ +import numpy as np + +def tsp_dp(distances): + n = len(distances) + dp = np.full((1 << n, n), np.inf) + path = np.zeros((1 << n, n), dtype=int) + + for i in range(n): + dp[1 << i][i] = 0 + + for S in range(1, 1 << n): + for j in range(n): + if not (S & (1 << j)): + continue + for k in range(n): + if k == j or not (S & (1 << k)): + continue + if dp[S][j] > dp[S - (1 << j)][k] + distances[k][j]: + dp[S][j] = dp[S - (1 << j)][k] + distances[k][j] + path[S][j] = k + + final_path = [] + S = (1 << n) - 1 + j = 0 + while S: + final_path.append(j) + k = path[S][j] + S -= 1 << j + j = k + final_path.append(0) + + ans = float('inf') + for j in range(n): # 枚举所有结束点 + if distances[j][0] != np.inf: # 如果结束点j与起点0相连 + ans = min(ans, dp[(1 << n) - 1][j] + distances[j][0]) + + return ans, final_path +# 示例使用 +distances = np.array([ + [np.inf, 50, 99, np.inf, 64, np.inf], + [50, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) +min_cost, min_path = tsp_dp(distances) +print("Minimum cost:", min_cost) +print("Minimum path:", min_path) \ No newline at end of file diff --git a/test/Prin.py b/test/Prin.py new file mode 100644 index 0000000..9b6f010 --- /dev/null +++ b/test/Prin.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/3 17:42 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : Prin.py + +import numpy as np + + +def prim_exists_path(distances): + num_cities = distances.shape[0] + visited = set() + pq = [] + start_node = 0 + + visited.add(start_node) + for neighbor in range(num_cities): + if neighbor != start_node and np.isfinite(distances[start_node, neighbor]): + pq.append((distances[start_node, neighbor], start_node, neighbor)) + + pq.sort(key=lambda x: x[0]) + + while pq: + weight, node1, node2 = pq.pop(0) + if node2 not in visited: + visited.add(node2) + for neighbor in range(num_cities): + if neighbor != node2 and np.isfinite(distances[node2, neighbor]): + pq.append((distances[node2, neighbor], node2, neighbor)) + + pq.sort(key=lambda x: x[0]) + + return len(visited) == num_cities + + +distances = np.array([ + [np.inf, 50, 99, np.inf, 64, np.inf], + [50, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +print(prim_exists_path(distances)) \ No newline at end of file diff --git a/test/demo1.py b/test/demo1.py new file mode 100644 index 0000000..33389fa --- /dev/null +++ b/test/demo1.py @@ -0,0 +1,76 @@ +import random + +def initialize_population(num_points, pop_size): + population = [] + for _ in range(pop_size): + individual = list(range(num_points)) + random.shuffle(individual) + population.append(individual) + return population + +def evaluate_fitness(individual, distances): + fitness = 0 + num_points = len(individual) + for i in range(num_points): + city1 = individual[i] + city2 = individual[(i + 1) % num_points] + fitness += distances[city1][city2] + return fitness + +def crossover(parent1, parent2): + child = [-1] * len(parent1) + start_idx = random.randint(0, len(parent1)-1) + end_idx = random.randint(start_idx+1, len(parent1)) + child[start_idx:end_idx] = parent1[start_idx:end_idx] + for city in parent2: + if city not in child: + for i in range(len(child)): + if child[i] == -1: + child[i] = city + break + return child + +def mutate(individual): + idx1, idx2 = random.sample(range(len(individual)), 2) + individual[idx1], individual[idx2] = individual[idx2], individual[idx1] + +def genetic_algorithm(distances, pop_size, num_generations): + num_points = len(distances) + population = initialize_population(num_points, pop_size) + + for generation in range(num_generations): + population_fitness = [] + for individual in population: + fitness = evaluate_fitness(individual, distances) + population_fitness.append((individual, fitness)) + + population_fitness.sort(key=lambda x: x[1]) # 按适应度排序 + elite_individual = population_fitness[0][0] + new_population = [elite_individual] + + while len(new_population) < pop_size: + parent1, parent2 = random.choices(population_fitness, k=2) + child = crossover(parent1[0], parent2[0]) + mutate(child) + new_population.append(child) + + population = new_population + + best_individual = population_fitness[0][0] + return best_individual + +# 示例使用 +# distances为距离矩阵 +distances = [ + [0, 50, 99, 0, 64, 0], + [50, 0, 12, 45, 74, 13], + [99, 12, 0, 25, 0, 61], + [0, 45, 25, 0, 45, 47], + [64, 74, 0, 45, 0, 0], + [0, 13, 61, 47, 0, 0] +] +population_size = 100 +num_generations = 1000 + +best_solution = genetic_algorithm(distances, population_size, num_generations) +print(best_solution) \ No newline at end of file diff --git a/test/demo10.py b/test/demo10.py new file mode 100644 index 0000000..75e42ce --- /dev/null +++ b/test/demo10.py @@ -0,0 +1,46 @@ +import numpy as np + +inf = float('inf') + +distances = np.array([ + [inf, 72, inf, inf, 81, 67], + [72, inf, 26, inf, 61, inf], + [inf, 26, inf, 53, 97, inf], + [inf, inf, 53, inf, inf, inf], + [81, 61, 97, inf, inf, 38], + [67, inf, inf, inf, 38, inf] +]) + +def find_cycles(start, distances): + n = len(distances) + path = [start] + cycles = [] + + def is_valid(node, position): + if distances[path[position - 1]][node] == inf: + return False + if node in path: + return False + return True + + def find_paths(node): + for next_node in range(n): + if is_valid(next_node, len(path)): + path.append(next_node) + if len(path) < n: + find_paths(next_node) + elif len(path) == n and distances[path[-1]][start] != inf: + cycles.append(path.copy()) + path.pop() + + find_paths(start) + return cycles + +all_cycles = find_cycles(0, distances) + +if all_cycles: + print("经过全部节点能返回起点的回路的全部路径为:") + for cycle in all_cycles: + print(cycle + [cycle[0]]) # 添加起点以形成完整的回路 +else: + print("不存在经过全部节点能返回起点的回路") \ No newline at end of file diff --git a/test/demo11.py b/test/demo11.py new file mode 100644 index 0000000..29d6ab6 --- /dev/null +++ b/test/demo11.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/9 11:26 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : demo11.py + +import os + +# 遍历指定文件夹 +def rename_files(folder_path): + for root, dirs, files in os.walk(folder_path): + for file in files: + # 检查文件类型是否为图片类型(这里使用了简单的判断方式) + if file.endswith('.jpg') or file.endswith('.png'): + # 获取当前文件所在的子文件夹名称 + subdir_name = os.path.basename(root) + # 组合新的文件名,添加子文件夹名称前缀 + new_file_name = subdir_name + '_' + file + # 使用 os.rename() 方法来重命名文件 + os.rename(os.path.join(root, file), os.path.join(root, new_file_name)) + +# 调用函数来重命名文件 +rename_files('D:/weixin/WeChat Files/wxid_f2yvfdxbyuag22/FileStorage/File/2023-11/球/') \ No newline at end of file diff --git a/test/demo12.py b/test/demo12.py new file mode 100644 index 0000000..b16d0ab --- /dev/null +++ b/test/demo12.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/9 11:28 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : demo12.py + +import os +import shutil + +# 遍历指定文件夹并复制图片文件到新的文件夹 +def copy_images(folder_path, target_folder_path): + # 创建目标文件夹(如果它不存在) + if not os.path.exists(target_folder_path): + os.makedirs(target_folder_path) + + for root, dirs, files in os.walk(folder_path): + for file in files: + # 检查文件类型是否为图片类型(这里使用了简单的判断方式) + if file.endswith('.jpg') or file.endswith('.png'): + # 构造源文件的完整路径 + source_file_path = os.path.join(root, file) + # 构造目标文件的完整路径 + target_file_path = os.path.join(target_folder_path, file) + # 使用 shutil 模块的 copy2() 函数复制文件(保留源文件的元数据) + shutil.copy2(source_file_path, target_file_path) + +# 调用函数来复制图片文件 +copy_images('D:/weixin/WeChat Files/wxid_f2yvfdxbyuag22/FileStorage/File/2023-11/球/', 'K:/工作/traveler/background/') \ No newline at end of file diff --git a/test/demo13.py b/test/demo13.py new file mode 100644 index 0000000..1f2ce65 --- /dev/null +++ b/test/demo13.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/9 11:54 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : demo13.py + +import os + +# 读取文件夹中的图像文件名到列表 +def get_image_filenames(folder_path): + image_filenames = [] + + for file_name in os.listdir(folder_path): + # 检查文件类型是否为图像类型(这里使用了简单的判断方式) + if file_name.endswith('.jpg') or file_name.endswith('.png'): + # 将文件名添加到列表中 + image_filenames.append(file_name) + + return image_filenames + +# 调用函数来获取图像文件列表 +image_files = get_image_filenames('K:/工作/traveler/background/') +print(image_files) \ No newline at end of file diff --git a/test/demo2.py b/test/demo2.py new file mode 100644 index 0000000..a63d93a --- /dev/null +++ b/test/demo2.py @@ -0,0 +1,106 @@ +import numpy as np + +class AntColonyOptimization: + def __init__(self, distances, num_ants=10, num_iterations=100, alpha=1, beta=1, evaporation=0.5, q0=0.9): + self.distances = distances + self.num_ants = num_ants + self.num_iterations = num_iterations + self.alpha = alpha + self.beta = beta + self.evaporation = evaporation + self.q0 = q0 + + def optimize(self): + num_cities = len(self.distances) + pheromone = np.ones((num_cities, num_cities)) + best_path = None + best_length = float('inf') + + for _ in range(self.num_iterations): + paths = [] + lengths = [] + + for _ in range(self.num_ants): + path = self.construct_path(pheromone) + length = self.calculate_length(path) + paths.append(path) + lengths.append(length) + + if length < best_length: + best_length = length + best_path = path + + self.update_pheromone(pheromone, paths, lengths) + + return best_path + + def construct_path(self, pheromone): + num_cities = len(pheromone) + path = [0] # 起始城市为0 + visited = np.zeros(num_cities, dtype=bool) + visited[0] = True + + for _ in range(num_cities - 1): + probs = self.calculate_probs(pheromone, path[-1], visited) + next_city = self.choose_next_city(probs) + + path.append(next_city) + visited[next_city] = True + + return path + + def calculate_probs(self, pheromone, curr_city, visited): + num_cities = len(pheromone) + probs = np.zeros(num_cities) + + denominator = 0 + for city in range(num_cities): + if not visited[city]: + denominator += pheromone[curr_city, city] ** self.alpha + (1.0 / self.distances[curr_city, city]) ** self.beta + + for city in range(num_cities): + if not visited[city]: + numerator = pheromone[curr_city, city] ** self.alpha + (1.0 / self.distances[curr_city, city]) ** self.beta + probs[city] = numerator / denominator + + return probs + + def choose_next_city(self, probs): + q = np.random.rand() + + if q < self.q0: + next_city = np.argmax(probs) + else: + next_city = np.random.choice(len(probs), p=probs) + + return next_city + + def calculate_length(self, path): + length = 0 + for i in range(len(path) - 1): + length += self.distances[path[i], path[i+1]] + + return length + + def update_pheromone(self, pheromone, paths, lengths): + pheromone *= self.evaporation + + for i, path in enumerate(paths): + for j in range(len(path) - 1): + city1 = path[j] + city2 = path[j+1] + pheromone[city1, city2] += 1.0 / lengths[i] + pheromone[city2, city1] += 1.0 / lengths[i] + +distances = np.array([ + [np.inf, 50, 99, np.inf, 64, np.inf], + [50, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +aco = AntColonyOptimization(distances) +best_path = aco.optimize() +print(best_path) \ No newline at end of file diff --git a/test/demo3.py b/test/demo3.py new file mode 100644 index 0000000..bee3ea6 --- /dev/null +++ b/test/demo3.py @@ -0,0 +1,66 @@ +import numpy as np + +def tsp_backtracking(distances): + num_cities = len(distances) + path = [0] # 起始城市为0 + visited = [False] * num_cities + visited[0] = True + min_distance = float('inf') + shortest_path = None + + def backtrack(curr_city, curr_distance, visited, path): + nonlocal min_distance, shortest_path + if len(path) == num_cities: + # 到达所有城市,更新最短路径 + if curr_distance + distances[curr_city][0] < min_distance: + min_distance = curr_distance + distances[curr_city][0] + shortest_path = path + [0] + else: + for next_city in range(num_cities): + if not visited[next_city]: + # 选择下一个未访问的城市 + visited[next_city] = True + path.append(next_city) + new_distance = curr_distance + distances[curr_city][next_city] + # 剪枝条件:当前路径已经大于最短路径,不继续搜索 + if new_distance < min_distance: + backtrack(next_city, new_distance, visited, path) + # 回溯 + visited[next_city] = False + path.pop() + backtrack(0, 0, visited, path) + return shortest_path +# 示例用法 +# distances = np.array([ +# [np.inf, 50, 99, np.inf, 64, np.inf], +# [50, np.inf, 12, 45, 74, 13], +# [99, 12, np.inf, 25, np.inf, 61], +# [np.inf, 45, 25, np.inf, 45, 47], +# [64, 74, np.inf, 45, np.inf, np.inf], +# [np.inf, 13, 61, 47, np.inf, np.inf] +# ]) +# inf = np.inf +# distances = np.array( +# [[inf, 69, 48, inf, 41, 78], +# [69, inf, 48, 53, 91, inf], +# [48, 48, inf, 19, 86, inf], +# [inf, 53, 19, inf, 96, 10], +# [41, 91, 86, 96, inf, inf], +# [78, inf, inf, 10, inf, inf]] +# ) + +inf = float('inf') + +distances = np.array([ + [inf, 72, inf, inf, 81, 67], + [72, inf, 26, inf, 61, inf], + [inf, 26, inf, 53, 97, inf], + [inf, inf, 53, inf, inf, inf], + [81, 61, 97, inf, inf, 38], + [67, inf, inf, inf, 38, inf] +]) +path = tsp_backtracking(distances) +if path: + print(path) +else: + print('不存在') \ No newline at end of file diff --git a/test/demo5.py b/test/demo5.py new file mode 100644 index 0000000..6629168 --- /dev/null +++ b/test/demo5.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/31 11:01 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : demo5.py + +import numpy as np + +inf = float('inf') + +distances = np.array([[inf, 71, 88, 28, 67, inf], [71, inf, inf, inf, 51, 98], [88, inf, inf, 70, inf, inf], [28, inf, 70, inf, 90, 75], [67, 51, inf, 90, inf, inf], [inf, 98, inf, 75, inf, inf]]) + +def find_hamiltonian_cycles(start, distances): + n = len(distances) + path = [start] + cycles = [] + + def is_valid(node, position): + if distances[path[position - 1]][node] == inf: + return False + if node in path: + return False + return True + + def find_paths(node): + for next_node in range(n): + if is_valid(next_node, len(path)): + path.append(next_node) + if len(path) < n: + find_paths(next_node) + elif len(path) == n and distances[path[-1]][start] != inf: + cycles.append(path + [start]) + path.pop() + + find_paths(start) + return cycles + +all_cycles = find_hamiltonian_cycles(0, distances) + +if all_cycles: + print("经过全部节点能返回起点的回路的全部路径为:") + for cycle in all_cycles: + print(cycle) # 输出所有回路路径 +else: + print("不存在经过全部节点能返回起点的回路") + diff --git a/test/demo6.py b/test/demo6.py new file mode 100644 index 0000000..1ae1e8f --- /dev/null +++ b/test/demo6.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/3 17:21 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : demo6.py + +import sys +import itertools + +import numpy as np + + +def tsp_dp(distances): + n = len(distances) + dp = [[sys.maxsize] * n for _ in range(1 << n)] + dp[1][0] = 0 + + for mask in range(1, 1 << n): + for current in range(n): + if mask & (1 << current): + for previous in range(n): + if previous != current and mask & (1 << previous): + dp[mask][current] = min(dp[mask][current], dp[mask ^ (1 << current)][previous] + distances[previous][current]) + + shortest_path = sys.maxsize + end_mask = (1 << n) - 1 + last_city = -1 + + for i in range(1, n): + if shortest_path > dp[end_mask][i] + distances[i][0]: + shortest_path = dp[end_mask][i] + distances[i][0] + last_city = i + + path = [] + mask = end_mask + while last_city >= 0: + path.append(last_city) + new_mask = mask ^ (1 << last_city) + last_city = -1 + for i in range(n): + if new_mask & (1 << i): + if last_city == -1 or dp[mask][last_city] + distances[last_city][i] == dp[mask][i]: + last_city = i + mask = new_mask + + path.append(0) + path.reverse() + + return shortest_path, path +# +# # 测试代码 +# distances = [ +# [0, 2, 9, 10], +# [1, 0, 6, 4], +# [15, 7, 0, 8], +# [6, 3, 12, 0] +# ] + + + +# 测试代码 +distances = np.array([ + [np.inf, 80, 99, np.inf, 64, np.inf], + [80, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +shortest_path, path = tsp_dp(distances) +print("最短路径长度为:", shortest_path) +print("最短路径为:", path) \ No newline at end of file diff --git a/test/demo7.py b/test/demo7.py new file mode 100644 index 0000000..2a7a1e0 --- /dev/null +++ b/test/demo7.py @@ -0,0 +1,119 @@ +from docx import Document +from docx.oxml import OxmlElement +from docx.oxml.ns import qn +from operator import eq + +flag = True + +def get_paragraph_shading(p): + """ + Get paragraph`s shading + Usage: + get_paragraph_shading(paragraph) + """ + p_pr = p._p.get_or_add_pPr() # 获取段落的pPr + # print(p_pr) + p_shd = p_pr.first_child_found_in("w:shd") # 找到 'w:shd' 标签 + # 如果不存在,就创建 + if p_shd is None: + p_shd = OxmlElement('w:shd') + p_pr.append(p_shd) + # 获取各属性的值 + val = p_shd.get(qn('w:val')) + # color = p_shd.get(qn('w:color')) + fill = p_shd.get(qn('w:fill')) + print([val, fill]) + return [val, fill] + +def get_paragraph_boders(p): + """ + Get paragraph`s boders + Usage: + get_paragraph_boders(paragraph) + """ + p_pr = p._p.get_or_add_pPr() # 获取段落的pPr + p_borders = p_pr.first_child_found_in("w:pBdr") # 找到 'p_borders' 标签 + # 如果不存在,就创建 + if p_borders is None: + p_borders = OxmlElement('w:pBdr') + p_pr.append(p_borders) + # 获取各属性的值 + borders = [] + for edge in ('left', 'top', 'right', 'bottom'): + element = p_borders.find(qn('w:{}'.format(edge))) + if element is None: + element = OxmlElement('w:{}'.format(edge)) + p_borders.append(element) + border = [] + for key in ["sz", "val", "color"]: + temp = str(element.get(qn('w:{}'.format(key)))) + if temp == 'none' or temp == '0' or temp == 'auto' or temp == 'NONE': + temp = 'None' + border.append(temp) + borders.append(border) + return borders + +def get_text_borders(p): + t_pr = p.runs[0].element.rPr + + t_bor = t_pr.first_child_found_in("w:bdr") # 找到 'w:bdr' 标签 + # 如果不存在,就创建 + if t_bor is None: + t_bor = OxmlElement('w:bdr') + t_pr.append(t_bor) + # # 获取各属性的值 + val = t_bor.get(qn('w:val')) + color = t_bor.get(qn('w:color')) + sz = t_bor.get(qn('w:sz')) + return [val, sz, color] + +def Comp(name1, name2): + """ + Compare the diferences of two files + Usage: + Comp(file_name1, file_name2) + """ + global flag + ff = 'K:/工作/traveler/test/' + # s = Document(name1) + # t = Document(name2) + s = Document(ff + name1) + t = Document(ff + name2) + + # 获取段落 + x = s.paragraphs + + y = t.paragraphs + + l = len(x) + for i in range(l-1): + s_shd = get_paragraph_shading(x[i]) + t_shd = get_paragraph_shading(y[i]) + s_border = get_paragraph_boders(x[i]) + t_border = get_paragraph_boders(y[i]) + if not eq(s_shd, t_shd): + print('效果对比出错,问题:',s_shd,'应该修改为:',t_shd) + print(1) + flag = False + break + if not eq(s_border, t_border): + print(1) + print('效果对比出错,问题:',s_border,'应该修改为:',t_border) + flag = False + break + + # 判断标题的文字边框 + if not eq(get_text_borders(x[0]), get_text_borders(y[0])): + print('文字边框设置出错,问题:',get_text_borders(x[0]),'应该修改为:',get_text_borders(y[0])) + flag = False + +try: + Comp('中国共产党党史.docx', '美化文档.docx') + + if flag: + print('Accepted!') + else: + print('文件内容错误,请调整后重试!') +except Exception as e: + print(e) + print('请按步骤创建、操作文本后重试!') \ No newline at end of file diff --git a/test/demo8.py b/test/demo8.py new file mode 100644 index 0000000..3453154 --- /dev/null +++ b/test/demo8.py @@ -0,0 +1,30 @@ +import numpy as np +import networkx as nx + +inf = float('inf') + + +def find_hamiltonian_cycles(distances): + num_nodes = distances.shape[0] + graph = nx.DiGraph() + for i in range(num_nodes): + for j in range(num_nodes): + if distances[i][j] != inf: + graph.add_edge(i, j, weight=distances[i][j]) + + hamiltonian_cycles = list(nx.simple_cycles(graph)) + hamiltonian_cycles = [cycle + [cycle[0]] for cycle in hamiltonian_cycles if len(cycle) == num_nodes - 1] + + return hamiltonian_cycles + + +# distances = np.array([[inf, 69, 48, inf, 41, 78], [69, inf, 48, 53, 91, inf], [48, 48, inf, 19, 86, inf], [inf, 53, 19, inf, 96, 10], [41, 91, 86, 96, inf, inf], [78, inf, inf, 10, inf, inf]]) +distances = np.array([[inf, 94, 67, 65, inf], [94, inf, inf, 41, inf], [67, inf, inf, inf, 65], [65, 41, inf, inf, 54], [inf, inf, 65, 54, inf]]) +all_hamiltonian_cycles = find_hamiltonian_cycles(distances) + +if all_hamiltonian_cycles: + print("存在的哈密尔顿回路路径为:") + for cycle in all_hamiltonian_cycles: + print(cycle) +else: + print("不存在哈密尔顿回路") \ No newline at end of file diff --git a/test/demo9.py b/test/demo9.py new file mode 100644 index 0000000..6b23864 --- /dev/null +++ b/test/demo9.py @@ -0,0 +1,42 @@ +import numpy as np + +inf = float('inf') + + +def find_all_cycles(distances): + num_nodes = distances.shape[0] + visited = [False] * num_nodes + cycles = [] + + def dfs(node, start, path): + visited[node] = True + for neighbor in range(num_nodes): + if distances[node, neighbor] != inf: + if neighbor == start and len(path) > 1: + cycles.append(path + [start]) + elif not visited[neighbor]: + dfs(neighbor, start, path + [neighbor]) + visited[node] = False + + for start_node in range(num_nodes): + dfs(start_node, start_node, [start_node]) + + return cycles + + +distances = np.array([ + [inf, inf, 21, inf, 53], + [inf, inf, 77, 84, 93], + [21, 77, inf, inf, 53], + [inf, 84, inf, inf, inf], + [53, 93, 53, inf, inf] +]) + +all_cycles = find_all_cycles(distances) + +if all_cycles: + print("存在的回路路径为:") + for cycle in all_cycles: + print(cycle) +else: + print("不存在回路") \ No newline at end of file diff --git a/test/dfs.py b/test/dfs.py new file mode 100644 index 0000000..0909a3a --- /dev/null +++ b/test/dfs.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/3 17:43 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : dfs.py + +import numpy as np +def dfs_exists_path(distances): + num_cities = distances.shape[0] + visited = set() + start_node = 0 + + def dfs(node): + visited.add(node) + for neighbor in range(num_cities): + if neighbor != node and neighbor not in visited and np.isfinite(distances[node, neighbor]): + dfs(neighbor) + + dfs(start_node) + + return len(visited) == num_cities + + +def exists_path_without_inf(distances): + # Check if there are any infinite distances + if np.any(np.isinf(distances)): + return False + + # Set the infinite distances to a large value + max_distance = np.max(distances) + 1 + distances[np.isinf(distances)] = max_distance + + # Use DFS to check if there exists a path + return dfs_exists_path(distances) + + +distances = np.array([ + [np.inf, 50, 99, np.inf, 64, np.inf], + [50, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +print(exists_path_without_inf(distances)) \ No newline at end of file diff --git a/test/dfs_1.py b/test/dfs_1.py new file mode 100644 index 0000000..3507d34 --- /dev/null +++ b/test/dfs_1.py @@ -0,0 +1,53 @@ +import numpy as np + + +def dfs_tsp(graph, start, current, path, visited, cost, min_cost, min_path): + if len(path) == len(graph) and graph[current][start] != np.inf: + path.append(start) + cost += graph[current][start] + if cost < min_cost[0]: + min_cost[0] = cost + min_path[0] = path.copy() + path.pop() + cost -= graph[current][start] + return + + for next_node in range(len(graph)): + if graph[current][next_node] != np.inf and not visited[next_node]: + visited[next_node] = True + path.append(next_node) + cost += graph[current][next_node] + dfs_tsp(graph, start, next_node, path, visited, cost, min_cost, min_path) + visited[next_node] = False + path.pop() + cost -= graph[current][next_node] + + +def tsp_dfs(graph): + n = len(graph) + min_cost = [float('inf')] + min_path = [[]] + + for start_node in range(n): + visited = [False] * n + path = [start_node] + cost = 0 + visited[start_node] = True + dfs_tsp(graph, start_node, start_node, path, visited, cost, min_cost, min_path) + + return min_cost[0], min_path[0] + + +# 示例使用 +distances = np.array([ + [np.inf, 50, 99, np.inf, 64, np.inf], + [50, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +min_cost, min_path = tsp_dfs(distances) +print("Minimum cost:", min_cost) +print("Minimum path:", min_path) \ No newline at end of file diff --git a/test/greedy.py b/test/greedy.py new file mode 100644 index 0000000..9b72609 --- /dev/null +++ b/test/greedy.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/31 10:22 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : greedy.py + +import numpy as np + +def tsp_greedy(distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + next_city = city + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + return path + +distances = np.array([ + [np.inf, 80, 99, np.inf, 64, np.inf], + [80, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +path = tsp_greedy(distances) +if len(path) == 6: + print('贪心算法路径:') + print(path) +else: + print('未找到!') \ No newline at end of file