You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OR Tools SCIP求解器约束不生效问题排查求助

作物分配线性规划模型约束失效排查

我用OR Tools的SCIP求解器开发了作物分配线性规划模型,小数据集测试时结果符合所有约束,但加载JSON大规模数据后,分配给各作物的总面积超出了预设的crop_limit值,明明已经添加了面积约束,求解器却没遵守,需要排查原因。


小数据集测试代码

from ortools.linear_solver import pywraplp
import pandas as pd
import json

def main():
    # Data
    priority = [
    [30,    20, 10, 10],
    [10,    20, 30, 40],
    [40,    30, 20, 10],
    [30,    20, 10, 10],
    [30,    20, 10, 10],
    [10,    20, 30, 40],
    [20,    20, 10, 10],
    [40,    20, 30, 40],
    [10,    30, 20, 20],
    [10,    40, 20, 10],
    [30,    20, 10, 99],
    [10,    15, 30, 40],
    ]

    field_area = [30,50,100,20,40,30,130,150,110,29,410,320]

    crop_limit = [110,1121,120,140]

    num_crops   = len(priority)
    num_fields   = len(priority[0])
    num_area = len(field_area)
    num_limit = len(crop_limit)
    print ('Fields',   num_crops)
    print ('Crops' , num_fields)
    print ('Area', num_crops)
    print ('Constraints', num_limit)

    solver = pywraplp.Solver.CreateSolver('SCIP')

    # Variables
    x = {}
    for i in range(num_crops):
        for j in range(num_fields):
            x[i, j] = solver.IntVar(0, 1, '')

    # Constraints
    # One field - one crop
    for i in range(num_crops):
        solver.Add(solver.Sum([x[i, j] for j in range(num_fields)]) == 1)

    # Area constraints
    for j in range(num_fields):
      for i in range(num_crops):
        solver.Add(solver.Sum([x[i, j]] *  field_area[i]) <= crop_limit[j])    

    # Objective
    objective_terms = []
    for i in range(num_crops):
        for j in range(num_fields):
            objective_terms.append(priority[i][j] * x[i, j])
            solver.Minimize(solver.Sum(objective_terms))

    # Solve
    status = solver.Solve()

    # Print solution.
    if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE:
        print(f'Total optimum = {solver.Objective().Value()}\n')
        for i in range(num_crops):
            for j in range(num_fields):
                if x[i, j].solution_value() > 0.5:
                    print(f'Field {i} Crop {j}.' +
                          f' Priority: {priority[i][j]}' + f' Area: {field_area[i]}' + f' From limit: {crop_limit[j]}' )
    else:
        print('Task doesent have decision.')

if __name__ == '__main__':
    main()

大规模数据加载代码

from ortools.linear_solver import pywraplp
import pandas as pd
import json

with open('..........SPP_DATA_v4.json') as json_file:
  data = json.load(json_file)
  field_area = data['fld_area']
  crop_limit = data['crop_goal'] 
  priority = data['spp_lines'] 
  num_fields    = len(priority)
  num_crops   = len(priority[0])
  num_area = len(field_area)
  num_limit = len(crop_limit)
  print ('Field',  num_fields )
  print ('Crops' ,num_crops )
  print ('Constraints', num_limit)

  solver = pywraplp.Solver.CreateSolver('SCIP')

    # Variables
  x = {}
  for i in range(num_fields):
      for j in range(num_crops):
          x[i, j] = solver.IntVar(0, 1, '')

    # Constraints
    # one field - one crop
  for i in range(num_fields):
     solver.Add(solver.Sum([x[i, j] for j in range(num_crops)]) == 1)

    # Maximum area constr
  for j in range(num_crops):
    for i in range(num_fields):
      solver.Add(solver.Sum([x[i, j]] *  field_area[i]) <= crop_limit[j])

    # Objective
  objective_terms = []
  for i in range(num_fields):
    for j in range(num_crops) :
        objective_terms.append(priority[i][j] * x[i, j])
        solver.Minimize(solver.Sum(objective_terms))

    # Solve
  status = solver.Solve()

    # Print solution.
  print(status)  
  if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE:
    print(f'Total optimum = {solver.Objective().Value()}\n')
    result_arr = []
    jsondata = {}
    for i in range(num_fields):
       for j in range(num_crops):
         if x[i, j].solution_value() > 0.5:
            result_arr.append({'f':i, 'c':j, 'p':priority[i][j],'a':field_area[i]})
  else:
    print('Task doedent have decision.')
 
  # Save the file
  jsondata['result'] = result_arr  
  jsonString = json.dumps(jsondata, indent=4)  
  with open('/content/drive/MyDrive/Colab Notebooks/response.json', 'w') as outfile:
   outfile.write(jsonString)
  print('File saved.')

核心问题分析与修正方案

  1. 面积约束逻辑完全错误
    你当前的面积约束写法是给每个(字段i,作物j)的组合单独加约束:x[i,j] * field_area[i] <= crop_limit[j],这只限制了单个字段分配给某作物的面积,完全没实现“某作物分配的总面积不超过限制”的需求。

    正确的约束逻辑应该是对每个作物j,求和所有分配给它的字段面积,确保总和不超过crop_limit[j]:

    # 修正后的面积约束(大规模数据版本)
    for j in range(num_crops):
        total_crop_area = solver.Sum([x[i, j] * field_area[i] for i in range(num_fields)])
        solver.Add(total_crop_area <= crop_limit[j])
    

    小数据集测试代码的面积约束同样犯了这个错误,只是小数据规模的巧合让结果看起来符合预期。

  2. 目标函数重复设置冗余
    两个版本的代码都把solver.Minimize放在了循环内部,导致每次循环都重新设置目标函数。虽然最终结果可能不受影响,但属于冗余错误,应移到循环外:

    # 修正后的目标函数(大规模数据版本)
    objective_terms = []
    for i in range(num_fields):
        for j in range(num_crops):
            objective_terms.append(priority[i][j] * x[i, j])
    solver.Minimize(solver.Sum(objective_terms))
    
  3. 小数据集的索引命名混淆
    小数据代码中把num_crops设为len(priority)(实际是字段数量),num_fields设为len(priority[0])(实际是作物数量),索引完全搞反,这种命名混淆会增加后续维护出错概率,建议统一命名规则。

内容的提问来源于stack exchange,提问作者Grigo Port

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 12:56:59