优化 | 使用凸包刻画约束满足问题中的alldifferent约束使模型更紧凑

科技   2024-12-20 20:03   德国  

TEXT


对于一个整数规划问题,通常在建模时我们希望模型更“紧凑”,因为“紧凑”的模型线性松弛问题的解空间较小更加易于求解。在一些整数规划问题中有一类被称为“all different”的约束,将这些约束用凸包来刻画能得到更加紧凑的模型Williams, H. P., & Yan, H. (2001). Representations of the all_different predicate of constraint satisfaction in integer programming. INFORMS Journal on Computing, 13(2), 96-103.
1. 几个可以用all different 约束来刻画的问题

(1)指派问题

将集合I中的元素唯一地与集合J中的元素进行匹配,传统的整数规划模型需要先定义一类0-1决策变量xij,然后有以下约束条件。

该整数规划模型有|I||J|个变量和|I|+|J|个约束条件

若用约束满足的思路来构建,则定义一类整数变量yi,表示元素i匹配到的元素j的值,然后再加一类all different 约束(表示yi的值各不相同

该模型的变量数为|I|个,约束数量为1CS约束(从后面论文描述情况来看数量上也为|I|+|J|个,不过变量数肯定是减少了)。

(2)党派参选问题(The Progressive Party Problem

定义0-1决策变量xijt表示人员j在时间t被分配到党派i时为1,否则为0,同时在任意时刻,对于每个人都只能选择1个党派。

该模型有|I||J||T|个变量,然后有|J|个约束。

若用约束满足的思路来构建,定义一个整数变量yjt表示人员j在时间t选择的党派,然后再加一类all different 约束

(3)课程排班问题(School Timetabling Problem

定义0-1决策变量xijk,表示教师i在时段t被分配到班级j时为1,否则为0,模型考虑的约束如下

该模型包含|I||J||K|个变量,然后有|J||K|+|I||K|+|I||J|个约束。

采用约束满足的思路来构建时定义整数变量yjk做如下替换

该模型包含|I||J||K|个变量,然后有|J||K|+|I||K|+|I||J|个约束。

2. 如何用凸包刻画all different 约束

考虑如下的all different约束

其中A表示整数备选集合

那么使用凸包刻画该约束的公式为(论文中有详细推导证明公式)

举一个考虑3个变量的简单例子,对于all_different(x1,x2,x3),A={0,1,2},那么六个可行点均在平面x1+x2+x3=3上

3. 基于指派问题进行简单测试

首先利用pyomo写一个经典指派问题的例子(数据集:Index of /~mastjjb/jeb/orlib/files), Pyomo 及求解器的安装可以参考开源建模框架+开源求解器 | 使用pyomo建模框架实现交通物流优化问题的求解  (shortest path problem)文章
from pyomo.environ import ConcreteModel, Set, Param, Var, Objective, minimize, Constraintfrom pyomo.opt import SolverFactoryimport pyomo.environ as pyoimport timedef read_assign_file(file_path):    with open(file_path, 'r') as file:        # 读取待分配物品数量n        n = int(file.readline().strip())        cost_matrix = []        current_row = []        count = 0        for line in file:            numbers = line.strip().split()            for num in numbers:                current_row.append(int(num))                count += 1                if count == n:                    cost_matrix.append(current_row)                    current_row = []                    count = 0
return n, cost_matrix
def build_assignment_model(n, cost_matrix): model = ConcreteModel()
# 定义物品集合 model.nums = Set(initialize=range(n))
# 定义决策变量x(i,j),表示物品i是否分配给物品j,是二进制变量 model.assign = Var(model.nums, model.nums, within=pyo.Binary)
# 定义成本参数,对应成本矩阵中的值 model.cost = Param(model.nums, model.nums, initialize=lambda model, i, j: cost_matrix[i][j])
# 目标函数,最小化总成本 def total_cost_rule(model): return sum(model.cost[i, j] * model.assign[i, j] for i in model.nums for j in model.nums) model.obj = Objective(rule=total_cost_rule, sense=minimize)
# 每个物品只能分配出去一次的约束 def assign_once_rule(model, i): return sum(model.assign[i, j] for j in model.nums) == 1 model.assign_once = Constraint(model.nums, rule=assign_once_rule)
# 每个物品最多只能接收一个物品的约束 def receive_once_rule(model, j): return sum(model.assign[i, j] for i in model.nums) <= 1 model.receive_once = Constraint(model.nums, rule=receive_once_rule)
return model
if __name__ == "__main__": file_path = "assign400.txt" n, cost_matrix = read_assign_file(file_path) model = build_assignment_model(n, cost_matrix)
# 使用SCIP求解器 solver = SolverFactory('SCIP') t1=time.time() result = solver.solve(model) t2= time.time() print(f"用时为{t2-t1}") if result.solver.termination_condition == 'optimal': print("找到最优解!") optimal_cost = model.obj() print("最优总成本:", optimal_cost)
# 输出分配方案(示例,你可以根据需求调整展示格式等) for i in range(n): for j in range(n): if model.assign[i, j].value == 1: print(f"物品{i}分配给物品{j}") else: print("未找到最优解,求解器终止状态:", result.solver.termination_condition)
和凸包建模进行对比,貌似确实快很多,不过凸包求解的结果正确性有待进一步检验(另外该建模方式会使目标函数定义变复杂)
凸包建模代码
from pyomo.environ import ConcreteModel, Set, Param, Var, Objective, minimize, Constraintfrom pyomo.opt import SolverFactoryimport pyomo.environ as pyoimport timedef read_assign_file(file_path):    with open(file_path, 'r') as file:        # 读取待分配物品数量n        n = int(file.readline().strip())        cost_matrix = []        current_row = []        count = 0        for line in file:            numbers = line.strip().split()            for num in numbers:                current_row.append(int(num))                count += 1                if count == n:                    cost_matrix.append(current_row)                    current_row = []                    count = 0
return n, cost_matrix
def build_assignment_model(n, cost_matrix): # 创建具体的模型实例 model = pyo.ConcreteModel("AssignmentProblem") I = list(i+1 for i in range(n)) # 定义决策变量 model.y = pyo.Var(I, within=pyo.PositiveIntegers)
# 约束条件1 def person_assigned_one_rule(model, h): return sum(model.y[t] for t in range(1, h + 1)) >= (h - 1) * h / 2
model.person_assigned_once = pyo.Constraint(I, rule=person_assigned_one_rule)
# 约束条件2 def person_assigned_two_rule(model, h): return sum(model.y[t] for t in range(1, h + 1)) <= h * (2 * len(I) - h + 1) / 2
model.person_assigned_two = pyo.Constraint(I, rule=person_assigned_two_rule)
# 目标函数:最小化总成本 def objective_rule(model): val = 0 for i in I: for j in I: if model.y[i].value == j: val += cost_matrix[i-1][j-1] return val
model.obj = pyo.Objective(rule=objective_rule, sense=pyo.minimize)
return model
if __name__ == "__main__": file_path = "assign500.txt" n, cost_matrix = read_assign_file(file_path) model = build_assignment_model(n, cost_matrix)
# 使用SCIP求解器 solver = SolverFactory('SCIP') t1=time.time() result = solver.solve(model) t2= time.time() print(f"用时为{t2-t1}") if result.solver.termination_condition == 'optimal': print("找到最优解!") optimal_cost = model.obj() print("最优总成本:", optimal_cost)


排版|小马

图片|小马

文字|小马








微信公众号后台回复

加群:加入全球华人OR|AI|DS社区硕博微信学术群

资料:免费获得大量运筹学相关学习资料

人才库:加入运筹精英人才库,获得独家职位推荐

电子书:免费获取平台小编独家创作的优化理论、运筹实践和数据科学电子书,持续更新中ing...

加入我们:加入「运筹OR帷幄」,参与内容创作平台运营

知识星球:加入「运筹OR帷幄」数据算法社区,免费参与每周「领读计划」、「行业inTalk」、「OR会客厅」等直播活动,与数百位签约大V进行在线交流



                    


        




文章须知

文章作者:小马过河啊

微信编辑:疑疑

文章转载自『小马过河啊』公众号,原文链接:使用凸包刻画约束满足问题中的alldifferent约束使模型更紧凑





关注我们 

       FOLLOW US



































运筹OR帷幄
致力于成为全球最大的运筹学中文线上社区
 最新文章