You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1731 lines
77 KiB

11 months ago
# -*- 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, "时间/h1-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, "时间/h1-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("<Button-1>", 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("<Button-1>", 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("<Button-1>", 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("<Button-1>", 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('<Button-1>', 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()