如何修改代码实现单类别最多选一次的Totpod最大化求解
问题分析与解决方案
原代码的核心问题是决策变量定义错误,以及目标函数和约束逻辑完全偏离需求。你需要为每个数据行(而非每个Cat类别)创建二进制变量,同时约束每个Cat类别下最多选中一行,最终最大化选中行的Totpod总和。
修正后的Pulp实现代码
import pandas as pd from pulp import LpProblem, LpVariable, LpMaximize, lpSum, value # 构建数据框,保留原始索引a/b/c data = {'a': ['Amu', 508], 'b': ['Coif', 508], 'c': ['Amu', 253]} df = pd.DataFrame.from_dict(data, orient='index', columns=['Cat', 'Totpod']) # 为每个行(索引a/b/c)创建二进制决策变量:1表示选中该行,0表示不选 row_vars = LpVariable.dicts("Row", df.index, cat="Binary") # 创建最大化问题 prob = LpProblem("Maximize_TotPod", LpMaximize) # 目标函数:最大化选中行的Totpod总和 prob += lpSum(row_vars[row] * df.loc[row, 'Totpod'] for row in df.index) # 约束条件:每个Cat类别下,选中的行数量不超过1 for cat in df['Cat'].unique(): prob += lpSum(row_vars[row] for row in df[df['Cat'] == cat].index) <= 1 # 求解问题 prob.solve() # 提取选中的行索引和总和 selected_rows = [row for row in df.index if row_vars[row].value() == 1] total_totpod = value(prob.objective) print("Selected index:", selected_rows) print("Max Totpod:", total_totpod)
关键修改点说明
- 决策变量调整:将原有的
cat_vars改为row_vars,针对每个数据行(a/b/c)创建二进制变量,精准控制每行是否被选中。 - 目标函数修正:直接累加选中行的Totpod值,而非按Cat类别求和后再计算。
- 约束逻辑修正:针对每个唯一的Cat类别,约束该类别下选中的行数量≤1,确保每个类别最多选一次。
- 结果计算修正:移除错误的
totpod_sum累加,直接取目标函数的计算值作为总和,同时输出选中的行索引而非Cat值。
可选方案:使用Scipy优化器实现
如果你想更换优化器,可使用Scipy的linprog(注意Scipy默认是最小化,需转换为最大化问题):
import pandas as pd from scipy.optimize import linprog # 构建数据 data = {'a': ['Amu', 508], 'b': ['Coif', 508], 'c': ['Amu', 253]} df = pd.DataFrame.from_dict(data, orient='index', columns=['Cat', 'Totpod']) # 目标函数系数:因为linprog是最小化,所以取负数 c = -df['Totpod'].values # 约束矩阵:每个Cat类别对应一行约束,确保选中数量≤1 constraints = [] for cat in df['Cat'].unique(): row_mask = df['Cat'] == cat constraint_row = [1 if row_mask.iloc[i] else 0 for i in range(len(df))] constraints.append({'type': '≤', 'fun': lambda x, cr=constraint_row: sum(x * cr) - 1}) # 变量边界:每个变量是0或1的二进制变量 bounds = [(0, 1) for _ in range(len(df))] # 求解 result = linprog(c, bounds=bounds, constraints=constraints, method='highs') # 提取结果 selected_rows = df.index[result.x.round() == 1].tolist() total_totpod = -result.fun print("Selected index:", selected_rows) print("Max Totpod:", total_totpod)
内容的提问来源于stack exchange,提问作者Wick
相关产品推荐
相关产品推荐

