Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

5 курс / lab5

.py
Скачиваний:
0
Добавлен:
26.01.2026
Размер:
18.72 Кб
Скачать
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import math
import random
import time


class Graph:
    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.adj_matrix[v][u] = weight
            self.edges.append((u, v, weight))

    def prim_algorithm(self, start_vertex=0):
        if self.vertices == 0:
            return [], 0

        selected = [False] * self.vertices
        selected[start_vertex] = True
        mst_edges = []
        total_weight = 0

        while len(mst_edges) < self.vertices - 1:
            min_weight = float('inf')
            u, v = -1, -1

            for i in range(self.vertices):
                if selected[i]:
                    for j in range(self.vertices):
                        if not selected[j] and self.adj_matrix[i][j] > 0:
                            if self.adj_matrix[i][j] < min_weight:
                                min_weight = self.adj_matrix[i][j]
                                u, v = i, j

            if u != -1 and v != -1:
                mst_edges.append((u, v, min_weight))
                selected[v] = True
                total_weight += min_weight

        return mst_edges, total_weight

    def get_edge_list(self):
        edges = []
        for i in range(self.vertices):
            for j in range(i + 1, 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 = Graph()
        self.mst_edges = []
        self.total_weight = 0
        self.vertex_positions = []

        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, "50")
        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)

        prim_frame = tk.Frame(control_frame, bg="#f0f0f0")
        prim_frame.pack(pady=5)

        tk.Label(prim_frame, text="Начальная вершина:", bg="#f0f0f0").pack(side=tk.LEFT)
        self.start_vertex_entry = tk.Entry(prim_frame, width=8)
        self.start_vertex_entry.insert(0, "0")
        self.start_vertex_entry.pack(side=tk.LEFT, padx=5)

        prim_button = tk.Button(control_frame, text="Запустить алгоритм Прима",
                                command=self.run_prim_algorithm, width=20, bg="#4CAF50", fg="white")
        prim_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=20, 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 = Graph(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]

                self.mst_edges = []
                self.total_weight = 0
                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 = Graph(vertices)

            for i in range(vertices):
                for j in range(i + 1, vertices):
                    if random.random() < density:
                        weight = random.randint(1, 20)
                        self.graph.adj_matrix[i][j] = weight
                        self.graph.adj_matrix[j][i] = weight

            self.mst_edges = []
            self.total_weight = 0
            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(i + 1, self.graph.vertices):
                if self.graph.adj_matrix[i][j] > 0:
                    x1, y1 = self.vertex_positions[i]
                    x2, y2 = self.vertex_positions[j]


                    is_mst_edge = any((u == i and v == j) or (u == j and v == i)
                                      for u, v, _ in self.mst_edges)

                    color = "red" if is_mst_edge else "gray"
                    width = 3 if is_mst_edge else 1

                    self.canvas.create_line(x1, y1, x2, y2, fill=color, width=width)


                    mid_x = (x1 + x2) / 2
                    mid_y = (y1 + y2) / 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):
            self.canvas.create_oval(x - 20, y - 20, x + 20, y + 20, fill="lightblue", outline="black", width=2)
            self.canvas.create_text(x, y, text=str(i), font=("Arial", 12, "bold"))

        if self.mst_edges:
            info_text = f"Минимальное остовное дерево\n"
            info_text += f"Общий вес: {self.total_weight}\n"
            info_text += f"Количество ребер: {len(self.mst_edges)}\n"
            info_text += f"Ребра MST: {', '.join([f'{u}-{v}({w})' for u, v, w in self.mst_edges])}"

            self.canvas.create_text(400, 550, text=info_text, font=("Arial", 10),
                                    fill="darkgreen", justify="center")

    def run_prim_algorithm(self):
        if self.graph.vertices == 0:
            messagebox.showwarning("Предупреждение", "Сначала создайте или загрузите граф")
            return

        try:
            start_vertex = int(self.start_vertex_entry.get())
            if start_vertex < 0 or start_vertex >= self.graph.vertices:
                messagebox.showerror("Ошибка", f"Начальная вершина должна быть от 0 до {self.graph.vertices - 1}")
                return

            start_time = time.time()
            self.mst_edges, self.total_weight = self.graph.prim_algorithm(start_vertex)
            end_time = time.time()

            execution_time = (end_time - start_time) * 1000

            self.result_text.delete(1.0, tk.END)
            self.result_text.insert(tk.END, f"Алгоритм Прима выполнен за {execution_time:.4f} мс\n\n")
            self.result_text.insert(tk.END, f"Начальная вершина: {start_vertex}\n")
            self.result_text.insert(tk.END, f"Общий вес MST: {self.total_weight}\n\n")
            self.result_text.insert(tk.END, "Ребра минимального остовного дерева:\n")

            for u, v, w in self.mst_edges:
                self.result_text.insert(tk.END, f"  {u} -- {v} : {w}\n")

            self.draw_graph()

        except ValueError:
            messagebox.showerror("Ошибка", "Введите корректный номер начальной вершины")

    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, 25, 30]
        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 = Graph(vertices)
                edges_count = 0

                for i in range(vertices):
                    for j in range(i + 1, vertices):
                        if random.random() < density:
                            weight = random.randint(1, 20)
                            test_graph.adj_matrix[i][j] = weight
                            test_graph.adj_matrix[j][i] = weight
                            edges_count += 1


                start_time = time.time()
                test_graph.prim_algorithm(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 ** 2
            text_widget.insert(tk.END,
                               f"{vertices:7d} | {edges:5d} | {density:9.1%} | {time_ms:10.4f} | O(V²) = O({vertices}²) = {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²)\n\n")
        text_widget.insert(tk.END, "2. Время выполнения растет квадратично с увеличением количества вершин\n\n")
        text_widget.insert(tk.END, "3. Плотность графа незначительно влияет на время выполнения,\n")
        text_widget.insert(tk.END, "   так как алгоритм всегда проверяет все возможные пары вершин\n\n")
        text_widget.insert(tk.END, "4. Для разреженных графов (мало ребер) эффективнее использовать\n")
        text_widget.insert(tk.END, "   реализацию с кучей, которая имеет сложность O(E log V)\n\n")
        text_widget.insert(tk.END, "5. Для плотных графов (много ребер) текущая реализация эффективна,\n")
        text_widget.insert(tk.END, "   так как E ≈ V² и O(V²) ≈ O(E)\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 = Graph()
        self.mst_edges = []
        self.total_weight = 0
        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 курс