Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
837539b67a |
185
main.py
185
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()
|
Loading…
Reference in New Issue
Block a user