从宝可梦属性DataFrame筛选合规可重复6行组合的方案咨询
问题本质梳理
当前面临的核心效率瓶颈,本质是49个属性行允许重复选取6次,全量可重复组合数约2580万组,硬遍历逐组校验的计算复杂度极高;且这个问题不属于连续优化范畴,硬套常规scipy优化模块反而容易因为非凸、离散属性的问题拉低求解效率。
两个核心规则的本质是:
- 硬约束:排除
max列后剩下的48个属性列,每一列的取值都要从选中的6行里拿到≥预设阈值的最大值,等价于每一个属性列,选中的6行中至少有1行在该列的取值达到阈值要求。 - 优化目标:所有满足硬约束的组合中,6行的
max列累加和越大越好,最后按这个和降序排列所有合规组合。
为什么不建议直接硬套scipy.optimize常规接口
常规scipy优化模块的线性规划、非线性优化接口大多面向连续变量,而这个问题是典型的离散集合覆盖变种——即从49个可选元素里选6次(可重复),满足所有列的覆盖要求,最大化目标和。硬把离散问题松弛成连续求解,不仅解的准确性没保障,额外的整数分支定界开销反而不如针对性的剪枝算法效率高。
高效可落地方案
1. 预处理阶段大幅降本
先对原始DataFrame做两步轻量处理,把后续每一步的计算成本压到最低:
- 对每一个排除
max列的属性列,按照预设阈值对每行做二值映射:只要某行在该列的取值≥阈值,就标记为1,否则为0。处理完之后,每一行就对应一个长度48的0-1特征向量,代表这一行能覆盖哪些属性列的阈值要求。 - 为了进一步加快计算速度,可以把每个行的48位0-1向量压缩成一个64位整数,后续列覆盖的校验只需要做整数按位或运算,比逐列判断快几个数量级。
- 最后把所有行按照
max列的数值做降序排序,因为优化目标是max和越大越好,优先从max值最高的行开始搜索,天然适配后续剪枝逻辑。
2. 回溯搜索+分支剪枝(比硬遍历快几个数量级)
搜索逻辑非常简单,按照排序后的行顺序依次决策:
- 每一层搜索决策:对当前行,决定选0次、1次直到剩余可选取的总次数(毕竟总共只选6行),不需要跳转到其他行选重复值。
- 剪枝规则1(约束剪枝):算当前已经选中的行累计覆盖了多少列,如果剩下所有能选的行(哪怕全选max值最高的剩余行)都补不齐未覆盖的列,直接跳过这个分支,不用往下搜索。
- 剪枝规则2(目标剪枝):算当前已经累计的max和,就算把剩下的选取额度全选max值最高的行,都达不到已经记录的合规组合的最高max和,直接剪掉这个分支。
这种方式实际运行时,通常只需要遍历整个组合空间的不到1%,就能把所有合规组合全部找出来。
3. 若需要调用scipy生态求解器
如果只需要找到max和最高的那一组最优组合,不需要枚举所有合规结果,可以使用scipy.optimize.milp(混合整数线性规划接口)构造问题,具体构造方式:
- 决策变量:
x_i代表第i行被选中的次数,是整数类型,取值范围0 ≤ x_i ≤6 - 目标函数:最小化
-sum(x_i * max_i),milp默认求解最小值,加负号就等价于最大化总max和 - 约束条件:
- 总选取次数约束:
sum(x_i) =6 - 列覆盖约束:对每一个属性列c,所有在c列取值≥阈值的行i,满足
sum(x_i) ≥1——确保每个列都有至少一行能达到阈值要求
- 总选取次数约束:
常见效率踩坑提示
- 不要提前生成全量2500万组的可重复组合再逐组校验,哪怕每组只花1微秒计算,整体也需要几十秒的运行时间,加上逐列取最大值的开销,速度会非常慢。
- 二值化预处理不要省略,这一步能把后续的覆盖校验计算量降低90%以上。
- 不要把允许重复选行的规则想得太复杂,回溯搜索的时候允许在当前行选多次就行,不需要额外生成重复行扩大可选池。
内容的提问来源于stack exchange,提问作者maverick
相关产品推荐
相关产品推荐

