From 837539b67a9ec2772a9f0d9f3e9ff2a12809c4d6 Mon Sep 17 00:00:00 2001 From: Factorino73 Date: Sat, 30 Nov 2024 22:58:17 +0400 Subject: [PATCH] LabWork01: done? --- main.py | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) diff --git a/main.py b/main.py index e69de29..074890c 100644 --- a/main.py +++ b/main.py @@ -0,0 +1,185 @@ +import numpy as np +import tkinter as tk +from tkinter import messagebox +from typing import List, Tuple + + +class TransportProblemApp: + def __init__(self, root: tk.Tk) -> None: + """ + Инициализация приложения. + + :param root: Основное окно приложения (Tkinter). + """ + self.root: tk.Tk = root + self.root.title("Транспортная задача") + + self.suppliers: int = 3 + self.consumers: int = 3 + + self.setup_ui() + + def setup_ui(self) -> None: + """ + Создание интерфейса приложения, включая поля для ввода данных и кнопки. + """ + self.frame_controls: tk.Frame = tk.Frame(self.root) + self.frame_controls.pack(pady=10) + + tk.Label(self.frame_controls, text="Количество поставщиков:").grid(row=0, column=0) + self.entry_suppliers: tk.Entry = tk.Entry(self.frame_controls, width=5) + self.entry_suppliers.insert(0, str(self.suppliers)) + self.entry_suppliers.grid(row=0, column=1) + + tk.Label(self.frame_controls, text="Количество потребителей:").grid(row=0, column=2) + self.entry_consumers: tk.Entry = tk.Entry(self.frame_controls, width=5) + self.entry_consumers.insert(0, str(self.consumers)) + self.entry_consumers.grid(row=0, column=3) + + tk.Button(self.frame_controls, text="Применить", command=self.update_table).grid(row=0, column=4, padx=10) + + self.frame_table: tk.Frame = tk.Frame(self.root) + self.frame_table.pack(pady=10) + self.update_table() + + self.frame_buttons: tk.Frame = tk.Frame(self.root) + self.frame_buttons.pack(pady=10) + + tk.Button(self.frame_buttons, text="Рассчитать", command=self.solve).pack() + + def update_table(self) -> None: + """ + Обновление таблицы для ввода данных о поставщиках, потребителях, запасах и потребностях. + """ + self.suppliers = int(self.entry_suppliers.get()) + self.consumers = int(self.entry_consumers.get()) + + for widget in self.frame_table.winfo_children(): + widget.destroy() + + self.entries_costs: List[List[tk.Entry]] = [] + self.entries_supplies: List[tk.Entry] = [] + self.entries_demands: List[tk.Entry] = [] + + # Заголовок "Пункт назначения" + tk.Label(self.frame_table, text="Пункт назначения").grid(row=0, column=1, columnspan=self.consumers) + tk.Label(self.frame_table, text="Пункт отправления").grid(row=1, column=0) + for j in range(self.consumers): + tk.Label(self.frame_table, text=f"B{j+1}").grid(row=1, column=j+1) + tk.Label(self.frame_table, text="Запас груза").grid(row=1, column=self.consumers+1) + + # Данные для поставщиков + for i in range(self.suppliers): + tk.Label(self.frame_table, text=f"A{i+1}").grid(row=i+2, column=0) + row_entries: List[tk.Entry] = [] + for j in range(self.consumers): + entry: tk.Entry = tk.Entry(self.frame_table, width=5) + entry.grid(row=i+2, column=j+1) + row_entries.append(entry) + self.entries_costs.append(row_entries) + + entry_supply: tk.Entry = tk.Entry(self.frame_table, width=5) + entry_supply.grid(row=i+2, column=self.consumers+1) + self.entries_supplies.append(entry_supply) + + # Данные для потребителей + tk.Label(self.frame_table, text="Потребность в грузе").grid(row=self.suppliers+2, column=0) + for j in range(self.consumers): + entry_demand: tk.Entry = tk.Entry(self.frame_table, width=5) + entry_demand.grid(row=self.suppliers+2, column=j+1) + self.entries_demands.append(entry_demand) + + def solve(self) -> None: + """ + Решение транспортной задачи методом минимальных элементов. + Вычисляет и отображает результаты. + """ + try: + supplies: np.ndarray = np.array([int(entry.get()) for entry in self.entries_supplies]) + demands: np.ndarray = np.array([int(entry.get()) for entry in self.entries_demands]) + costs: np.ndarray = np.array([[int(entry.get()) for entry in row] for row in self.entries_costs], dtype=float) + + if supplies.sum() != demands.sum(): + messagebox.showerror("Ошибка", "Сумма запасов должна равняться сумме потребностей.") + return + + # Создание копии массива для расчета итоговой стоимости + original_costs: np.ndarray = costs.copy() + + allocation: np.ndarray = self.minimum_cost_method(supplies, demands, costs) + total_cost, abs_errors = self.calculate_results(allocation, original_costs) + + self.show_results(allocation, total_cost, abs_errors) + + except ValueError: + messagebox.showerror("Ошибка", "Все значения должны быть целыми числами.") + + def minimum_cost_method(self, supplies: np.ndarray, demands: np.ndarray, costs: np.ndarray) -> np.ndarray: + """ + Метод минимальных элементов для решения транспортной задачи. + + :param supplies: Запасы для поставщиков. + :param demands: Потребности для потребителей. + :param costs: Стоимость перевозки между поставщиками и потребителями. + :return: Распределение грузов между поставщиками и потребителями. + """ + allocation: np.ndarray = np.zeros_like(costs) + + while supplies.sum() > 0 and demands.sum() > 0: + min_index: Tuple[int, int] = np.unravel_index(np.argmin(costs, axis=None), costs.shape) # type: ignore + i, j = min_index + + qty: int = min(supplies[i], demands[j]) + allocation[i, j] = qty + + supplies[i] -= qty + demands[j] -= qty + + costs[i, j] = np.inf # Исключить обработанную клетку + + return allocation + + def calculate_results(self, allocation: np.ndarray, costs: np.ndarray) -> Tuple[float, np.ndarray]: + """ + Подсчет общей стоимости перевозок и абсолютных погрешностей для решения задачи. + + :param allocation: Распределение грузов между поставщиками и потребителями. + :param costs: Стоимость перевозки между поставщиками и потребителями. + :return: Общая стоимость перевозок и массив абсолютных погрешностей. + """ + # Замена np.inf на 0 для расчётов + costs_no_inf: np.ndarray = np.where(costs == np.inf, 0, costs) + total_cost: float = np.sum(allocation * costs_no_inf) + abs_errors: np.ndarray = allocation * 0.05 # 5% допустимая погрешность + return total_cost, abs_errors + + def show_results(self, allocation: np.ndarray, total_cost: float, abs_errors: np.ndarray) -> None: + """ + Отображение результатов решения задачи. + + :param allocation: Распределение грузов между поставщиками и потребителями. + :param total_cost: Общая стоимость перевозок. + :param abs_errors: Абсолютные погрешности для каждого элемента распределения. + """ + results_window: tk.Toplevel = tk.Toplevel(self.root) + results_window.title("Результаты") + + tk.Label(results_window, text="Распределение грузов:").pack(pady=5) + allocation_text: tk.Text = tk.Text(results_window, height=10, width=50) + allocation_text.insert(tk.END, str(allocation)) + allocation_text.config(state=tk.DISABLED) + allocation_text.pack() + + tk.Label(results_window, text=f"Общая стоимость перевозок: {total_cost}").pack(pady=5) + + tk.Label(results_window, text="Абсолютные погрешности:").pack(pady=5) + errors_text: tk.Text = tk.Text(results_window, height=10, width=50) + errors_text.insert(tk.END, str(abs_errors)) + errors_text.config(state=tk.DISABLED) + errors_text.pack() + + +if __name__ == "__main__": + root: tk.Tk = tk.Tk() + app: TransportProblemApp = TransportProblemApp(root) + root.mainloop() \ No newline at end of file