Добавил:
lovickaaolesa
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:5 курс / lab6
.pyimport tkinter as tk
from tkinter import ttk, messagebox, filedialog
import math
import random
import time
class DirectedGraph:
def __init__(self, vertices=0):
self.vertices = vertices
self.adj_matrix = [[0] * vertices for _ in range(vertices)]
self.edges = []
def add_edge(self, u, v, weight):
if 0 <= u < self.vertices and 0 <= v < self.vertices:
self.adj_matrix[u][v] = weight
self.edges.append((u, v, weight))
def bellman_ford(self, source):
if self.vertices == 0:
return [], []
distances = [float('inf')] * self.vertices
predecessors = [-1] * self.vertices
distances[source] = 0
for _ in range(self.vertices - 1):
for u, v, w in self.edges:
if distances[u] != float('inf') and distances[u] + w < distances[v]:
distances[v] = distances[u] + w
predecessors[v] = u
for u, v, w in self.edges:
if distances[u] != float('inf') and distances[u] + w < distances[v]:
return None, None
return distances, predecessors
def get_edge_list(self):
edges = []
for i in range(self.vertices):
for j in range(self.vertices):
if self.adj_matrix[i][j] != 0:
edges.append((i, j, self.adj_matrix[i][j]))
return edges
class GraphVisualizer:
def __init__(self, root):
self.root = root
self.root.title("Алгоритм Беллмана-Форда")
self.root.geometry("1200x700")
self.graph = DirectedGraph()
self.vertex_positions = []
self.highlighted_path = []
self.create_widgets()
self.create_canvas()
def create_widgets(self):
control_frame = tk.Frame(self.root, width=300, bg="#f0f0f0")
control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=10, pady=10)
control_frame.pack_propagate(False)
title_label = tk.Label(control_frame, text="Управление графом",
font=("Arial", 14, "bold"), bg="#f0f0f0")
title_label.pack(pady=10)
load_button = tk.Button(control_frame, text="Загрузить из файла",
command=self.load_graph_from_file, width=20)
load_button.pack(pady=5)
save_button = tk.Button(control_frame, text="Сохранить в файл",
command=self.save_graph_to_file, width=20)
save_button.pack(pady=5)
ttk.Separator(control_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=10)
gen_frame = tk.Frame(control_frame, bg="#f0f0f0")
gen_frame.pack(pady=5)
tk.Label(gen_frame, text="Вершины:", bg="#f0f0f0").pack(side=tk.LEFT)
self.vertices_entry = tk.Entry(gen_frame, width=8)
self.vertices_entry.insert(0, "8")
self.vertices_entry.pack(side=tk.LEFT, padx=5)
tk.Label(gen_frame, text="Плотность %:", bg="#f0f0f0").pack(side=tk.LEFT, padx=(10, 0))
self.density_entry = tk.Entry(gen_frame, width=8)
self.density_entry.insert(0, "30")
self.density_entry.pack(side=tk.LEFT, padx=5)
gen_button = tk.Button(control_frame, text="Сгенерировать граф",
command=self.generate_random_graph, width=20)
gen_button.pack(pady=5)
ttk.Separator(control_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=10)
path_frame = tk.Frame(control_frame, bg="#f0f0f0")
path_frame.pack(pady=5)
tk.Label(path_frame, text="Из:", bg="#f0f0f0").pack(side=tk.LEFT)
self.source_entry = tk.Entry(path_frame, width=8)
self.source_entry.insert(0, "0")
self.source_entry.pack(side=tk.LEFT, padx=5)
tk.Label(path_frame, text="В:", bg="#f0f0f0").pack(side=tk.LEFT, padx=(10, 0))
self.target_entry = tk.Entry(path_frame, width=8)
self.target_entry.insert(0, "3")
self.target_entry.pack(side=tk.LEFT, padx=5)
bellman_button = tk.Button(control_frame, text="Запустить Беллмана-Форда",
command=self.run_bellman_ford, width=20, bg="#4CAF50", fg="white")
bellman_button.pack(pady=5)
analyze_button = tk.Button(control_frame, text="Анализ временной сложности",
command=self.analyze_time_complexity, width=20)
analyze_button.pack(pady=5)
clear_button = tk.Button(control_frame, text="Очистить граф",
command=self.clear_graph, width=20)
clear_button.pack(pady=5)
ttk.Separator(control_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=10)
info_label = tk.Label(control_frame, text="Информация о графе",
font=("Arial", 12, "bold"), bg="#f0f0f0")
info_label.pack(pady=5)
self.info_text = tk.Text(control_frame, height=8, width=30, font=("Arial", 10))
self.info_text.pack(pady=5)
edges_label = tk.Label(control_frame, text="Список ребер",
font=("Arial", 12, "bold"), bg="#f0f0f0")
edges_label.pack(pady=5)
self.edges_text = tk.Text(control_frame, height=10, width=30, font=("Arial", 9))
self.edges_text.pack(pady=5)
def create_canvas(self):
canvas_frame = tk.Frame(self.root)
canvas_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10)
self.canvas = tk.Canvas(canvas_frame, bg="white", highlightthickness=1,
highlightbackground="black")
self.canvas.pack(fill=tk.BOTH, expand=True)
result_frame = tk.Frame(self.root, width=300, bg="#f0f0f0")
result_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=10, pady=10)
result_frame.pack_propagate(False)
result_label = tk.Label(result_frame, text="Результаты",
font=("Arial", 14, "bold"), bg="#f0f0f0")
result_label.pack(pady=10)
self.result_text = tk.Text(result_frame, height=25, width=35, font=("Arial", 10))
self.result_text.pack(pady=5)
def load_graph_from_file(self):
filename = filedialog.askopenfilename(
title="Выберите файл с графом",
filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
)
if filename:
try:
with open(filename, 'r') as file:
lines = file.readlines()
vertices = int(lines[0].strip())
self.graph = DirectedGraph(vertices)
for i in range(1, len(lines)):
row = list(map(int, lines[i].strip().split()))
for j in range(vertices):
self.graph.adj_matrix[i - 1][j] = row[j]
if row[j] != 0:
self.graph.edges.append((i - 1, j, row[j]))
self.highlighted_path = []
self.update_edge_list()
self.draw_graph()
messagebox.showinfo("Успех", f"Граф загружен из файла {filename}")
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось загрузить файл: {e}")
def save_graph_to_file(self):
if self.graph.vertices == 0:
messagebox.showwarning("Предупреждение", "Нет графа для сохранения")
return
filename = filedialog.asksaveasfilename(
title="Сохранить граф",
defaultextension=".txt",
filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
)
if filename:
try:
with open(filename, 'w') as file:
file.write(str(self.graph.vertices) + "\n")
for i in range(self.graph.vertices):
row = " ".join(str(x) for x in self.graph.adj_matrix[i])
file.write(row + "\n")
messagebox.showinfo("Успех", f"Граф сохранен в файл {filename}")
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось сохранить файл: {e}")
def generate_random_graph(self):
try:
vertices = int(self.vertices_entry.get())
density = float(self.density_entry.get()) / 100
if vertices < 2 or vertices > 20:
messagebox.showwarning("Предупреждение", "Количество вершин должно быть от 2 до 20")
return
self.graph = DirectedGraph(vertices)
self.graph.edges = []
for i in range(vertices):
for j in range(vertices):
if i != j and random.random() < density:
weight = random.randint(-5, 20)
if weight <= 0:
weight = random.randint(1, 20)
self.graph.adj_matrix[i][j] = weight
self.graph.edges.append((i, j, weight))
self.highlighted_path = []
self.update_edge_list()
self.draw_graph()
except ValueError:
messagebox.showerror("Ошибка", "Введите корректные значения")
def update_edge_list(self):
self.info_text.delete(1.0, tk.END)
self.edges_text.delete(1.0, tk.END)
if self.graph.vertices > 0:
edges = self.graph.get_edge_list()
self.info_text.insert(tk.END, f"Вершин: {self.graph.vertices}\n")
self.info_text.insert(tk.END, f"Ребер: {len(edges)}\n")
for u, v, w in edges:
self.edges_text.insert(tk.END, f"{u} → {v} : {w}\n")
def draw_graph(self):
self.canvas.delete("all")
if self.graph.vertices == 0:
return
center_x, center_y = 400, 300
radius = 250
self.vertex_positions = []
for i in range(self.graph.vertices):
angle = 2 * math.pi * i / self.graph.vertices
x = center_x + radius * math.cos(angle)
y = center_y + radius * math.sin(angle)
self.vertex_positions.append((x, y))
for i in range(self.graph.vertices):
for j in range(self.graph.vertices):
if self.graph.adj_matrix[i][j] > 0:
x1, y1 = self.vertex_positions[i]
x2, y2 = self.vertex_positions[j]
dx = x2 - x1
dy = y2 - y1
length = math.sqrt(dx * dx + dy * dy)
if length > 0:
offset_x = (dx / length) * 20
offset_y = (dy / length) * 20
x1 += offset_x
y1 += offset_y
x2 -= offset_x
y2 -= offset_y
is_highlighted = any((self.highlighted_path[k] == i and
self.highlighted_path[k + 1] == j)
for k in range(len(self.highlighted_path) - 1))
color = "red" if is_highlighted else "gray"
width = 3 if is_highlighted else 1
self.canvas.create_line(x1, y1, x2, y2, arrow=tk.LAST,
fill=color, width=width)
mid_x = (x1 + x2) / 2
mid_y = (y1 + y2) / 2
if is_highlighted:
self.canvas.create_oval(mid_x - 15, mid_y - 15, mid_x + 15, mid_y + 15,
fill="yellow", outline="red", width=2)
self.canvas.create_text(mid_x, mid_y,
text=str(self.graph.adj_matrix[i][j]),
fill="blue", font=("Arial", 10, "bold"))
for i, (x, y) in enumerate(self.vertex_positions):
is_highlighted = i in self.highlighted_path
fill_color = "orange" if is_highlighted else "lightblue"
outline_color = "red" if is_highlighted else "black"
width = 3 if is_highlighted else 2
self.canvas.create_oval(x - 20, y - 20, x + 20, y + 20,
fill=fill_color, outline=outline_color, width=width)
self.canvas.create_text(x, y, text=str(i),
font=("Arial", 12, "bold"))
def run_bellman_ford(self):
if self.graph.vertices == 0:
messagebox.showwarning("Предупреждение", "Сначала создайте или загрузите граф")
return
try:
source = int(self.source_entry.get())
target = int(self.target_entry.get())
if source < 0 or source >= self.graph.vertices or target < 0 or target >= self.graph.vertices:
messagebox.showerror("Ошибка",
f"Вершины должны быть от 0 до {self.graph.vertices - 1}")
return
start_time = time.time()
distances, predecessors = self.graph.bellman_ford(source)
end_time = time.time()
execution_time = (end_time - start_time) * 1000
self.result_text.delete(1.0, tk.END)
if distances is None:
self.result_text.insert(tk.END, "Обнаружен цикл отрицательного веса!\n")
self.result_text.insert(tk.END, "Алгоритм не может найти кратчайшие пути.\n")
return
self.result_text.insert(tk.END, f"Время выполнения: {execution_time:.4f} мс\n\n")
self.result_text.insert(tk.END, f"Кратчайшие расстояния от вершины {source}:\n")
for i in range(self.graph.vertices):
dist = distances[i] if distances[i] != float('inf') else "∞"
self.result_text.insert(tk.END, f" до {i}: {dist}\n")
self.result_text.insert(tk.END, f"\nКратчайший путь из {source} в {target}:\n")
if distances[target] == float('inf'):
self.result_text.insert(tk.END, "Путь не существует\n")
return
path = []
current = target
while current != -1:
path.append(current)
current = predecessors[current]
path.reverse()
self.highlighted_path = path
path_str = " → ".join(str(v) for v in path)
self.result_text.insert(tk.END, f" Путь: {path_str}\n")
self.result_text.insert(tk.END, f" Длина: {distances[target]}\n")
self.draw_graph()
except ValueError:
messagebox.showerror("Ошибка", "Введите корректные номера вершин")
def highlight_path(self, path):
self.highlighted_path = path
self.draw_graph()
def analyze_time_complexity(self):
analysis_window = tk.Toplevel(self.root)
analysis_window.title("Анализ временной сложности")
analysis_window.geometry("800x600")
title_label = tk.Label(analysis_window,
text="Анализ временной сложности алгоритма Беллмана-Форда",
font=("Arial", 14, "bold"))
title_label.pack(pady=10)
text_frame = tk.Frame(analysis_window)
text_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
scrollbar = tk.Scrollbar(text_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
text_widget = tk.Text(text_frame, wrap=tk.WORD, yscrollcommand=scrollbar.set)
text_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=text_widget.yview)
vertices_list = [5, 10, 15, 20]
density_list = [0.3, 0.5, 0.7]
results = []
text_widget.insert(tk.END, "ГЕНЕРАЦИЯ ТЕСТОВЫХ ДАННЫХ...\n")
text_widget.insert(tk.END, "=" * 80 + "\n\n")
for vertices in vertices_list:
for density in density_list:
test_graph = DirectedGraph(vertices)
edges_count = 0
for i in range(vertices):
for j in range(vertices):
if i != j and random.random() < density:
weight = random.randint(1, 20)
test_graph.adj_matrix[i][j] = weight
test_graph.edges.append((i, j, weight))
edges_count += 1
start_time = time.time()
test_graph.bellman_ford(0)
end_time = time.time()
execution_time = (end_time - start_time) * 1000
results.append((vertices, edges_count, density, execution_time))
text_widget.insert(tk.END, "ТАБЛИЦА АНАЛИЗА ВРЕМЕННОЙ СЛОЖНОСТИ\n")
text_widget.insert(tk.END, "=" * 80 + "\n")
text_widget.insert(tk.END, "Вершин | Ребра | Плотность | Время (мс) | O-нотация\n")
text_widget.insert(tk.END, "-" * 80 + "\n")
for vertices, edges, density, time_ms in results:
theoretical_complexity = vertices * edges
text_widget.insert(tk.END,
f"{vertices:7d} | {edges:5d} | {density:9.1%} | {time_ms:10.4f} | "
f"O(V*E) = O({vertices}*{edges}) = {theoretical_complexity}\n")
text_widget.insert(tk.END, "=" * 80 + "\n\n")
text_widget.insert(tk.END, "ВЫВОДЫ:\n")
text_widget.insert(tk.END, "=" * 40 + "\n")
text_widget.insert(tk.END,
"1. Временная сложность алгоритма Беллмана-Форда: O(V*E)\n\n")
text_widget.insert(tk.END,
"2. Время выполнения линейно зависит от количества ребер\n\n")
text_widget.insert(tk.END,
"3. Алгоритм работает с графами с отрицательными весами\n\n")
text_widget.insert(tk.END,
"4. Обнаруживает циклы отрицательного веса\n\n")
text_widget.insert(tk.END,
"5. Медленнее алгоритма Дейкстры для графов без отрицательных весов\n\n")
text_widget.insert(tk.END,
"6. Эффективен для разреженных графов\n")
close_button = tk.Button(analysis_window, text="Закрыть",
command=analysis_window.destroy, width=20)
close_button.pack(pady=10)
def clear_graph(self):
self.graph = DirectedGraph()
self.highlighted_path = []
self.canvas.delete("all")
self.info_text.delete(1.0, tk.END)
self.edges_text.delete(1.0, tk.END)
self.result_text.delete(1.0, tk.END)
def main():
root = tk.Tk()
app = GraphVisualizer(root)
root.mainloop()
if __name__ == "__main__":
main()
Соседние файлы в папке 5 курс
