ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

2024国赛C题农作物种植策略:混合整数线性规划建模与求解

2024国赛C题农作物种植策略:混合整数线性规划建模与求解 简介2024国赛C题农作物的种植策略高分方案面向参加全国大学生数学建模竞赛的队伍也适合农业种植决策优化课题作为参考。项目完整覆盖问题一至问题三的求解思路综合运用非线性回归、线性回归、皮尔森相关性检验、K均值聚类等方法并以多个脚本文件分模块实现论文中给出了具体分析和操作说明。压缩包共二十四个文件以十二个数据表、八个脚本、两份文档和两份说明为主整体仅二点二一兆轻量易用。数据表涵盖地块、作物、预期销售量、种植成本、销售单价及二〇二三年全年汇总信息聚类类别表格均已整理代码按照第一问、第二问、第三问等模块划分便于逐问复现和结果核查。已有二百五十三人学习可用于对照论文理解建模流程、快速运行脚本或在此基础上改进优化策略。1. 2024国赛C题农作物的种植策略到底在求什么2024国赛C题《农作物的种植策略》是数模国赛里典型的资源分配优化题把几十块地、若干作物、七年窗口放在同一张表里求解利润最大的逐年种植安排。模型核心不是时间序列预测而是把“不重茬、豆科养地、销售上限、大棚类型限制”翻译成约束再交给求解器代码实现上主流路线是混合整数线性规划配合 Python 的 PuLP 或 MATLAB 的 intlinprog。下面按建模、代码、论文、验证四段展开给出一条可以照做的完整路径。新手照着参数表能把程序跑通老手则能把约束边界和灵敏度分析做到答辩不虚。2. 农作物种植策略的建模把“轮作豆科养地销售上限”写成约束2.1 为什么2024国赛C题首选混合整数线性规划先明确一个问题这道题的决策是离散的。每一块地在每一年种什么作物是“选一种”而不是“种一点算一点”所以决策变量天然是0-1型。连续优化手段比如梯度下降在这类问题上没有用武之地而遗传算法、模拟退火这类启发式算法虽然也能写出漂亮的流程图但解的好坏没有理论上限答辩时被问“你凭什么说你这个解接近最优”很难回答。混合整数线性规划MIP的好处就是可解释且可验证目标函数和所有约束都是线性的求解器CBC、Gurobi、intlinprog直接返回全局最优解和gap。国赛C题这个体量几十块地乘七年乘十几种作物变量最多几千个MIP在开源求解器下几秒到几分钟就能收敛。历年的数模国赛优秀论文里凡是优化类问题用MIP的评阅基本不会在算法合理性上挑毛病。如果题目变成了“考虑价格波动”这种带随机性的问法建模就要升级把价格离散成几个情景给每个情景一个概率目标改成期望利润最大这就是两阶段随机规划。但基础问法的主干仍然是MIP随机化是后续扩展方向不是第一版模型的必选项。2.2 目标函数与四类约束的数学表达设x[i][y][c]为0-1变量第i块地第y年种作物c时取1否则取0S_i是第i块地的面积Y_c是作物c的亩产P_c和C_c分别是收购价与亩成本。目标函数是所有地块七年利润之和max Σ_i Σ_y Σ_c S_i ( Y_c P_c - C_c ) x[i][y][c]单位要统一面积用亩产量用kg/亩价格用元/kg成本用元/亩最后利润单位是元。很多队伍代码算出结果后数量级对不上十有八九是价格单位写成了元/吨。四类约束对应题目的四个硬性条件每块地每年恰好种一种作物Σ_c x[i][y][c] 1。不能重茬相邻两年种同一作物的组合禁止x[i][y][c] x[i][y-1][c] 1。2024年那一期还要和2023年已知的种植表做同样比较。豆类作物当年种植面积占总面积的比例落在5%到10%之间这是“用地养地”的约束。每种作物的年产量不超过预期销售量超过部分不能按正常价格计入利润。地块类型和作物之间的兼容关系是另一个前置条件常见的数据形态如下表地块类型可种作物说明普通大田小麦、玉米、水稻、黄豆水稻只在水浇条件好的地块普通大棚黄豆、白菜、胡萝卜、西红柿可以种豆类完成养地指标智慧大棚白菜、胡萝卜、西红柿技术限制不能种粮食作物这张表直接决定变量生成时哪些组合可以不建。别小看这一步兼容性检查漏掉一个组合求解器可能给出一个“智慧大棚种小麦”的荒谬解而模型根本不会报错。2.3 决策变量的粒度整块地决策与按面积分配的取舍C题数据按地块逐块给出最常见的做法是“整块地0-1决策”一块地一年只种一种作物变量个数等于地块数乘年数乘作物数。这个粒度的优点是轮作约束写起来最直接结果也好画成地块-年份矩阵图。如果细究题目文字有些地块一年里可能种两季夏茬和冬茬分开这时候要把变量扩展成x[i][y][s][c]s取夏茬或冬茬每块地每年从“种一种”变成“两季各一种”。扩展后还要加茬口衔接约束比如冬小麦收割后夏茬只能种玉米或黄豆不能种生育期过长的作物。我一般会先按单季模型跑通再在后面的问法里扩展两季这样出错时容易定位。面积连续变量的做法是让一块地一年同时种多种作物需要额外的布尔变量标记“是否种植”来线性化面积与利润的乘积项变量规模翻倍对C题的数据量来说没有必要。除非题目明确要求模拟“部分地块休耕”否则整块地0-1是最优选择。3. 用Python落地种植策略PuLP线性整数规划求解与结果输出3.1 数据准备地块、作物参数与2023年初始种植表先把附件整理成三张表地块表、作物表、上一年种植表。下面是一份可直接运行的示例数据实际比赛以附件为准但结构可以直接套用。from pulp import (LpProblem, LpMaximize, LpVariable, lpSum, LpBinary, LpStatus, value, PULP_CBC_CMD) # 年份窗口2024-2030共7年 years [2024, 2025, 2026, 2027, 2028, 2029, 2030] # 地块表编号 - (类型, 面积亩) fields { B1: (大田, 10), B2: (大田, 15), B3: (大田, 12), G1: (普通大棚, 6), G2: (普通大棚, 8), Z1: (智慧大棚, 5), Z2: (智慧大棚, 6), } # 作物表作物 - (类别, 亩产kg, 价格元/kg, 亩成本元, 年销售上限kg) crops { 小麦: (粮食, 400, 2.4, 600, 8000), 玉米: (粮食, 500, 2.2, 550, 10000), 水稻: (粮食, 450, 2.8, 700, 5000), 黄豆: (豆类, 200, 4.5, 400, 3000), 白菜: (经济, 3500, 0.8, 900, 20000), 胡萝卜: (经济, 2000, 1.5, 800, 15000), 西红柿: (经济, 3000, 2.0, 1200, 12000), } # 2023年末的种植情况用于2024年“不重茬”约束 prev_crop { B1: 玉米, B2: 小麦, B3: 黄豆, G1: 白菜, G2: 西红柿, Z1: 西红柿, Z2: 胡萝卜, }这里的销售上限是“当年最多卖这么多kg”建模前先看它与面积的乘积关系如果全种满都摸不到上限说明约束是松的结果和没有它一样如果几种作物全都卡在上限说明它们才是利润的主要来源。初始种植表 prev_crop 必须和地块类型兼容表对得上否则第一年约束可能直接让某个地块无解。3.2 核心代码变量定义、约束循环与求解# 地块-作物兼容性检查 def ok_field_crop(f, c): ftype fields[f][0] if c 水稻 and f ! B2: # 只有B2有水浇条件 return False if ftype 大田: return c in [小麦, 玉米, 水稻, 黄豆] if ftype 普通大棚: return c in [黄豆, 白菜, 胡萝卜, 西红柿] if ftype 智慧大棚: return c in [白菜, 胡萝卜, 西红柿] return False # 0-1决策变量第i块地、第y年、第c种作物 x {} for f in fields: for y in years: for c in crops: if ok_field_crop(f, c): x[(f, y, c)] LpVariable(fx_{f}_{y}_{c}, catLpBinary) prob LpProblem(FarmPlan_2024C, LpMaximize) # 目标7年总利润最大单位元 prob lpSum( fields[f][1] * (crops[c][1] * crops[c][2] - crops[c][3]) * x[(f, y, c)] for f in fields for y in years for c in crops if (f, y, c) in x ) # 约束1每块地每年恰好种一种作物 for f in fields: for y in years: prob lpSum(x[(f, y, c)] for c in crops if (f, y, c) in x) 1 # 约束2不能重茬 for yi, y in enumerate(years): for f in fields: if yi 0: # 2024年对照2023年已知种植 for c in crops: if (f, y, c) in x and c prev_crop[f]: prob x[(f, y, c)] 0 else: py years[yi - 1] for c in crops: if (f, y, c) in x and (f, py, c) in x: prob x[(f, y, c)] x[(f, py, c)] 1 # 约束3豆类面积占总面积5%-10% total_area sum(fields[f][1] for f in fields) for y in years: bean_area lpSum(fields[f][1] * x[(f, y, 黄豆)] for f in fields if (f, y, 黄豆) in x) prob bean_area 0.05 * total_area prob bean_area 0.10 * total_area # 约束4每种作物年产量不超过预期销售量 for y in years: for c in crops: prob lpSum( fields[f][1] * crops[c][1] * x[(f, y, c)] for f in fields if (f, y, c) in x ) crops[c][4] # 求解 solver PULP_CBC_CMD(msgFalse, timeLimit120) status prob.solve(solver) print(求解状态:, LpStatus[status]) print(最优目标值:, value(prob.objective))这段代码有四个容易写错的地方一是ok_field_crop里的水稻特判因为示例数据里只有B2有水浇条件实际按附件改成对应地块即可二是目标函数里if (f, y, c) in x的过滤必须带否则会把未创建的变量拉进表达式PuLP会抛KeyError三是轮作约束第一年单独处理初始种植表不能直接当成求出来的结果它是题目给的“现状”四是timeLimit120是最后一道保险求解器超时也会返回当前最好解不要因为跑得慢就换启发式算法。3.3 结果输出与常见求解异常排查if LpStatus[status] Optimal: for y in years: plan [] for f in fields: for c in crops: if (f, y, c) in x and x[(f, y, c)].value() 0.5: plan.append((f, c)) profit sum( fields[f][1] * (crops[c][1] * crops[c][2] - crops[c][3]) for f, c in plan ) bean_area sum(fields[f][1] for f, c in plan if c 黄豆) print(f{y}: , .join(f{f}{c} for f, c in plan) f | 利润 {profit:.2f} | 大豆面积 {bean_area} 亩)跑完这段输出的是每年各地块种什么、当年利润和豆类面积。豆类面积应该稳定落在总面积乘5%到10%之间如果某年超过说明豆类品种的销售上限或地块兼容性写错了。常见的异常就三类。Infeasible先把豆类比例的下界改成0再跑如果可解问题出在养地约束多数情况是“黄豆能种的地块面积总和不足以凑满5%”需要放宽到大棚如果仍然无解检查2023年初始种植表有没有种在不能种的地块上。Unbounded一般是没有加销售上限或“必须种一种”的约束目标函数变成线性无界把两个约束补上就好。求解时间超过120秒检查是不是变量创建时类别写错或地块类型兼容约束漏掉导致搜索空间暴增。4. 高分论文的结构从模型假设到种植策略结果表的表达4.1 论文摘要、问题分析与模型假设怎么写国赛论文看摘要的时间很短摘要第一句就要亮明问题类型针对2024国赛C题农作物种植策略问题建立了以七年总利润最大为目标的0-1整数线性规划模型。第二句写模型特色把不重茬、豆类养地、销售上限三个约束点出来。第三句写求解方法与核心结果例如使用Python的PuLP CBC求解器求得全局最优解七年总利润为XX万元。第四句给灵敏度结论比如智慧大棚作物的价格波动对种植结构影响最大。四句话各司其职不要在第一段堆背景。问题分析部分不要复述题目要用一句话把“数据表变约束、约束定变量、变量交给求解器”这条链画出来。模型假设写三条足够各作物单产取数据表给出的均值价格与成本在规划窗口内按给定趋势外推地块基础设施条件不变。假设写太多反而暴露对数据口径不清楚。4.2 结果呈现矩阵表与热力图比柱状图更值钱优化模型的输出是地块-年份矩阵文字描述不清楚柱状图也只能展示面积总量看不出“哪块地哪年种了什么”。我一般会输出一张热力图横轴是地块编号纵轴是年份单元格颜色表示作物种类。import matplotlib.pyplot as plt import numpy as np crop_names list(crops.keys()) crop_index {c: i for i, c in enumerate(crop_names)} # plan_matrix[i][j] 第i年、第j块地的作物编号 plan_matrix np.zeros((len(years), len(fields)), dtypeint) for yi, y in enumerate(years): for fi, f in enumerate(fields): for c in crops: if (f, y, c) in x and x[(f, y, c)].value() 0.5: plan_matrix[yi][fi] crop_index[c] plt.imshow(plan_matrix, cmaptab20, aspectauto) plt.yticks(ticksrange(len(years)), labelsyears) plt.xticks(ticksrange(len(fields)), labelslist(fields.keys()), rotation45) plt.colorbar(label作物编号) plt.title(2024-2030 各地块种植策略热力图) plt.show()imshow 有两个参数值得说cmap 用 tab20 是保证不同作物色块不撞色aspectauto 防止横轴地块少时图形被拉高。热力图下面必须附一张作物编号对照表不然颜色含义要靠猜。正文里还要放一张“分年度作物种植面积表”结构如下数据列含义生成方式年份2024-2030循环变量作物七种作物名称固定列种植亩数当年该作物总面积按x变量累加面积净利润当年总利润目标函数按年拆分这张表看着简单但它是第三问写策略调整建议的直接证据把每年面积的变化趋势写进文字里比单列一串数字强得多。4.3 模型评价与附件代码整理的避坑点模型评价不要写“模型简单、易于实现”这种话。高分写法是把边界说清楚优点是MIP带最优性保证结果可复现缺点是未考虑极端天气和市场相关性后续可以扩展为两阶段随机规划或鲁棒优化。这样的评价才算完成“模型-求解-验证”闭环。附件代码按00_data.py、01_model.py、02_solve.py、03_plot.py命名所有路径写成相对路径主程序一行命令跑完。评阅老师拿到附件的第一步是运行跑不通就是零分和满分的区别。求解器选择上也别贪大开源CBC对这个体量够用真要用商业求解器论文里要写清楚使用说明避免获奖后被复核时陷入麻烦。5. 种植策略的灵敏度检验三个能让你少丢分的验证技巧5.1 用小算例验证轮作约束是否真的生效取两块面积相同的地块、两种作物做小算例作物A亩利润120元作物B亩利润70元两年窗口禁止重茬。手算结果很明确每块地第一年种A、第二年种B总利润为(12070)×2×面积。把主程序里的地块和作物列表裁剪成这个规模跑出来和手算一致说明轮作约束实现正确。然后注释掉轮作约束再跑一次目标值会变大这个差值就是“轮作的机会成本”写进论文可以解释为什么某些年份会选择低利润作物。5.2 检查约束松弛度找出被卡住的瓶颈求解完成后把销售上限约束的松弛量输出出来。哪种作物连续多年松弛为0就说明它每年都种到销量上限是利润最厚的品种扩大销路比调整土地更有效。豆类面积比例如果总是贴着5%下界说明种黄豆本身不赚钱、完全靠政策约束撑着这个结论可以直接支撑“政策干预成本”的分析。智慧大棚地块如果出现连续多年种同一作物的趋势要人工核对轮作约束是否被正确施加因为0-1模型不会自己纠正数据错误。5.3 参数扫描价格波动下种植策略何时跳变把最赚钱的作物挑出来价格乘以0.8到1.2的系数循环求解base_price crops[西红柿][2] for r in [0.8, 0.9, 1.0, 1.1, 1.2]: crops[西红柿] (crops[西红柿][0], crops[西红柿][1], round(base_price * r, 3), crops[西红柿][3], crops[西红柿][4]) # 重新调用 solve()记录西红柿种植面积与目标值记录每个系数下的种植面积和目标值价格-面积曲线会出现明显的阶梯跳变点就是策略切换阈值。论文里写“西红柿价格下跌10%时最优策略从连续种植转为轮换种植”这类结论比只给一个总利润数字有说服力得多。扫描结果和基础情景放在同一张表里方便评委直接对比。本文还有配套的精品资源点击获取
返回列表