如何用PuLP生成类Excel Solver的线性规划灵敏度分析报告?
PuLP生成Excel Solver风格的灵敏度报告方案
PuLP本身的高层API没有直接暴露目标函数系数和约束右端值的允许增减范围,但可以通过调用底层CBC求解器的灵敏度输出功能,再解析日志来实现。以下是具体步骤:
1. 配置CBC求解器输出灵敏度信息
创建模型时,使用PULP_CBC_CMD并传入-sensitivity参数开启灵敏度分析,同时指定日志文件保存输出:
import pulp # 构建示例LP模型 model = pulp.LpProblem("Example_LP", pulp.LpMaximize) x1 = pulp.LpVariable('x1', lowBound=0) x2 = pulp.LpVariable('x2', lowBound=0) model += 3*x1 + 2*x2, "Objective" model += 2*x1 + x2 <= 100, "Constraint1" model += x1 + x2 <= 80, "Constraint2" model += x1 <= 40, "Constraint3" # 配置求解器,开启灵敏度输出并保存到日志文件 solver = pulp.PULP_CBC_CMD(msg=True, options=['-sensitivity', '-logpath', 'sensitivity_log.txt']) model.solve(solver) # 输出已获取的基础信息 print("=== 基础求解结果 ===") print("变量值与缩减成本:") for var in model.variables(): print(f"{var.name}: {var.varValue:.2f}, 缩减成本: {var.dj:.2f}") print("\n约束松弛值与影子价格:") for name, constraint in model.constraints.items(): print(f"{name}: 松弛值: {constraint.slack:.2f}, 影子价格: {constraint.pi:.2f}")
2. 解析灵敏度日志生成报告
CBC会在日志文件中输出目标系数、约束RHS的允许增减范围,编写解析函数提取这些信息并格式化输出:
def parse_sensitivity_log(log_path, model): var_report = [] const_report = [] current_section = None with open(log_path, 'r') as f: for line in f: line = line.strip() if not line: continue # 切换解析章节 if line.startswith("Variable Sensitivity Analysis"): current_section = "var" continue elif line.startswith("Constraint Sensitivity Analysis"): current_section = "const" continue # 解析变量灵敏度 if current_section == "var": parts = line.split(':') var_name = parts[0].strip() details = parts[1].split(',') obj_coeff = float(details[0].split()[-1]) allow_inc = details[1].split()[-1] allow_inc = float(allow_inc) if allow_inc != "Infinity" else float('inf') allow_dec = details[2].split()[-1] allow_dec = float(allow_dec) if allow_dec != "Infinity" else float('inf') # 匹配已有的缩减成本 dj = next(var.dj for var in model.variables() if var.name == var_name) var_report.append({ "变量名": var_name, "目标系数": obj_coeff, "允许增加量": allow_inc, "允许减少量": allow_dec, "缩减成本": dj }) # 解析约束灵敏度 elif current_section == "const": parts = line.split(':') const_name = parts[0].strip() details = parts[1].split(',') rhs_val = float(details[0].split()[-1]) shadow_price = float(details[1].split()[-1]) allow_inc = details[2].split()[-1] allow_inc = float(allow_inc) if allow_inc != "Infinity" else float('inf') allow_dec = details[3].split()[-1] allow_dec = float(allow_dec) if allow_dec != "Infinity" else float('inf') # 匹配已有的松弛值 slack = model.constraints[const_name].slack const_report.append({ "约束名": const_name, "右端值(RHS)": rhs_val, "影子价格": shadow_price, "允许增加量": allow_inc, "允许减少量": allow_dec, "松弛值": slack }) return var_report, const_report # 生成并打印Excel风格报告 var_sens, const_sens = parse_sensitivity_log("sensitivity_log.txt", model) print("\n=== 变量灵敏度报告(Excel Solver风格) ===") print(f"{'变量名':<10} {'目标系数':<10} {'允许增加量':<15} {'允许减少量':<15} {'缩减成本':<10}") for item in var_sens: print(f"{item['变量名']:<10} {item['目标系数']:<10.2f} {item['允许增加量']:<15} {item['允许减少量']:<15} {item['缩减成本']:<10.2f}") print("\n=== 约束灵敏度报告(Excel Solver风格) ===") print(f"{'约束名':<15} {'右端值(RHS)':<15} {'影子价格':<10} {'允许增加量':<15} {'允许减少量':<15} {'松弛值':<10}") for item in const_sens: print(f"{item['约束名']:<15} {item['右端值(RHS)']:<15.2f} {item['影子价格']:<10.2f} {item['允许增加量']:<15} {item['允许减少量']:<15} {item['松弛值']:<10.2f}")
注意事项
- 该方案依赖PuLP默认的CBC求解器,其他免费求解器(如GLPK)的输出格式不同,需调整解析逻辑。
- CBC版本可能会微调日志格式,若解析失败需根据实际输出修改分割规则。
- 若不想生成日志文件,可通过
subprocess直接调用CBC并捕获stdout,但logpath参数更简便。
内容的提问来源于stack exchange,提问作者KDL4ever
相关产品推荐
相关产品推荐

