基于OR-Tools求解食材最优等比缩放系数满足营养区间约束
解决方案
核心调整逻辑
- 原有代码变量定义错误,不需要为每个营养素单独定义变量,唯一决策变量是所有食材共用的缩放系数k
- 先计算无缩放状态下各营养素的基准总含量,再乘以k后约束在对应营养素的上下限范围内
- 最优解目标为总营养尽可能接近各营养素上下限的中位数,我们将目标设定为最小化各营养素实际值与中位数的相对偏差和,对绝对值偏差做线性化处理适配线性规划要求
修改后完整可运行代码
from ortools.linear_solver import pywraplp # 计算无缩放时所有食材的基准总营养 def get_base_nutrient_sum(ingredients): base = [0.0 for _ in range(4)] for ingredient in ingredients: amount = ingredient[1] per_g_nutrients = ingredient[2] for i in range(4): base[i] += amount * per_g_nutrients[i] return base def main(): nutrients = [ ['卡路里', 557.7, 669.24], ['脂肪', 39.04, 51.43], ['碳水', 0, 13], ['蛋白质', 31.19, 41.59] ] # 名称, 用量(克), [每克卡路里、脂肪、净碳水、蛋白质含量] ingredients = [ ['碎牛肉', 226.79616, [1.27, 0.1, 0, 0.08585000000000001]], ['食盐', 6.083333333333333, [0, 0, 0, 0]], ['黑胡椒', 1.0374999999999999, [1.255 , 0.0163, 0.19325, 0.05195]], ['卡真调味料', 4, [0.46604999999999996, 0.010249999999999999, 0.06565, 0.0191]], ['马苏里拉芝士', 28.34952, [1.5, 0.11175, 0.01095, 0.11085]], ['切达芝士', 56.69904, [2.02, 0.16655, 0.015449999999999998, 0.11435000000000001]], ['黄油', 14.1875, [3.585, 0.40555, 0.0003, 0.00425]], ['熟培根', 28, [2.74, 0.21635000000000001, 0.006750000000000001, 0.17864999999999998]] ] # 初始化求解器 solver = pywraplp.Solver('ScaleIngredients', pywraplp.Solver.GLOP_LINEAR_PROGRAMMING) # 1. 计算无缩放基准总营养 base_nutrients = get_base_nutrient_sum(ingredients) print(f"无缩放基准总营养: {base_nutrients}") # 2. 定义唯一决策变量:缩放系数k k = solver.NumVar(0, 2, 'scaling_factor') # 3. 添加各营养素的范围约束:base_nutrients[i] * k 落在 [min, max] 区间 for i in range(4): nutrient_name, min_val, max_val = nutrients[i] solver.Add(base_nutrients[i] * k >= min_val) solver.Add(base_nutrients[i] * k <= max_val) # 4. 计算各营养素的中位数目标值 targets = [(n[1] + n[2])/2 for n in nutrients] # 5. 定义偏差变量处理绝对值,线性化目标函数(最小化总相对偏差) total_deviation = 0 for i in range(4): # 正负偏差变量 dev_plus = solver.NumVar(0, solver.infinity(), f'dev_plus_{i}') dev_minus = solver.NumVar(0, solver.infinity(), f'dev_minus_{i}') # 约束:实际值 = 目标值 + 正偏差 - 负偏差 solver.Add(base_nutrients[i] * k == targets[i] + dev_plus - dev_minus) # 累加相对偏差,避免高数值营养素权重过大 total_deviation += (dev_plus + dev_minus) / targets[i] # 6. 设定目标:最小化总偏差 solver.Minimize(total_deviation) # 求解 status = solver.Solve() if status == solver.OPTIMAL: optimal_k = k.solution_value() print(f"\n最优缩放系数: {optimal_k}") print("缩放后各营养素含量:") for i in range(4): actual = base_nutrients[i] * optimal_k print(f"{nutrients[i][0]}: {actual:.4f} (目标中位数: {targets[i]:.4f}, 允许范围: [{nutrients[i][1]}, {nutrients[i][2]}])") else: print("无可行解,请检查营养素上下限设置是否合理") if __name__ == '__main__': main()
运行结果说明
运行代码后会直接输出最优缩放系数,以及缩放后的各营养素数值,所有结果都会自动符合设定的范围约束,且尽可能接近各营养素的中位数目标。
内容的提问来源于stack exchange,提问作者Stark
相关产品推荐
相关产品推荐

