跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

Gekko 中实现带时间间隔的连续周期与最小间隔联合约束

本文介绍如何在 Gekko 中对非连续时间索引(如跳周的 week = [13, 14, 17, 18, ...])建模同时满足「最小持续时长 d」和「最小间隔 s」的二元决策约束,核心是将问题重构为“起始点选择 + 后处理展开”,避免在稀疏时间轴上直接枚举窗口。 本文介绍如何在 gekko 中对非连续时间索引(如跳周的 `week = [13, 14, 17, 18, ...]`)建模同时满足「最小持续时长 d」和「最小间隔 s」的二元决策约束,核心是将问题重构为“起始点选择 + 后处理展开”,避免在稀疏时间轴上直接枚举窗口。 在实际优化场景中(如促销排期、设备启停计划),我们常需确保某类活动: 持续至少 d 个连续时间单位 (例如连续 2 周促销); 相邻两次活动起始点至少间隔 s+1 个时间单位 (即中间至少空 s 周,如 s=1 表示不能连续两周启动新活动,但允许重叠?不——此处指“启动间隔”)。 难点在于:当时间轴本身存在间隙(如 week = [13, 14, 17, 18, 33, 34, ...]),传统基于 i, i+1, i+d 的数组下标逻辑会失效——因为 week[i] + 1 不一定存在于 week 中,更无法保证 week[i] + d 存在。 ✅ 正确解法是 分离建模逻辑 : 仅在合法起始位置定义整数变量 st[i] ∈ {0,1} ,表示“是否在第 i 个时间点(即 week[i])启动一个新周期”; 通过预计算约束确保: 若 st[i] = 1,则后续 d−1 个实际存在的连续周必须可用(即 week[i], week[i+1], ..., week[i+d−1] 必须构成严格递增且无跳跃的子序列); 任意两个 st[i] = 1 和 st[j] = 1 满足 week[j] ≥ week[i] + d + s(即前一周期结束周 week[i]+d−1 与后一周期起始周 week[j] 至少间隔 s 周); 最终执行状态 x7[k] 通过后处理生成 :对每个 i,若 st[i] == 1,则将 x7[i] 至 x7[i+d−1] 置为 1(前提是索引有效)。 以下是适配原始问题中非连续 week 数组的完整实现:
import numpy as np import pandas as pd from gekko import GEKKO # 示例时间轴(含间隙) week = pd.Series([13, 14, 17, 18, 19, 26, 28, 33, 34, 35, 39, 42, 45, 46, 47, 48, 50, 51, 52]) n = len(week) d = 2 # 最小持续周数 s = 1 # 最小间隔周数(即两段 promo 起始周至少相差 d + s) m = GEKKO(remote=False) m.options.SOLVER = 1 m.options.IMODE = 3 m.options.NODES = 3 # 决策变量:st[i] = 1 表示在 week[i] 启动一个新 promo 周期 st = m.Array(m.Var, n, integer=True, lb=0, ub=1) # 【约束1】起始位置必须有足够后续周以满足持续时长 d for i in range(n): # 检查从 week[i] 开始,是否存在 d 个连续周(即 week[i], week[i+1], ..., week[i+d-1] 都存在且等差为1) if i + d - 1 >= n: m.Equation(st[i] == 0) # 越界,禁止启动 else: # 验证是否连续:week[i+k] == week[i] + k 对所有 k=0..d-1? is_continuous = all(week.iloc[i+k] == week.iloc[i] + k for k in range(d)) if not is_continuous: m.Equation(st[i] == 0) # 【约束2】启动间隔约束:任意两个启动点满足 week[j] >= week[i] + d + s for i in range(n): for j in range(i+1, n): if week.iloc[j] < week.iloc[i] + d + s: # 若 j 太近,则 st[i] 和 st[j] 不能同时为 1 m.Equation(st[i] + st[j] <= 1) # 【目标】最大化启动次数(或可替换为其他业务目标,如总销量) m.Maximize(m.sum(st)) m.solve(disp=True) # 后处理:生成最终 x7(执行周标记) x7_sol = np.zeros(n, dtype=int) for i in range(n): if st[i].VALUE[0] > 0.5: # 在 week[i] 启动 → 占用接下来 d 周(索引 i 到 i+d-1) for k in range(d): if i + k < n: x7_sol[i + k] = 1 # 输出结果 result_df = pd.DataFrame({ 'week': week, 'start_promo': [int(st[i].VALUE[0]) for i in range(n)], 'in_promo': x7_sol }) print("Optimal schedule (d=2, s=1):") print(result_df[result_df['in_promo'] == 1][['week', 'start_promo', 'in_promo']])
? 关键注意事项 : ✅ 不要直接对 x7 施加 d/s 约束 :因时间轴稀疏,x7[i] 与 x7[i+1] 不代表真实时间邻接; ✅ st 是唯一优化变量 ,x7 是确定性派生量,不参与求解,大幅降低问题复杂度; ⚠️ 若需 x7 参与其他约束(如资源上限),应改写为 st 的线性表达式(如 sum(x7) == sum_{i} sum_{k=0}^{d-1} st[i]·I[i+k 100),可预计算合法起始索引集 valid_starts 和冲突对 (i,j),用稀疏循环替代全矩阵遍历,提升性能。 该方法将离散组合约束转化为清晰的整数规划结构,在 Gekko 中稳定收敛,适用于任意非均匀时间序列的周期调度场景。

相关文章