ЛР-02: Рационы и медкомплекты#
Worked example: military 01#
Это полностью разобранный example. Он показывает образец постановки, решения и интерпретации транспортной модели.
1. Постановка кейса#
Военный закрытый кейс с нормальной балансировкой запасов и потребностей.
Запасы#
Поставщик |
Объём |
|---|---|
База Север |
24 |
База Центр |
30 |
База Юг |
26 |
Спрос#
Потребитель |
Объём |
|---|---|
Группа 1 |
18 |
Группа 2 |
20 |
Группа 3 |
16 |
Группа 4 |
26 |
Матрица затрат#
Откуда / Куда |
Группа 1 |
Группа 2 |
Группа 3 |
Группа 4 |
|---|---|---|---|---|
База Север |
5 |
6 |
8 |
9 |
База Центр |
4 |
5 |
7 |
8 |
База Юг |
7 |
5 |
4 |
6 |
import numpy as np
import pandas as pd
from scipy.optimize import linprog
def balance_transport_problem(supplies, demands, costs, supplier_names, consumer_names):
supplies = supplies.astype(float).copy()
demands = demands.astype(float).copy()
costs = costs.astype(float).copy()
supplier_names = list(supplier_names)
consumer_names = list(consumer_names)
diff = supplies.sum() - demands.sum()
if diff > 0:
demands = np.append(demands, diff)
consumer_names.append('Фиктивный потребитель')
costs = np.column_stack([costs, np.zeros(len(supplies))])
elif diff < 0:
supplies = np.append(supplies, -diff)
supplier_names.append('Фиктивный поставщик')
costs = np.vstack([costs, np.zeros(len(demands))])
return supplies, demands, costs, supplier_names, consumer_names
def solve_transport_problem(supplies, demands, costs):
m, n = costs.shape
c = costs.flatten()
A_eq = []
b_eq = []
for i in range(m):
row = np.zeros(m * n)
row[i * n:(i + 1) * n] = 1
A_eq.append(row)
b_eq.append(supplies[i])
for j in range(n):
row = np.zeros(m * n)
row[j::n] = 1
A_eq.append(row)
b_eq.append(demands[j])
result = linprog(
c,
A_eq=np.array(A_eq),
b_eq=np.array(b_eq),
bounds=[(0, None)] * (m * n),
method='highs',
)
if not result.success:
raise RuntimeError(result.message)
return result, result.x.reshape(m, n)
supplier_names = ['База Север', 'База Центр', 'База Юг']
consumer_names = ['Группа 1', 'Группа 2', 'Группа 3', 'Группа 4']
supplies = np.array([24, 30, 26], dtype=float)
demands = np.array([18, 20, 16, 26], dtype=float)
costs = np.array([[5, 6, 8, 9], [4, 5, 7, 8], [7, 5, 4, 6]], dtype=float)
balanced_supplies, balanced_demands, balanced_costs, supplier_names, consumer_names = balance_transport_problem(
supplies, demands, costs, supplier_names, consumer_names
)
result, plan = solve_transport_problem(balanced_supplies, balanced_demands, balanced_costs)
plan_df = pd.DataFrame(plan, index=supplier_names, columns=consumer_names)
cost_df = pd.DataFrame(balanced_costs, index=supplier_names, columns=consumer_names)
print('Оптимальная стоимость:', round(result.fun, 2))
print()
print('План перевозок:')
display(plan_df)
print('Матрица затрат:')
display(cost_df)
Оптимальная стоимость: 448.0
План перевозок:
| Группа 1 | Группа 2 | Группа 3 | Группа 4 | |
|---|---|---|---|---|
| База Север | 0.0 | 20.0 | 0.0 | 4.0 |
| База Центр | 18.0 | 0.0 | 0.0 | 12.0 |
| База Юг | 0.0 | 0.0 | 16.0 | 10.0 |
Матрица затрат:
| Группа 1 | Группа 2 | Группа 3 | Группа 4 | |
|---|---|---|---|---|
| База Север | 5.0 | 6.0 | 8.0 | 9.0 |
| База Центр | 4.0 | 5.0 | 7.0 | 8.0 |
| База Юг | 7.0 | 5.0 | 4.0 | 6.0 |
used_routes = []
for supplier in plan_df.index:
for consumer in plan_df.columns:
value = float(plan_df.loc[supplier, consumer])
if value > 1e-9:
used_routes.append({
'маршрут': f'{supplier} -> {consumer}',
'объём': round(value, 2),
'тариф': float(cost_df.loc[supplier, consumer]),
'затраты': round(value * float(cost_df.loc[supplier, consumer]), 2),
})
used_routes_df = pd.DataFrame(used_routes)
print('Использованные маршруты:')
display(used_routes_df)
print('Проверка баланса по поставщикам:')
display(pd.DataFrame({'план': plan_df.sum(axis=1), 'запас': balanced_supplies}, index=plan_df.index))
print('Проверка баланса по потребителям:')
display(pd.DataFrame({'план': plan_df.sum(axis=0), 'спрос': balanced_demands}, index=plan_df.columns))
Использованные маршруты:
| маршрут | объём | тариф | затраты | |
|---|---|---|---|---|
| 0 | База Север -> Группа 2 | 20.0 | 6.0 | 120.0 |
| 1 | База Север -> Группа 4 | 4.0 | 9.0 | 36.0 |
| 2 | База Центр -> Группа 1 | 18.0 | 4.0 | 72.0 |
| 3 | База Центр -> Группа 4 | 12.0 | 8.0 | 96.0 |
| 4 | База Юг -> Группа 3 | 16.0 | 4.0 | 64.0 |
| 5 | База Юг -> Группа 4 | 10.0 | 6.0 | 60.0 |
Проверка баланса по поставщикам:
| план | запас | |
|---|---|---|
| База Север | 24.0 | 24.0 |
| База Центр | 30.0 | 30.0 |
| База Юг | 26.0 | 26.0 |
Проверка баланса по потребителям:
| план | спрос | |
|---|---|---|
| Группа 1 | 18.0 | 18.0 |
| Группа 2 | 20.0 | 20.0 |
| Группа 3 | 16.0 | 16.0 |
| Группа 4 | 26.0 | 26.0 |
2. Что важно проговорить в выводе#
одна и та же транспортная математика работает и в прикладной военной оболочке;
содержательная интерпретация строится вокруг устойчивости снабжения;
для отчёта полезно назвать ключевые маршруты первого выбора.
Для отчёта обычно достаточно перечислить ненулевые маршруты, пояснить роль фиктивного узла, если он появился, и отдельно указать итоговую стоимость перевозок.