尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

数学建模竞赛一等奖方案:基于MILP与图分解的PCI规划算法解析

数学建模竞赛一等奖方案:基于MILP与图分解的PCI规划算法解析 1. 从“撞衫”到“串台”PCI规划问题的本质是什么如果你在移动通信网络优化这个行当里待过哪怕只是听过几耳朵大概率也听说过“PCI冲突”和“PCI混淆”这两个词。它们就像网络里的两个“捣蛋鬼”一个让手机“认错人”一个让手机“听不清”。2024年MathorCup A题直指的就是这个让无数网优工程师头疼的“PCI规划”问题。我这次带队拿了个全国一等奖回头复盘觉得最有价值的不是最终的代码和结果而是整个解题过程中对这个问题从“是什么”到“为什么”再到“怎么办”的层层剥茧。简单来说PCIPhysical Cell Identity物理小区标识就是每个蜂窝小区比如一个基站下的一个扇区的“身份证号”。手机在茫茫信号海洋里就是靠这个号码来识别和连接不同的小区的。这个号码范围不大只有0到1007总共1008个。想象一下一个大型城市的移动网络有成千上万个小区这1008个号码就得循环使用就像只有1008件不同款式的衣服却要给上万人穿还得保证邻居之间不能“撞衫”冲突也不能让一个人同时看到两个穿一模一样衣服的远房亲戚而困惑混淆。所以PCI规划的核心矛盾就出来了有限的号码资源 vs. 复杂的无线环境。题目给出的干扰矩阵和冲突矩阵其实就是把现实世界中“撞衫”和“听不清”的量化关系给抽象出来了。我们的任务就是在这套规则下给所有小区分配合适的PCI让整个网络的“冲突”和“混淆”总代价降到最低。这听起来像个简单的排列组合题但一旦规模上去比如题目中的2067个小区就是一个典型的NP-Hard组合优化问题暴力穷举在有限比赛时间内是绝对不可能的。这就引出了我们解题的第一个关键决策选择什么样的模型和算法来逼近最优解2. 解题工具箱为什么是混合整数线性规划MILP面对“给2067个小区分配1008个PCI”这个问题我们首先排除了智能优化算法如遗传算法、粒子群算法作为主攻方向。虽然它们在解决复杂优化问题上名声在外但在这个具体场景下有几个短板一是收敛速度和结果稳定性在有限时间内比赛通常就几天难以保证二是对于这种强约束每个小区必须且只能分配一个PCI和明确目标函数最小化总干扰的问题数学规划方法往往能给出更“扎实”的理论边界和更稳定的求解过程。我们最终选择了混合整数线性规划MILP作为核心建模框架。这里解释一下“为什么是它”MILP的天然适配性这个问题的决策变量很直观——为每个小区i分配一个PCI值p。这天然就是一个0-1整数决策变量x[i][p] 1表示小区i被分配了PCI p否则为0。目标函数是干扰和冲突的加权和干扰矩阵和冲突矩阵给出了系数这很容易写成线性形式。约束条件也很清晰每个小区必须且只能选一个PCIsum(x[i][:]) 1这就是典型的整数规划约束。所以整个问题可以非常干净、无歧义地建模成一个MILP模型。用专业的话说就是“模型的可表达性非常好”。求解器的威力现代MILP求解器如Gurobi、CPLEX经过数十年的发展其分支定界、切割平面等算法已经极度成熟和高效。对于这种中等规模2067*1008 ≈ 208万个0-1变量的问题一个好的MILP模型配合高性能求解器有很大概率能在可接受时间内找到最优解或者一个质量非常高的可行解上界/下界很紧。这比我们自己从头写一个启发式算法要可靠得多。灵活处理复杂约束题目中除了基本的冲突和干扰还可能隐含或引申出其他约束比如某些特殊场景的小区需要分配特定的PCI组MOD3、MOD30规划用于避免特定的信号干扰。MILP模型可以非常方便地通过添加线性约束来纳入这些要求而智能算法处理这类复杂约束通常更麻烦需要设计特殊的修复算子或罚函数。当然直接对2067个小区和1008个PCI建立完整的MILP模型变量数超过200万即使对于现代求解器也是一个挑战。因此模型简化与降维成为了我们第二步的关键工作。注意在实际比赛中我们使用了Gurobi求解器。它的学术许可可以免费申请对于参加数学建模竞赛的同学来说是非常强大且合法的工具。安装和基础调用并不复杂官方文档和案例也很丰富。3. 化繁为简模型降维与启发式策略设计直接建模的巨量变量会导致求解器内存消耗巨大、求解时间不可控。我们必须想办法缩小搜索空间。我们的策略核心是“先分组后优化”。3.1 基于冲突关系的社区发现图划分我们把每个小区看作图中的一个节点。如果两个小区之间存在冲突关系即冲突矩阵中对应值不为0我们就在它们之间连一条边。这样整个网络就构成了一张图。一个关键的观察是PCI冲突只发生在有边直接相连的小区之间。换句话说如果两个小区在图上是互不连通的属于不同的连通分量那么它们无论分配什么PCI都不会产生冲突代价。基于这个发现我们第一步就是用图论算法如深度优先搜索DFS或并查集找出所有的连通分量。结果发现这2067个小区被划分成了几十个大小不一的连通子图其中最大的一个子图可能包含几百个小区而很多小的子图只有几个甚至一两个小区。这样做的好处是革命性的解耦求解每个连通分量内部的PCI分配是独立的不影响其他分量。我们可以对每个连通分量单独建立MILP模型并求解最后把结果合并即可。这相当于把一个大问题拆成了几十个小问题。极大降维对于只有两三个小区的小分量其PCI分配方案可能一眼就能看出最优解比如分配完全不冲突的PCI甚至无需启动求解器。需要严肃对待的只有那些包含几十个、上百个小区的大分量。并行计算各个分量的求解是完全独立的这为并行计算提供了可能可以充分利用多核CPU资源缩短总时间。3.2 PCI资源的预筛选与剪枝即使在一个连通分量内部1008个PCI也并非都是候选。对于某个小区i我们可以根据冲突矩阵进行预筛选找出所有与小区i存在冲突的小区集合ConflictNeighbors(i)。如果这些邻居小区中已经有某个PCIp被分配在迭代求解或初始解中那么小区i就应尽量避免选择p因为这会直接产生冲突代价。更激进一点的剪枝是对于小区i只考虑那些未被其任何冲突邻居“占用”的PCI。如果这样的PCI数量足够多就能大幅减少变量x[i][p]中 p 的候选范围。在实际操作中我们采用了一种动态剪枝策略先为所有小区随机分配一个不违反硬冲突如果题目有定义的话的PCI作为初始解。然后在针对某个连通分量构建MILP模型时对于其中的每个小区我们只保留其当前PCI以及其冲突邻居未使用的PCI中的一部分例如随机采样K个作为候选。这样就把决策变量从分量小区数 * 1008缩小到了分量小区数 * (K1)通常K取20-50就能包含足够好的解空间而变量数减少了95%以上。3.3 分层优化与迭代改进我们并没有指望一次MILP求解就能得到全局最优解。而是设计了一个分层迭代的框架初始解生成采用贪心算法遍历所有小区为每个小区分配一个使其与已分配邻居冲突和干扰增量最小的PCI。分量分解基于初始解和冲突矩阵进行图连通分量分析。分量内优化对每个连通分量尤其是大型分量使用经过PCI候选集剪枝后的MILP模型进行重新优化。求解器只在这个缩小的空间里搜索更优解。全局整合与迭代更新优化后的分量解合并成新的全局解。计算新的总代价。然后可以以这个新解为基础重新进行PCI候选集的动态剪枝因为邻居关系可能已变然后再次对各个分量进行优化。如此迭代2-3轮直到目标函数值不再明显下降或达到时间限制。这个过程类似于“大规模邻域搜索”MILP在这里扮演了“智能局部搜索”的角色在精心构造的、规模可控的邻域内寻找最优解。4. 代码实现中的关键细节与“坑点”理论模型很美好但代码实现时一堆“坑”。这里分享几个让我们调试了最久的关键细节。4.1 干扰与冲突矩阵的存储与索引优化题目给出的干扰和冲突矩阵通常是稀疏的。2067个小区如果存成全矩阵是2067x2067约400万个元素大部分是0。直接存成二维数组如numpy.ndarray在内存和计算上都是浪费。我们的做法是使用稀疏字典存储。例如对于干扰矩阵# 假设 interference_data 是一个列表每个元素是 (i, j, value) interference_dict {} for i, j, val in interference_data: if val ! 0: # 只存非零值 interference_dict[(i, j)] val在构建MILP目标函数时我们需要遍历所有非零的干扰/冲突对。如果使用稀疏存储遍历次数就是非零元的数量可能只有几十万次而不是400万次。这能极大加快模型构建速度。另一个关键点是索引转换。题目给的小区编号可能不是从0或1开始的连续整数。为了在数组和模型变量中高效索引我们第一步就是建立从原始小区ID到内部连续索引0, 1, 2, ...的映射。original_ids [1001, 1005, 2003, ...] # 原始ID列表 id_to_index {orig_id: idx for idx, orig_id in enumerate(original_ids)} index_to_id {idx: orig_id for idx, orig_id in enumerate(original_ids)}所有内部计算和模型变量都使用连续的index只在最终输出结果时再通过index_to_id映射回原始ID。这能避免字典查找带来的性能开销让代码更清晰。4.2 MILP模型构建的效率技巧使用Gurobi这类求解器时直接通过循环添加变量和约束可能会很慢特别是当变量数达到数十万时。我们采用了以下加速技巧变量批量添加使用model.addVars()一次添加所有变量而不是在循环中逐个添加。# 假设 candidate_pcis[i] 是小区i的候选PCI列表 x model.addVars( [(i, p) for i in range(num_cells) for p in candidate_pcis[i]], vtypeGRB.BINARY, namex )约束批量构建使用线性表达式累加LinExpr或列表推导式来构建约束减少Python层面的循环和函数调用。# 每个小区必须选一个PCI的约束 for i in range(num_cells): model.addConstr( quicksum(x[i, p] for p in candidate_pcis[i]) 1, namefassign_one_{i} )目标函数的高效设置干扰和冲突项都是线性的。我们预先计算好系数然后使用quicksum一次性构建。obj_terms [] # 添加干扰项 for (i, j), coeff in interference_dict.items(): idx_i, idx_j id_to_index[i], id_to_index[j] # 注意这里需要遍历的是i和j的候选PCI的组合如果都分配了特定PCI则贡献coeff # 实际建模中这会产生一个二次项 x[i,p]*x[j,q]需要线性化处理如果系数coeff是常数则需引入辅助变量。 # 更常见的简化是干扰/冲突矩阵直接对应特定的PCI关系。如果题目给出的矩阵已经隐含了PCI则目标函数是线性的。 # 假设题目中干扰矩阵的值就是“如果小区i和j分配了相同的PCI则产生该代价”那么目标函数项为 coeff * y[i,j]其中y[i,j]是表示是否分配了相同PCI的辅助变量。这里引出了最大的一个“坑”目标函数的正确线性化。在很多实际问题中干扰/冲突代价不仅与小区对有关还与它们具体分配的PCI的某种关系如是否相等、模3是否相等有关。这会导致目标函数或约束中出现x[i,p] * x[j,q]这样的二次项。对于0-1变量这种二次项可以通过引入辅助变量和线性约束进行精确线性化但这会显著增加变量和约束的数量。我们必须仔细审题明确代价的计算规则。在MathorCup这道题中经过我们反复确认认为题目给出的干扰矩阵I(i,j)和冲突矩阵C(i,j)是预先计算好的、与PCI无关的常数。也就是说只要小区i和j被同时“激活”即被考虑在内就会产生I(i,j)或C(i,j)的代价而不管它们分配了什么PCI。这大大简化了问题目标函数直接就是线性加权和。如果规则是“分配相同PCI才产生冲突代价”那模型复杂度将是指数级上升。4.3 求解器参数调优与日志解读默认设置的Gurobi可能不会在限定时间内给出好解。我们调整了几个关键参数TimeLimit: 为每个分量的MILP求解设置合理的时间限制如300秒防止在某个难点上卡住。MIPGap: 设定一个可接受的优化间隙如0.5%或0.1%。当求解器找到的解与理论下界的差距小于这个值时就停止搜索。这能在解的质量和求解时间之间取得平衡。Threads: 设置使用的CPU线程数。对于并行求解各个分量我们通常每个进程/线程给一个求解器实例并设置Threads1以避免资源争抢。对于单个大型分量求解可以设置Threads为物理核心数。LogFile和OutputFlag: 将求解日志输出到文件并关闭控制台输出OutputFlag0以保持整洁后期调试时再分析日志。看求解日志是一门学问。重点关注Best Obj和Best Bound: 当前找到的最佳可行解的目标值以及当前证明的理论下界。两者的差距就是Gap。Nodes(探索的搜索树节点数) 和Current Node的深度了解求解进度。如果Gap下降很慢或者Best Obj很久不更新可能意味着问题很难或者初始解/模型有问题。5. 从模型到现实PCI规划实战中的延伸思考比赛模型是高度简化的真实的PCI规划要复杂得多。借着这个一等奖的解题思路我们可以聊聊实际工作中的考量。5.1 MOD3与MOD30规划为什么是3和30比赛中我们最小化的是总干扰和冲突。现实中PCI还直接影响物理层信号的解调。PCI决定了主同步信号PSS和辅同步信号SSS的序列。其中PSS只有3种可能对应PCI mod 3SSS有168种可能对应PCI除以3的商。因此MOD3冲突如果相邻小区PCI mod 3相同手机在初始同步时可能无法正确区分它们导致同步困难或切换失败。因此相邻小区的PCI模3值必须不同这是一个比“PCI不同”更严格的约束。MOD30规划这与下行参考信号RS的频域位置有关。PCI mod 30 决定了RS在资源块中的频域偏移。如果相邻小区MOD30相同它们的RS会在相同的子载波上发送造成持续的相互干扰严重影响信道估计质量。因此紧密相邻的同频小区尤其是共站邻区的PCI模30值也应尽量不同。在实际规划中MOD3通常是硬约束必须满足。MOD30是强优化目标尽可能避免。我们的比赛模型可以很容易地扩展这些约束在MILP模型中为每一对需要MOD3规避的小区对添加约束(p_i % 3) ! (p_j % 3)。这需要引入额外的辅助变量来表示模运算的结果但仍然是线性可表达的。5.2 分层小区与PCI复用距离网络中有宏基站、微基站、室内分布系统等不同层。不同层的小区覆盖范围差异巨大。一个宏小区的覆盖范围内可能包含几十个微小区。PCI规划需要遵循“复用距离”原则相同PCI的小区之间必须保持足够的地理间隔以确保它们不会进入彼此的邻区列表。这个“足够”的距离对于宏小区来说可能是几公里对于微小区可能只有几百米。在模型中这可以通过冲突矩阵来体现。如果两个小区属于不同层且距离很远即使它们理论上PCI相同其冲突矩阵系数也可以设为0。反之如果距离过近即使属于不同层也可能需要设置一个很高的冲突代价来禁止PCI复用。这要求规划工具必须结合精确的地理信息。5.3 动态PCI与自组织网络SON我们比赛解决的是静态规划问题即给定一个固定的网络拓扑和预测的流量一次性分配PCI。但现实网络是动态的基站可能临时故障、新基站会加入、话务热点会随时间如早晚高峰、节假日和空间迁移。静态规划无法适应这种变化。因此在4G/5G网络中引入了自组织网络SON的概念其中就包括动态PCI分配功能。网络可以定期如每天或每小时或基于事件如小区激活/去激活重新计算和优化PCI分配。其核心算法与我们比赛所用的模型在本质上是一致的但要求算法必须更快在线或准在线、更稳健并且能够处理增量式变化即只调整受影响的部分小区而不是全网重规划。这时我们采用的“连通分量分解”和“迭代局部优化”思路就非常有价值可以快速响应局部网络变化。6. 参赛心得不止于模型的解题策略最后分享几点这次参赛的体会可能比技术细节更有普适性。6.1 审题与假设一切的基础数学建模竞赛中对题目背景的理解和合理假设的建立往往比算法本身更重要。比如这道题我们花了大量时间讨论“干扰矩阵和冲突矩阵究竟是与PCI分配无关的常数还是分配了相同PCI后才生效” 这直接决定了模型是线性还是非线性是简单还是复杂。我们通过分析题目描述中的措辞、结合通信常识PCI冲突/混淆的定义并构造极简案例进行推演最终确定了“常数矩阵”的假设。这个假设是整个解题大厦的地基。6.2 可视化让问题与结果“看得见”在解题过程中我们大量使用了可视化工具。用networkx和matplotlib绘制小区冲突关系图直观地看到连通分量的分布判断我们的图划分算法是否正确。将最终的PCI分配结果在地理背景图如果有或拓扑图上用颜色代表PCI值或MOD3值渲染出来。一眼就能看出PCI的复用模式是否合理是否存在明显的冲突簇。这比看一堆数字和指标要直观得多也更容易发现潜在问题。绘制求解器收敛曲线目标值随迭代/时间下降的过程帮助我们评估算法效率和调优方向。6.3 文档与代码的规范性比赛时间紧但我们也坚持写简单的代码注释和关键步骤的文档。这不仅仅是为了最后的论文更是为了团队内部高效协作。当队友在调试一个复杂的约束添加循环时清晰的变量命名和注释能省下大量沟通成本。我们使用Git进行版本管理每次大的模型修改或算法迭代都提交一次并写清楚commit message。这样当某个修改导致结果变差时我们可以快速回溯到之前的稳定版本。6.4 结果的稳健性检验得到一组PCI分配方案和总代价后我们并没有立刻欢呼。而是设计了几种检验方法随机扰动检验随机选择一小部分小区随机改变其PCI然后重新计算总代价。如果代价上升很少说明我们的解可能位于一个非常平坦的“高原”上还有很多等价优解。如果代价急剧上升说明我们的解可能是一个“孤峰”比较脆弱。这有助于我们理解解的质量。敏感性分析微调目标函数中干扰和冲突的权重系数重新求解。观察最优解的结构PCI分配模式是否发生剧烈变化。如果变化不大说明我们的解对权重不敏感比较稳健。边界案例测试构造只有几个小区的微型网络手动推导其最优PCI分配然后用我们的算法去跑看是否能得到相同结果。这是验证算法逻辑正确性的最基本方法。回过头看这道题之所以能拿一等奖关键在于我们没有停留在“套用现成算法”的层面而是真正理解了PCI规划这个通信领域具体问题的内在逻辑并据此设计了“图分解降维 MILP精确局部搜索 迭代改进”的混合策略。它平衡了求解质量和计算效率并且其模块化的思想分解、求解、合并可以扩展到更大规模的网络。代码实现中的那些“坑”比如稀疏存储、索引优化、求解器调参都是将理论模型转化为实际可运行解决方案的必经之路。这个过程远比一个最终的数字结果更有价值。
返回列表