子回路消除约束
什么是子回路
在不同的问题中子回路的定义略有不同,例如在TSP中子回路是不包含所有节点的闭环,而在VRP中则是指起点和终点都不是depot的环,如图(B)所示。尽管定义有所不同,但是他们消除子回路的方式是相同的。本文介绍两种最常见的子环路消除约束:MTZ和DFJ约束。
DFJ约束
DFJ约束的原理
DFJ约束来源于一个非常直观的认识,即对于任意一个子回路来说,一定满足该回路中所包含的弧的数量等于该回路中所包含的点的数量,其中
于是,想要破除子回路,我们只需要破坏上述条件就可以了,这样就得到了DFJ约束。
注:这里为什么有
,因为只有 只有一个点的话是绝对不可能形成子回路的,而当 时, 实际上就是 ,这个时候满足 的就是我们想要刻画的那个回路,当然不能删掉了。
DFJ约束的实现
我们知道任何一个集合的真子集个数为
子回路检测算法def findAllSubroutes(solution:ndarray)->Dict[int,List[List]]: route_dict = {} # subtours of each car for k in range(solution.shape[2]): subtour_kcars = [] # subtours of car k # check subtours of car k for i in range(1,solution.shape[1]-1): flag = False subtour_kcars_start_i = [] # subtours of car k that starts from point i for j in range(1,solution.shape[1]-1): # car k moved from point i to j if solution[i,j,k] > 0.9: subtour_kcars_start_i.append(i) subtour_kcars_start_i.append(j) current_point = j # cheack whether i->j formed a subtour while not flag: for next_point in range(1,solution.shape[1]): if solution[current_point,next_point,k] > 0.9: subtour_kcars_start_i.append(next_point) current_point = next_point # we find a subtour start from ponstallint i if subtour_kcars_start_i[0] == subtour_kcars_start_i[-1]: flag = True subtour_kcars.append(subtour_kcars_start_i) break # car k finally arrived point (solution.shape[2]-1) means i->j didn't form a subtour if current_point == solution.shape[1]-1: flag = True break else: continue route_dict[k] = subtour_kcars return route_dict
回调函数def generateDFJConstraint(model,where): # each time when gurobi find a feasible solution,we check whether we should add some DFJ constraints if where == GRB.Callback.MIPSOL: solution = zeros((len(_data.V),len(_data.V),len(_data.K))) # save the solution results in matrix format for key,value in x.items(): idx1,idx2,idx3 = key solution[idx1,idx2,idx3] = _model.cbGetSolution(value) route_dict = findAllSubroutes(solution) # 找到当前解对应的所有子回路 for k in _data.K: for subtour_kcars_start_i in route_dict[k]: # Now we get the set S(subtour_kcars_start_i[:-1]) _model.cbLazy( quicksum(x[i,j,k] for i,j in _data.A if (i in subtour_kcars_start_i[:-1] and j in subtour_kcars_start_i[:-1])) <= len(subtour_kcars_start_i[:-1])-1 )
MTZ约束
MTZ约束我觉得是一个更加巧妙的消除子回路的方法,并且实现上非常简单。它消除子回路的思想是:赋予每个节点一个变量
注:MTZ约束非常多非常多,关键看你怎么定义变量的含义。
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.