线性规划疑问:最优解为何是数据集最小值?如何调整?
问题描述
使用Pulp库编写医疗人力成本线性规划模型,目标是最小化人力成本/收入比,但求解后最优解始终取数据集中护士工时、医生工时的最小值,希望获得更合理的非最小值最优解,询问如何避免及可添加的额外约束。
附原代码:
import pandas as pd from pulp import LpProblem, LpVariable, lpSum, LpMinimize, LpStatus # Sample hospital data data1 = { 'Hospital_ID': range(1, 11), 'Cost_Per_Nurse_Hour': [50, 55, 60, 45, 52, 58, 53, 49, 56, 54], 'Cost_Per_Doctor_Hour': [5000, 4500, 4000, 5500, 4800, 5100, 4700, 4900, 5200, 4600], 'Doctor_Hours': [100, 110, 95, 105, 108, 115, 98, 102, 107, 103], 'Nurse_Hours': [200, 190, 180, 210, 195, 205, 198, 215, 190, 200], 'Total_Patients': [150, 160, 145, 155, 148, 162, 149, 153, 158, 146], 'Hospital_Revenue': [80000, 75000, 82000, 78000, 79000, 81000, 79000, 80500, 79500, 81500], } data = pd.DataFrame(data1) # Create the optimization problem prob = LpProblem("Maximize_Margin_Labor", LpMinimize) # Define decision variables nurse_hours_ = LpVariable("Nurse_Hours",lowBound=0, cat='Continuous') doctor_hours_ = LpVariable("Doctor_Hours",lowBound=0, cat='Continuous') # Objective function (minimize labor cost/ revenue ratio) total_labor_cost = nurse_hours_ * lpSum(data.loc[i,"Cost_Per_Nurse_Hour"] for i in data.index) + doctor_hours_ * lpSum(data.loc[i,"Cost_Per_Doctor_Hour"] for i in data.index) margin_labor = total_labor_cost / lpSum(data["Hospital_Revenue"])*100 prob += margin_labor # Existing constraints prob += nurse_hours_ >= 190 # minimum nurse hours observed prob += nurse_hours_ <= 215 # maximum nurse hours observed prob += doctor_hours_ >= 95 # minimum doctor hours observed prob += doctor_hours_ <= 115 # maximum doctor hours observed prob += nurse_hours_ - 2.1*doctor_hours_ <= 0 # 2.1 maximum observed Nurse Hours/Doctor Hours prob += nurse_hours_ - 1.73*doctor_hours_ >= 0 # 1.73 minimum observed Nurse Hours/Doctor Hours # Solve the problem status = prob.solve() # Print the results print("Solver status:", LpStatus[status]) optimal_nurse_hours = int(nurse_hours_.value()) optimal_doctor_hours = int(doctor_hours_.value()) # Calculate labor cost and labor cost/revenue ratio for the optimal solutions total_labor_cost = optimal_nurse_hours * lpSum(data["Cost_Per_Nurse_Hour"]) + optimal_doctor_hours * lpSum(data["Cost_Per_Doctor_Hour"]) margin_labor_percentage = (total_labor_cost / lpSum(data["Hospital_Revenue"])) * 100 print("Optimal Nurse Hours:", optimal_nurse_hours) # 190 minimum nurse hours observed print("Optimal Doctor Hours:", optimal_doctor_hours) # 95 minimum doctor hours observed print("Margin Labor Percentage:", margin_labor_percentage, "%")
原因分析
当前模型的目标是最小化人力成本占收入的比例,而护士和医生的单位工时成本均为正数,线性规划的特性会让目标函数在可行域的成本最低点(即工时下限)取得最优解——因为工时越少,总人力成本越低,自然满足最小化比例的目标。现有约束仅限定了工时的上下范围和比例关系,但没有从业务逻辑上限制“不能仅用最少人力”的要求。
可添加的额外约束
以下约束基于医疗业务逻辑设计,能避免模型取工时最小值,同时保证解的合理性:
1. 基于患者数量的服务能力约束
根据历史数据计算每个患者所需的最低护士/医生工时,确保人力能覆盖患者需求:
- 计算历史平均护士工时/患者:
avg_nurse_per_patient = data['Nurse_Hours'].sum() / data['Total_Patients'].sum() - 计算历史平均医生工时/患者:
avg_doctor_per_patient = data['Doctor_Hours'].sum() / data['Total_Patients'].sum() - 添加约束:总护士工时 ≥ 总患者数 × 最低护士工时/患者;总医生工时 ≥ 总患者数 × 最低医生工时/患者
2. 工时的均值偏移约束
设定工时不能低于历史均值的一定比例,避免过度偏离合理区间:
- 计算历史护士工时均值:
avg_nurse_hours = data['Nurse_Hours'].mean() - 计算历史医生工时均值:
avg_doctor_hours = data['Doctor_Hours'].mean() - 添加约束:
nurse_hours_ >= avg_nurse_hours * 0.9(比如不低于均值的90%);doctor_hours_ >= avg_doctor_hours * 0.9
3. 避免边界解的硬约束(可选,较生硬)
直接设定工时变量高于现有最小值,比如:
nurse_hours_ >= 195(比原最小值190高)doctor_hours_ >= 100(比原最小值95高)
4. 多目标优化约束
如果允许轻微增加成本,换取更合理的工时,可以引入加权多目标:
- 原目标(最小化成本比例)为主目标,添加辅助目标(最大化工时或贴近均值),赋予权重平衡两者。比如:
# 辅助目标:让工时贴近历史均值,权重可调整 dev_nurse = abs(nurse_hours_ - avg_nurse_hours) dev_doctor = abs(doctor_hours_ - avg_doctor_hours) prob += margin_labor + 0.1*(dev_nurse + dev_doctor) # 0.1为权重,可根据需求调整
修改后的示例代码
以下是添加了患者服务能力约束和均值偏移约束的完整代码:
import pandas as pd from pulp import LpProblem, LpVariable, lpSum, LpMinimize, LpStatus # Sample hospital data data1 = { 'Hospital_ID': range(1, 11), 'Cost_Per_Nurse_Hour': [50, 55, 60, 45, 52, 58, 53, 49, 56, 54], 'Cost_Per_Doctor_Hour': [5000, 4500, 4000, 5500, 4800, 5100, 4700, 4900, 5200, 4600], 'Doctor_Hours': [100, 110, 95, 105, 108, 115, 98, 102, 107, 103], 'Nurse_Hours': [200, 190, 180, 210, 195, 205, 198, 215, 190, 200], 'Total_Patients': [150, 160, 145, 155, 148, 162, 149, 153, 158, 146], 'Hospital_Revenue': [80000, 75000, 82000, 78000, 79000, 81000, 79000, 80500, 79500, 81500], } data = pd.DataFrame(data1) # Create the optimization problem prob = LpProblem("Maximize_Margin_Labor", LpMinimize) # Define decision variables nurse_hours_ = LpVariable("Nurse_Hours",lowBound=0, cat='Continuous') doctor_hours_ = LpVariable("Doctor_Hours",lowBound=0, cat='Continuous') # Objective function (minimize labor cost/ revenue ratio) total_labor_cost = nurse_hours_ * lpSum(data.loc[i,"Cost_Per_Nurse_Hour"] for i in data.index) + doctor_hours_ * lpSum(data.loc[i,"Cost_Per_Doctor_Hour"] for i in data.index) total_revenue = lpSum(data["Hospital_Revenue"]) margin_labor = (total_labor_cost / total_revenue) * 100 prob += margin_labor # Existing constraints prob += nurse_hours_ >= 190 # minimum nurse hours observed prob += nurse_hours_ <= 215 # maximum nurse hours observed prob += doctor_hours_ >= 95 # minimum doctor hours observed prob += doctor_hours_ <= 115 # maximum doctor hours observed prob += nurse_hours_ - 2.1*doctor_hours_ <= 0 # 2.1 maximum observed Nurse Hours/Doctor Hours prob += nurse_hours_ - 1.73*doctor_hours_ >= 0 # 1.73 minimum observed Nurse Hours/Doctor Hours # ---------------------- 添加的新约束 ---------------------- # 1. 基于患者数量的服务能力约束 total_patients = lpSum(data["Total_Patients"]) avg_nurse_per_patient = data['Nurse_Hours'].sum() / total_patients.value() avg_doctor_per_patient = data['Doctor_Hours'].sum() / total_patients.value() # 确保工时满足至少覆盖患者需求(可调整系数,比如0.95为95%的覆盖) prob += nurse_hours_ >= total_patients * avg_nurse_per_patient * 0.95 prob += doctor_hours_ >= total_patients * avg_doctor_per_patient * 0.95 # 2. 均值偏移约束:不低于历史均值的90% avg_nurse_hours = data['Nurse_Hours'].mean() avg_doctor_hours = data['Doctor_Hours'].mean() prob += nurse_hours_ >= avg_nurse_hours * 0.9 prob += doctor_hours_ >= avg_doctor_hours * 0.9 # --------------------------------------------------------- # Solve the problem status = prob.solve() # Print the results print("Solver status:", LpStatus[status]) optimal_nurse_hours = round(nurse_hours_.value(), 2) optimal_doctor_hours = round(doctor_hours_.value(), 2) # Calculate labor cost and labor cost/revenue ratio for the optimal solutions total_labor_cost = optimal_nurse_hours * lpSum(data["Cost_Per_Nurse_Hour"]) + optimal_doctor_hours * lpSum(data["Cost_Per_Doctor_Hour"]) margin_labor_percentage = (total_labor_cost / total_revenue) * 100 print("Optimal Nurse Hours:", optimal_nurse_hours) print("Optimal Doctor Hours:", optimal_doctor_hours) print("Margin Labor Percentage:", round(margin_labor_percentage, 2), "%")
说明
修改后的模型会在满足业务合理性的前提下,寻找成本比例最低的解,不会再直接取工时最小值。你可以根据实际业务需求调整约束中的系数(比如覆盖比例、均值比例等),平衡成本和服务质量。
内容的提问来源于stack exchange,提问作者Heus
相关产品推荐
相关产品推荐

