PIbd-31_MasenkinMS_CompMath/main.py
2024-11-30 22:58:17 +04:00

185 lines
9.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()