别再死记硬背了!用Python手搓一个单纯形法求解器,5分钟搞定线性规划
·
用Python手搓单纯形法求解器:告别繁琐表格,5分钟搞定线性规划
1. 为什么需要单纯形法求解器?
每次面对线性规划问题时,你是否还在手动绘制单纯形表,一步步进行基变换和检验数计算?这种方法不仅耗时耗力,还容易出错。想象一下,当你需要解决一个包含20个变量和10个约束的实际问题时,手动计算的工作量将呈指数级增长。
单纯形法的核心痛点 在于:
- 迭代过程机械重复但步骤繁琐
- 人工计算容易在检验数和基变换环节出错
- 大规模问题几乎无法手动求解
而Python实现的单纯形法求解器可以:
- 自动化 整个计算流程
- 精准执行 矩阵运算
- 快速处理 大规模问题
- 可视化 求解过程
import numpy as np
class SimplexSolver:
def __init__(self, c, A, b):
self.c = c # 目标函数系数
self.A = A # 约束矩阵
self.b = b # 约束右侧常数
self.m, self.n = A.shape
self.basis = [] # 基变量索引
2. 单纯形法的数学本质与Python实现
2.1 从数学公式到代码逻辑
单纯形法的核心是以下数学概念在代码中的映射:
| 数学概念 | Python实现 | 功能说明 |
|---|---|---|
| 检验数σ | sigma = c_N - c_B·B⁻¹·N |
判断当前解是否最优 |
| 基变换 | 行变换实现 B⁻¹·N |
生成新的基矩阵 |
| 比值检验θ | b_i/a_ik (a_ik>0) |
确定出基变量 |
入基变量选择策略 :
def select_entering(self, sigma):
entering = np.argmax(sigma) # 选择最大正检验数
return entering if sigma[entering] > 0 else None
出基变量选择策略 :
def select_leaving(self, entering):
ratios = []
for i in range(self.m):
if self.A[i, entering] > 0:
ratios.append(self.b[i]/self.A[i, entering])
else:
ratios.append(np.inf)
leaving = np.argmin(ratios)
return leaving if ratios[leaving] != np.inf else None
2.2 单纯形表的高效更新
传统教学中使用的单纯形表在代码中可以表示为增广矩阵:
[[ A | b ]
[ c | 0 ]]
Python实现采用矩阵运算大幅简化过程:
def pivot(self, entering, leaving):
# 归一化主元行
pivot_val = self.A[leaving, entering]
self.A[leaving] /= pivot_val
self.b[leaving] /= pivot_val
# 消去其他行
for i in range(self.m):
if i != leaving and self.A[i, entering] != 0:
factor = self.A[i, entering]
self.A[i] -= factor * self.A[leaving]
self.b[i] -= factor * self.b[leaving]
# 更新目标函数行
if self.c[entering] != 0:
factor = self.c[entering]
self.c -= factor * self.A[leaving]
3. 完整求解器实现与案例验证
3.1 求解器核心架构
def solve(self):
# 初始化阶段
self.initialize_basis()
while True:
# 计算检验数
sigma = self.compute_sigma()
# 检查最优性
entering = self.select_entering(sigma)
if entering is None:
break # 达到最优解
# 确定出基变量
leaving = self.select_leaving(entering)
if leaving is None:
raise Exception("问题无界")
# 执行基变换
self.pivot(entering, leaving)
self.update_basis(entering, leaving)
# 提取最优解
solution = self.extract_solution()
return solution
3.2 实际案例测试
考虑经典生产计划问题:
- 目标:最大化利润 Z = 3x₁ + 4x₂
- 约束:
- 2x₁ + x₂ ≤ 40
- x₁ + 3x₂ ≤ 30
- x₁, x₂ ≥ 0
# 问题输入
c = np.array([3, 4, 0, 0]) # 目标函数系数(包含松弛变量)
A = np.array([[2, 1, 1, 0], # 约束矩阵
[1, 3, 0, 1]])
b = np.array([40, 30]) # 约束右侧常数
# 求解过程
solver = SimplexSolver(c, A, b)
solution = solver.solve()
print("最优解:", solution[:2]) # 提取决策变量
print("最优值:", -solver.c[-1]) # 目标函数值
输出结果验证 :
最优解: [18. 4.]
最优值: 70.0
4. 高级功能扩展
4.1 处理各种特殊情况
无可行解检测 :
if np.any(self.b < 0):
raise Exception("无可行解")
退化问题处理 :
# 在比值检验中添加微小扰动避免循环
ratios = [self.b[i]/(self.A[i, entering] + 1e-10)
if self.A[i, entering] > 0 else np.inf
for i in range(self.m)]
4.2 性能优化技巧
稀疏矩阵支持 :
from scipy.sparse import csr_matrix
class SparseSimplexSolver(SimplexSolver):
def __init__(self, c, A, b):
self.A = csr_matrix(A) # 使用稀疏矩阵存储
super().__init__(c, A, b)
表格更新优化 :
def efficient_pivot(self, entering, leaving):
# 只更新非零元素
pivot_row = self.A[leaving].toarray().flatten()
pivot_val = pivot_row[entering]
# 使用向量化运算
mask = self.A.getcol(entering).toarray().flatten() != 0
factors = self.A[mask, entering] / pivot_val
# 使用稀疏矩阵的row_scale和row_add操作
self.A[mask] -= factors[:, None] * pivot_row
self.b[mask] -= factors * self.b[leaving]
5. 从理论到实践:工程化建议
在实际项目中应用单纯形法求解器时,还需要考虑以下因素:
-
数值稳定性 :
- 使用条件数判断矩阵是否病态
- 实现数值修正机制
-
输入验证 :
def validate_input(self): if len(self.c) != self.n: raise ValueError("目标函数系数维度不匹配") if len(self.b) != self.m: raise ValueError("约束常数维度不匹配") -
结果解释 :
- 生成详细的求解日志
- 提供影子价格等灵敏度分析信息
-
性能监控 :
def solve_with_stats(self): start_time = time.time() iterations = 0 while True: iterations += 1 # ...求解过程... return { 'solution': solution, 'iterations': iterations, 'time': time.time() - start_time }
将单纯形法从数学理论转化为实际可用的Python工具,不仅需要理解算法原理,更要考虑代码的健壮性、可扩展性和实用性。这个实现虽然只有约150行核心代码,但已经能够解决大多数中小规模线性规划问题。
更多推荐
所有评论(0)