
本文介绍如何在 Gekko 中对非连续时间索引(如存在周数空缺的 week = [13,14,17,18,...])建模同时满足最小持续时长(duration) 和最小间隔(spacing) 的整数规划约束,通过引入“启动变量”并后处理生成实际激活序列,避免因时间不连续导致的索引错位问题。
本文介绍如何在 gekko 中对非连续时间索引(如存在周数空缺的 `week = [13,14,17,18,...]`)建模同时满足**最小持续时长(duration)** 和**最小间隔(spacing)** 的整数规划约束,通过引入“启动变量”并后处理生成实际激活序列,避免因时间不连续导致的索引错位问题。
在实际优化场景中(如促销排期、设备启停调度),常需满足两类硬性约束:
- Duration(d):每次启用必须持续至少 d 个连续时间单位;
- Spacing(s):任意两次启用的起始时间之间至少间隔 s 个时间单位(非结束到开始)。
当时间轴本身存在空缺(例如 week = [13,14,17,18,19,26,...]),直接使用 x[i] + x[j]
✅ 推荐解法:分离“启动决策”与“执行状态”
不直接对最终激活变量 x7[i](是否在第 i 个时间点启用)建模,而是引入二元变量 st[i] 表示“是否在 week[i] 这一周启动一次新活动”。随后通过逻辑规则推导出完整激活序列,并施加基于 st 的紧凑约束。
? 核心建模步骤
-
定义启动变量(Start Variable)
st = m.Array(m.Var, n, integer=True, lb=0, ub=1)
st[i] == 1 表示在 week[i] 启动一个长度为 d 的活动。
确保启动可行(Duration Feasibility)
若在 week[i] 启动,则必须保证后续 d−1 个时间点均存在于 week 中(即 i+d−1for i in range(n - d + 1, n): m.Equation(st[i] == 0) # 无法容纳完整周期,禁止启动-
编码 Spacing 约束(关键!)
要求任意两个启动时间至少相隔 s 周 → 即若 st[i] == 1,则对所有 j 满足 week[j] 时间跨度内,最多允许一个启动事件(因启动后占用 d 周,且下一次启动最早在 +s 周后,故窗口大小为 d + s):for i in range(n - d - s + 1): # 窗口左边界 # 找出 week[i] 到 week[i] + d + s - 1 范围内的所有索引 window_end_time = week[i] + d + s - 1 j_list = [j for j in range(i, n) if week[j] <= window_end_time] if len(j_list) > 0: m.Equation(sum([st[j] for j in j_list]) <= 1)? 注意:此处 d + s 是时间跨度(单位:周),不是数组索引长度。必须显式按 week 值筛选索引,而非简单切片 st[i:i+d+s](后者仅适用于等距时间)。
-
推导最终激活变量(Post-processing)
求解完成后,用 Python 构造实际激活序列 x7_final:x7_final = np.zeros(n, dtype=int) for i in range(n - d + 1): if st[i].VALUE[0] > 0.5: # 启动点 x7_final[i:i+d] = 1 # 连续 d 周激活 集成至原模型目标与约束
将 x7_final 替换原代码中的 x7 参与目标函数(如 m.Maximize(m.sum(... * x7_final)))及其它逻辑约束(如 sum(x7_final) 非优化变量,仅用于结果解释和目标计算;所有优化逻辑均由 st 驱动。
⚠️ 注意事项与最佳实践
- 勿混淆时间值与索引:week[i] 是日历周数(如 17),i 是数组下标(如 2)。所有约束必须基于 week[i] 值运算。
- Spacing 是启动点间距:约束 week[j] - week[i] >= s 当 st[i]=st[j]=1 且 i≠j,而非 x7 变量间的距离。
- Gekko 不支持动态索引表达式:不能写 m.Equation(st[week.index(week[i]+s)] == 0);必须预计算所有合法 (i,j) 对或使用上述滑动时间窗方法。
- 性能提示:若 n 较大(>100),可预先构建稀疏启动冲突矩阵,用 m.sum([st[i] for i in conflict_set[j]])
- 验证输出:务必检查解出的 st 对应的 week 值是否满足 week[j] - week[i] >= s,并确认每个 st[i]=1 后 d 个 week 均存在(即 i+d-1
✅ 示例输出(s=1, d=2, week=[13,14,17,18,19,26,...])
| week | st (start) | x7_final (active) |
|---|---|---|
| 13 | 1 | 1 |
| 14 | 0 | 1 |
| 17 | 1 | 1 |
| 18 | 0 | 1 |
| 19 | 0 | 0 |
| 26 | 0 | 0 |
| ... | ... | ... |
结果严格满足:每段激活长度为 2(如 [13,14], [17,18]),且启动周 13→17 间隔 4 ≥ s=1,符合要求。
该方法将复杂的时间逻辑解耦为清晰的“启动决策”+“确定性展开”,兼具数学严谨性与工程可实现性,是处理非均匀时间网格下 duration & spacing 联合约束的推荐范式。

















