Загрузка данных
import numpy as np
def hungarian_correct(cost_matrix):
n = cost_matrix.shape[0]
C = cost_matrix.astype(float).copy()
for i in range(n):
min_row = np.min(C[i, :])
C[i, :] -= min_row
for j in range(n):
min_col = np.min(C[:, j])
C[:, j] -= min_col
def max_zero_matching(matrix):
row_cover = np.zeros(n, dtype=bool)
col_cover = np.zeros(n, dtype=bool)
assign = np.full(n, -1)
for i in range(n):
for j in range(n):
if matrix[i, j] == 0 and not col_cover[j]:
assign[i] = j
col_cover[j] = True
break
return assign
while True:
assign = max_zero_matching(C)
row_mark = np.zeros(n, dtype=bool)
col_mark = np.zeros(n, dtype=bool)
for i in range(n):
if assign[i] == -1:
row_mark[i] = True
changed = True
while changed:
changed = False
for i in range(n):
if row_mark[i]:
for j in range(n):
if C[i, j] == 0 and not col_mark[j]:
col_mark[j] = True
changed = True
for i in range(n):
if not row_mark[i] and assign[i] != -1 and col_mark[assign[i]]:
row_mark[i] = True
changed = True
covered_rows = ~row_mark
covered_cols = col_mark
num_lines = np.sum(covered_rows) + np.sum(covered_cols)
if num_lines == n:
break
unc_rows = np.where(~covered_rows)[0]
unc_cols = np.where(~covered_cols)[0]
min_val = np.min(C[np.ix_(unc_rows, unc_cols)])
for i in range(n):
for j in range(n):
if ~covered_rows[i] and ~covered_cols[j]:
C[i, j] -= min_val
elif covered_rows[i] and covered_cols[j]:
C[i, j] += min_val
def find_perfect_matching(matrix):
used_col = np.zeros(n, dtype=bool)
assignment = np.full(n, -1)
def dfs(row):
if row == n:
return True
for col in range(n):
if matrix[row, col] == 0 and not used_col[col]:
used_col[col] = True
if dfs(row + 1):
assignment[row] = col
return True
used_col[col] = False
return False
if dfs(0):
return assignment
else:
for i in range(n):
for j in range(n):
if matrix[i, j] == 0 and not used_col[j]:
assignment[i] = j
used_col[j] = True
break
return assignment
final_assign = find_perfect_matching(C)
total = cost_matrix[np.arange(n), final_assign].sum()
X = np.zeros_like(cost_matrix, dtype=int)
X[np.arange(n), final_assign] = 1
return final_assign, total, X
cost = np.array([
[1, 3, 7, 8, 3, 1],
[8, 1, 1, 7, 5, 4],
[5, 1, 2, 1, 1, 9],
[9, 4, 1, 3, 2, 8],
[2, 5, 4, 3, 9, 5],
[7, 2, 10, 1, 5, 1]
])
assign, f_val, X = hungarian_correct(cost)
print("\nМатрица назначений X:")
print(X)
print(f"\nf(x) = {f_val}")
def show_table(plan, sup, dem, title="Таблица", rrows=None, rcols=None):
if rrows is None:
rrows = len(sup)
if rcols is None:
rcols = len(dem)
print(f"\n{title}")
print("┌───────" + "┬──────────" * len(dem) + "┬─────────┐")
print("│ ", end="")
for j in range(len(dem)):
print(f"│ B{j+1:<2} ", end="")
print("│ Запасы │")
print("├───────" + "┼──────────" * len(dem) + "┼─────────┤")
for i in range(len(sup)):
print(f"│ A{i+1:<2} ", end="")
for j in range(len(dem)):
if plan[i][j] > 0:
print(f"│ {plan[i][j]:<6} ", end="")
else:
print(f"│ · ", end="")
print(f"│ {sup[i]:<6} │")
if i < len(sup) - 1:
print("├───────" + "┼──────────" * len(dem) + "┼─────────┤")
print("├───────" + "┼──────────" * len(dem) + "┼─────────┤")
print("│ Спрос ", end="")
for j in range(len(dem)):
print(f"│ {dem[j]:<6} ", end="")
print("│ │")
print("└───────" + "┴──────────" * len(dem) + "┴─────────┘")
def real_cost(plan, cst, rrows, rcols):
total = 0
for i in range(rrows):
for j in range(rcols):
total += plan[i][j] * cst[i][j]
return total
def balance(sup, dem, cst):
total_sup = sum(sup)
total_dem = sum(dem)
print(f"Сумма запасов: {total_sup}")
print(f"Сумма спроса: {total_dem}")
if total_sup == total_dem:
return sup, dem, cst
elif total_sup < total_dem:
fake = total_dem - total_sup
sup.append(fake)
cst.append([0] * len(dem))
print(f"Добавлен фиктивный поставщик с запасом {fake}")
return sup, dem, cst
else:
fake = total_sup - total_dem
dem.append(fake)
for row in cst:
row.append(9)
print(f"Добавлен фиктивный потребитель со спросом {fake} (стоимость 9)")
return sup, dem, cst
def northwest(sup, dem, cst):
m, n = len(sup), len(dem)
s, d = sup[:], dem[:]
plan = [[0] * n for _ in range(m)]
base = []
i = j = 0
while i < m and j < n:
amount = min(s[i], d[j])
plan[i][j] = amount
if amount > 0:
base.append((i, j))
s[i] -= amount
d[j] -= amount
if s[i] == 0:
i += 1
if d[j] == 0:
j += 1
return plan, base
def mincost(sup, dem, cst):
m, n = len(sup), len(dem)
s, d = sup[:], dem[:]
plan = [[0] * n for _ in range(m)]
base = []
cells = [(cst[i][j], i, j) for i in range(m) for j in range(n)]
cells.sort()
for c, i, j in cells:
if s[i] > 0 and d[j] > 0:
amount = min(s[i], d[j])
if amount > 0:
plan[i][j] = amount
base.append((i, j))
s[i] -= amount
d[j] -= amount
return plan, base
def potentials(plan, cst, base, m, n):
u = [None] * m
v = [None] * n
u[0] = 0
changed = True
while changed:
changed = False
for i, j in base:
if u[i] is not None and v[j] is None:
v[j] = cst[i][j] - u[i]
changed = True
elif u[i] is None and v[j] is not None:
u[i] = cst[i][j] - v[j]
changed = True
return u, v
def main():
real_sup = [240, 100, 240, 100, 240]
real_dem = [90, 180, 350, 185, 80]
real_cst = [
[7, 6, 4, 3, 9],
[3, 8, 5, 4, 7],
[2, 3, 7, 2, 3],
[4, 5, 2, 3, 5],
[5, 7, 3, 9, 2]
]
sup, dem, cst = balance(
real_sup[:], real_dem[:],
[row[:] for row in real_cst]
)
rrows = len(real_sup)
rcols = len(real_dem)
plan_nw, base_nw = northwest(sup[:], dem[:], cst)
cost_nw = real_cost(plan_nw, cst, rrows, rcols)
plan_mc, base_mc = mincost(sup[:], dem[:], cst)
cost_mc = real_cost(plan_mc, cst, rrows, rcols)
print(f"\nСтоимость опорного плана (северо-западный угол): {cost_nw}")
print(f"Стоимость опорного плана (минимальная стоимость): {cost_mc}")
if cost_nw <= cost_mc:
best_plan, best_base = plan_nw, base_nw
else:
best_plan, best_base = plan_mc, base_mc
m, n = len(sup), len(dem)
u, v = potentials(best_plan, cst, best_base, m, n)
print("\nПотенциалы:")
print("u =", [f"{ui:.2f}" if ui is not None else "None" for ui in u])
print("v =", [f"{vj:.2f}" if vj is not None else "None" for vj in v])
print("\nОценки свободных клеток:")
for i in range(m):
for j in range(n):
if (i, j) in best_base:
continue
if u[i] is not None and v[j] is not None:
delta = cst[i][j] - u[i] - v[j]
if delta < -1e-10:
print(f"Δ({i+1},{j+1}) = {delta:6.2f} *")
else:
print(f"Δ({i+1},{j+1}) = {delta:6.2f}")
show_table(best_plan, sup, dem, "ЛУЧШИЙ ОПОРНЫЙ ПЛАН", rrows, rcols)
print(best_plan)
if __name__ == "__main__":
import numpy as np
def simplex_general(c, A, b, signs, sense='min', M=1e5):
"""
Универсальный симплекс-метод с искусственным базисом.
c : коэффициенты целевой функции
A : матрица ограничений
b : правые части (любого знака)
signs : знаки ограничений ('<=', '>=', '=')
sense : 'min' или 'max'
M : большой коэффициент штрафа
"""
n_orig = len(c)
c = np.array(c, dtype=float)
# 1. Приведение правых частей к неотрицательным
m = len(signs)
A_mod = []
b_mod = []
signs_mod = []
for i in range(m):
if b[i] < 0:
A_mod.append([-a for a in A[i]])
b_mod.append(-b[i])
# меняем знак неравенства
if signs[i] == '<=':
signs_mod.append('>=')
elif signs[i] == '>=':
signs_mod.append('<=')
else: # '='
signs_mod.append('=')
else:
A_mod.append(A[i][:])
b_mod.append(b[i])
signs_mod.append(signs[i])
A_mod = np.array(A_mod, dtype=float)
b_mod = np.array(b_mod, dtype=float)
# 2. Подсчёт дополнительных переменных
n_le = sum(1 for s in signs_mod if s == '<=')
n_ge = sum(1 for s in signs_mod if s == '>=')
n_eq = sum(1 for s in signs_mod if s == '=')
total_vars = n_orig + n_le + 2 * n_ge + n_eq
A_ext = np.zeros((m, total_vars), dtype=float)
b_vec = b_mod.copy()
basis = []
art_vars = []
slack_idx = n_orig
surplus_idx = n_orig + n_le
art_ge_idx = n_orig + n_le + n_ge
art_eq_idx = n_orig + n_le + n_ge + n_ge
row = 0
for i, sign in enumerate(signs_mod):
A_ext[row, :n_orig] = A_mod[i]
if sign == '<=':
A_ext[row, slack_idx] = 1.0
basis.append(slack_idx)
slack_idx += 1
row += 1
elif sign == '>=':
A_ext[row, surplus_idx] = -1.0
A_ext[row, art_ge_idx] = 1.0
basis.append(art_ge_idx)
art_vars.append(art_ge_idx)
surplus_idx += 1
art_ge_idx += 1
row += 1
elif sign == '=':
A_ext[row, art_eq_idx] = 1.0
basis.append(art_eq_idx)
art_vars.append(art_eq_idx)
art_eq_idx += 1
row += 1
# 3. Целевая функция и штрафы
c_ext = np.zeros(total_vars, dtype=float)
c_ext[:n_orig] = c
# Для искусственных переменных: +M при min, -M при max
for a in art_vars:
c_ext[a] = M if sense == 'min' else -M
basis = np.array(basis, dtype=int)
Cb = c_ext[basis]
A0 = b_vec.copy()
eps = 1e-10
# 4. Симплекс-итерации
while True:
z = Cb @ A_ext
deltas = c_ext - z
if sense == 'max':
# проверка оптимальности: все Δj <= 0
if np.all(deltas <= eps):
break
# ввод: максимальная положительная Δj
ent_col = np.argmax(deltas)
if deltas[ent_col] <= eps:
break
else: # минимизация
# проверка оптимальности: все Δj >= -eps (т.е. неотрицательные)
if np.all(deltas >= -eps):
break
# ввод: минимальная отрицательная Δj (наибольшая по модулю)
ent_col = np.argmin(deltas)
if deltas[ent_col] >= -eps:
break
col = A_ext[:, ent_col]
ratios = np.full(m, np.inf)
for i in range(m):
if col[i] > eps:
ratios[i] = A0[i] / col[i]
row = np.argmin(ratios)
if ratios[row] == np.inf:
raise ValueError("Целевая функция не ограничена.")
# Обновление базиса
basis[row] = ent_col
Cb[row] = c_ext[ent_col]
pivot = A_ext[row, ent_col]
A0[row] /= pivot
A_ext[row, :] /= pivot
for i in range(m):
if i != row:
factor = A_ext[i, ent_col]
if abs(factor) > eps:
A0[i] -= factor * A0[row]
A_ext[i, :] -= factor * A_ext[row, :]
# 5. Извлечение решения
x_opt = np.zeros(n_orig, dtype=float)
for i, var_id in enumerate(basis):
if var_id < n_orig:
x_opt[var_id] = A0[i]
f_opt = Cb @ A0
return x_opt, f_opt
# ================= ВАША ЗАДАЧА (исходные данные) =================
if __name__ == "__main__":
c = [175, 107]
A = [
[-30, 9],
[-30, -45],
[-60, -36]
]
b = [-1887, -4425, -4692]
signs = ['>=', '>=', '<=']
x_opt, f_opt = simplex_general(c, A, b, signs, sense='max')
print("Оптимальное решение:")
for i, val in enumerate(x_opt):
print(f" x{i+1} = {val:.6f}")
print(f"Минимальное значение функции: {f_opt:.6f}")