前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >论文拾萃|Solution-based tabu search求解Max-Minsum DP(附代码及详细注释)

论文拾萃|Solution-based tabu search求解Max-Minsum DP(附代码及详细注释)

作者头像
用户1621951
发布2021-03-16 11:20:32
6210
发布2021-03-16 11:20:32
举报
文章被收录于专栏:数据魔术师数据魔术师

Part 1 Max-Minsum Dispersion Problem

关于Max-Minsum Dispersion Problem的介绍详见之前推文模拟退火(SA)算法求解Max-Minsum Dispersion Problem(附代码及详细注释)

Part 2 禁忌搜索算法(TS)再回顾

2.1 TS算法介绍

禁忌搜索算法(Tabu Search,TS)是由美国科罗拉多大学的Fred Glover教授于1986年提出的可用于有效解决组合优化问题的一种智能优化算法。

禁忌搜索是Local Search(LS)的扩展,是一种全局逐步寻优的全局性邻域搜索算法,其核心思想是:通过禁忌策略实现记忆功能,通过破禁准则继承LS的强局部搜索能力。种种机制的配合,使得TS一方面具备高局部搜索能力,同时又能防止算法在优化寻优过程中陷入局部最优

禁忌搜索的主要构成要素是:

  • 评价函数(Evaluation Function)
  • 邻域移动(Move Operator)
  • 禁忌表(Tabu Table)
  • 邻居选择策略(Neighbor Selection Strategy)
  • 破禁准则(Aspiration Criterion)
  • 停止准则(Stop Criterion)

对于禁忌搜索各部分内容,公众号之前的推文中已经介绍过多次了。还不了解的同学可以先移步干货 | 到底是什么算法,能让人们如此绝望?了解算法细节。

Part 3 具体算法介绍

Attibute-based tabu search是一种流行的元启发式方法(meta-heuristic),被广泛用于解决NP-hard组合问题,这种方法在禁忌搜索的过程中选择禁忌解的某个特征,即属性。相对来说,研究较少的Solution-based tabu search是则记录访问过的整个解。相比之下,记录解比记录某个属性更“精确”,不容易将未访问过的解错误地禁忌。

然而,一般的禁忌解会导致算法的运行时间大幅增加,因为判定两个解是否完全相同通常需要很长时间。利用哈希表将数字对应到解上是一种减少时间消耗的有效方法。哈希表通过哈希函数将数字映射到解上,在对比两个解是否相同时只需要对比哈希值即可。本文的算法就利用了哈希表进行对solution的禁忌。

本文中,小编参考文末所列文献中的方法,利用Solution-based tabu search来求解Max-Minsum DP,该算法依靠hash vectors来有效地确定候选解的禁忌状态,并通过一个受约束的交换邻域来保证算法的高计算效率。

Solution-based tabu search的伪代码如下:

其中M*表示至今发现的最优解,

H_k(k=1,2,3)

即hash vectors,也就是本问题的禁忌表,

h_k(k=1,2,3)

为相对应的hash functions。

如图所示,首先初始化hash vectors(第2-6行)并生成初始解M(第7行)。然后,算法多次迭代以改进初始解(第9-18行)。在每次迭代中,该算法首先从当前解的邻域

N_{swap}^{c}(M)

确定最佳非禁忌邻居解M',然后使用M'取代当前解M。随后,根据新解M更新hash vectors(即禁忌表)(第15-17行)。重复while循环,直到达到时间限制(

t_{max}

)。最后,将搜索过程中找到的最优解M∗作为最终结果返回。

友情提示:以下3.1-3.3部分请先参见之前推文模拟退火(SA)算法求解Max-Minsum Dispersion Problem(附代码及详细注释)以免跨度过大,晦涩难懂

3.1 初始解生成

每次迭代的初始解,我们亦是随机产生,随机选择m个元素构成M,未被选中的即为N\M。利用

x=(x_1,x_2,……,x_n)

中代表解,如果

i∈M

,则

x_i

=1,否则

x_i

=0。故初始解InitiaSol由随机挑选集合N中m个元素产生。 (专业代码及算例可以从http://www.info.univ-angers.fr/~hao/MaxMinsumdp.html下载)

3.2 邻域动作

采用exchange算子:从被选择的元素的集合中随机选择元素u,即u∈M,从不被选择的元素的集合中随机选择元素v,即v∈N\M,交换u, v。

将一次邻域动作定义为<u,v>,则对当前解对应M进行一次邻域动作得到的邻居解为M⊕<u,v>,

N_{swap}^{full}(M)

即为一次操作所有可能的邻居解,可以表示为

N_{swap}^{full}(M)

={M⊕<u,v>:u∈M,v∈N\M}。

3.2.1 参数约束的重要性

就本问题而言,邻域

N_{swap}^{full}(M)

大小正好等于m×(n−m),对于相对较大的n和m(比方说几百个元素),搜索的邻域会变得非常大,使得算法运行速度变慢。我们通过参数约束交换邻域

N_{swap}^{c}(M)

来减小计算量。所谓“参数约束”就是指每次搜索时仅搜索由参数大小决定的邻域的子集,而不是整个邻域。其中,

N_{swap}^{c}(M)

可以表示为

N_{swap}^{c}(M)

={M⊕<u,v>:u∈X⊂M,v∈Y⊂N\M}。

3.2.2 参数约束交换邻域

为了加快搜索过程,我们将需要被交换的元素被限制为两个高质量子集X和Y,为了识别也可说是得到这两个高质量子集,我们利用n维向量

∆= (∆1 , ∆2 , . . . , ∆n )

,其中

∆i=∑_{j∈M}d_{ij}

,则

f_{(M)}=Min_{i∈M} ∆i

,对于集合M,根据其∆值快速排序得到降序排列的集合M的元素,前ρ*|M|个元素作为集合X,对于集合N\M,根据其∆值快速排序得到升序排列的集合N\M的元素,前ρ*|N\M|个元素作为集合Y,其中ρ为预定的参数,构造约束邻域的总计算复杂度为O(mlogm+(n−m)log(n−m)),相对较低。参数约束交换邻域的大小等于O(

ρ^2

×m(n−m)),当ρ小于0.5时复杂度大幅度减小。

3.3 去重优化

对于本问题,给定邻域解和对应向量

(∆1 , ∆2 , . . . , ∆n )

,目标值

f_{(M)}

可以在O(M)时间内计算,此外,若是两个元素u∈M,v∈N\M交换,则向量

∆= (∆1 , ∆2 , . . . , ∆n)

可以在O(N)时间内快速更新,具体可表示为下图:

值得一提的是,本问题解法中,我们已提前计算每个未选中元素到所有被选中元素的距离和,更新的新元素v对应Δ值可以直接以之减去被替换元素u与新元素v的距离,十分方便。

3.4 哈希函数

3.4.1 确定禁忌状态

SBTS算法依赖于三个hash functions及其关联的hash vectors来有效地确定候选解的禁忌状态,对于给定的hash functions即h和大小为L的hash vectors即H,h可以用来映射候选解,使得

H[h(x)]

二进制值标识解x的禁忌状态:

H[h(x)]=1

表示解x以前被访问过并且被标记为禁忌状态(因此,除非满足破禁标准,否则x在当前迭代中被排除在考虑范围之外),而

H[h(x)]=0

表示x没有被访问过,因此有资格在当前迭代中被考虑。此外,通过使用三个hash functions和数量颇多的hash vectors,我们可以显著降低误分类候选解的禁忌状态的概率

3.4.2 函数定义
γ_k

是对三个hash function采用不同值的参数,

L

是hash vector的长度, 被设置为

10^8

x=(x_1,x_2,……,x_n)

作为候选解,如果

i∈M

,则

x_i

=1,否则为0。三个hash function定义如下:

3.4.3 初始化与更新

hash vectors

H_k(k=1,2,3)

如下进行初始化更新

在搜索开始时,hash vectors

H_k(k=1,2,3)

被初始化为0,这意味着禁忌列表不禁忌任何解。

然后,随着搜索的进行,hash vectors

H_k(k=1,2,3)

被动态更新:一旦当前解被其邻域解之一替换,则

H_k[h_k(x)]←1(k=1,2,3)

最后,有一个关键问题是如何使用这些hash vectors和相关的hash functions来确定候选解的禁忌状态。在我们的算法的每次迭代中,对于邻域中的每个解x,我们首先检查

H_k[h_k(x)](k=1,2,3)

的值。如果它们都取值为1,则认为解x以前被访问过,因此禁止再次考虑。否则,解x被确定为非禁忌解。作为说明性示例,下图出示禁忌列表禁止的先前访问的解x(因为

H_1[h_1(x)]

H_2[h_2(x)]

H_3[h_3(x)]

都取值为1)。另一方面,任何至少有一个

H_k(k=1,2,3)

值等于0的解都是当前迭代的合格解,意即可以被访问。

3.4.4 计算复杂度

不难看出,solution-based tabu search的关键就在于同时使用三个hash function,函数本身的简单设计使得其可以快速更新,同时多次使用强化效果,显著降低误分类候选解的概率。对于当前解x的邻居解x⊕<u,v>,其中u∈M和v∈N\M,

h_k

(x⊕<u,v>)的值可以在O(1)中计算得到,即

h_k(x)+v^{γ_k}−u^{γ_k}

,这意味着给定解的邻居解的禁忌状态可以在O(1)时间内确定。然而,对于初始解

x^0

,我们需要从头开始计算相应的hash values

h_k(x^0)(k=1,2,3)

,复杂度为O(N)。

Part 4 核心代码分享

在这里小编为大家搜集了,文献中常用的6组140个基准实例(分别为APOM set,SOM-b set,GDK-c set,DM1a set,DM1c set,DM2 set)用于测试代码,并简单修改了论文作者提供的代码。参考论文、算例和代码的下载请移步留言区。

算例MDP_Andrade_Instances\andrade_mdptabu search\01a050m10.dat的部分内容如图:

经小编修改并注释的核心代码如下:

代码语言:javascript
复制
void Swap_Tabu_Search(int Sol[], int NSol[], double P[], double* F)
{
 //register表示使用CPU内部寄存器,可以加快变量读写速度
 register int swap, NM;
 register double p, f_c, p_min;
 register double tabu_best_fc, best_fc; //禁忌状态的最优解及当前最优解
 int non_improve = 0; // the stop conditon of TS
 int best_x[50] = { 0 }, best_y[50] = { 0 }, x = 0, y = 0;
 int tabu_best_x[50] = { 0 }, tabu_best_y[50] = { 0 };

 NM = N - M;
 int Hx1 = 0, Hx2 = 0, Hx3 = 0;
 int Hx11 = 0, Hx22 = 0, Hx33 = 0;
 double current_time, starting_time;
 //公式hash funcions
 for (int i = 0;i < M;i++)
 {
  Hx1 += W1[Sol[i]];
  Hx2 += W2[Sol[i]];
  Hx3 += W3[Sol[i]];
 }

 Build_Potential(Sol, NSol, P);
 //拷贝,将当前解作为最优解
 f_best = f;
 for (int i = 0;i < M;i++) {
  SS_BEST.S[i] = Sol[i];
  //cout << SS_BEST.S[i] << " ";
 }
 //cout << endl;
 for (int i = 0;i < NM;i++) {
  SS_BEST.NS[i] = NSol[i];
  //cout << SS_BEST.NS[i] << " ";
 }
 //cout << endl;
 for (int i = 0;i < N;i++) {
  SS_BEST.P[i] = P[i];
  //cout << SS_BEST.P[i] << " ";
 }
 //cout << endl;

 //记录时间
 starting_time = clock();
 int iter = 0;
 current_time = (double)(1.0 * (clock() - starting_time) / CLOCKS_PER_SEC);

 //the number of tabu neighbors and non-tabu neighbors
 int num_tabu_best = 0, num_best = 0;

 while (current_time < time_limit)
 {
  tabu_best_fc = -9999999.0;
  best_fc = -9999999.0;
  num_tabu_best = 0;
  num_best = 0;
  //排序以得高质量子集X、Y,否则全搞m*(n-m)太大了
  for (int i = 0;i < M;i++)
   dataM[i] = P[Sol[i]];
  Quick_Sort(dataM, M, Sol);

  for (int i = 0;i < NM;i++)
   dataNM[i] = P[NSol[i]];
  Quick_Sort(dataNM, NM, NSol);

  //a) Evaluating the neighborhood
  for (int x = 0.3 * M;x >= 0;x--)
  {
   for (int y = 0.7 * NM;y < NM;y++)
   {
    p_min = 9999999.0;

    //其他P[Sol[i]]需将与Sol[x]的距离转换为与Sol[y]的距离
    for (int i = 0;i < x;i++)
    {
     p = P[Sol[i]] - D[Sol[i]][Sol[x]] + D[Sol[i]][NSol[y]];
     p_min = p < p_min ? p : p_min;
    }
    //自身P[Sol[i]]的改变
    p = P[NSol[y]] - D[NSol[y]][Sol[x]];
    p_min = p < p_min ? p : p_min;
    //其他P[Sol[i]]需将与Sol[x]的距离转换为与Sol[y]的距离
    for (int i = x + 1;i < M;i++)
    {
     p = P[Sol[i]] - D[Sol[i]][Sol[x]] + D[Sol[i]][NSol[y]];
     p_min = p < p_min ? p : p_min;
    }

    f_c = p_min;

    Hx11 = Hx1 + (W1[NSol[y]] - W1[Sol[x]]);
    Hx22 = Hx2 + (W2[NSol[y]] - W2[Sol[x]]);
    Hx33 = Hx3 + (W3[NSol[y]] - W3[Sol[x]]);

    //禁忌状态
    if (H1[Hx11 % L] == 1 && H2[Hx22 % L] == 1 && H3[Hx33 % L] == 1)
    {
     if (f_c > tabu_best_fc) //为打破禁忌做准备
     {
      tabu_best_x[0] = x;
      tabu_best_y[0] = y;
      tabu_best_fc = f_c;
      num_tabu_best = 1;
     }
     else if (f_c == tabu_best_fc && num_tabu_best < 50)
     {
      tabu_best_x[num_tabu_best] = x;
      tabu_best_y[num_tabu_best] = y;
      num_tabu_best++;
     }
    }
    else
    {
     if (f_c > best_fc)
     {
      best_x[0] = x;
      best_y[0] = y;
      best_fc = f_c;
      num_best = 1;
     }
     else if (f_c == best_fc && num_best < 50)
     {
      best_x[num_best] = x;
      best_y[num_best] = y;
      num_best++;
     }
    }
   }
  }
  //b. Moves
  if ((num_tabu_best > 0 && tabu_best_fc > best_fc && tabu_best_fc > f_best + 1.0e-7) || num_best == 0) // aspiration criterion
  {
   //无路可走,打破禁忌
   f = tabu_best_fc;
   int select = rand() % num_tabu_best;
   int u = tabu_best_x[select];
   int v = tabu_best_y[select];
   //u,v交换
   Swap_Update_Potential(P, Sol[u], NSol[v]);

   Hx1 = Hx1 + W1[NSol[v]] - W1[Sol[u]];
   Hx2 = Hx2 + W2[NSol[v]] - W2[Sol[u]];
   Hx3 = Hx3 + W3[NSol[v]] - W3[Sol[u]];

   H1[Hx1 % L] = 1;
   H2[Hx2 % L] = 1;
   H3[Hx3 % L] = 1;

   swap = Sol[u];
   Sol[u] = NSol[v];
   NSol[v] = swap;
  }
  // non tabu
  else
  {
   f = best_fc;
   int select = rand() % num_best;
   int u = best_x[select];
   int v = best_y[select];
   Swap_Update_Potential(P, Sol[u], NSol[v]);

   Hx1 = Hx1 + W1[NSol[v]] - W1[Sol[u]];
   Hx2 = Hx2 + W2[NSol[v]] - W2[Sol[u]];
   Hx3 = Hx3 + W3[NSol[v]] - W3[Sol[u]];
   H1[Hx1 % L] = 1;
   H2[Hx2 % L] = 1;
   H3[Hx3 % L] = 1;

   swap = Sol[u];
   Sol[u] = NSol[v];
   NSol[v] = swap;
  }
  //c. print
  iter++;
  current_time = (double)(1.0 * (clock() - starting_time) / CLOCKS_PER_SEC);
  if (f >= f_best + 1.0e-7) //浮点数无法精确表达
  {
   if (f > f_best + 1.0e-7)
   {
    f_best = f;
    for (int i = 0;i < M;i++)
     SS_BEST.S[i] = Sol[i];
    for (int i = 0;i < NM;i++)
     SS_BEST.NS[i] = NSol[i];
    for (int i = 0;i < N;i++)
     SS_BEST.P[i] = P[i];
    SS_BEST.f = f;
    non_improve = 0;
    time_to_target = current_time;
   }
   else if (f == f_best)
    non_improve++;
  }
  else non_improve++;
 }
 for (int i = 0;i < M;i++)  Sol[i] = SS_BEST.S[i];
 for (int i = 0;i < NM;i++) NSol[i] = SS_BEST.NS[i];
 for (int i = 0;i < N;i++)  P[i] = SS_BEST.P[i];
 *F = f_best;
}

结果如图:


欲下载本文相关代码,请移步留言区

参考论文

Xiangjing Lai,Dong Yue,Jin-Kao Hao,Fred Glover "Solution-based tabu search for the maximum min-sum dispersion problem." Information Sciences 441 (2018) 79-94.

论文第一作者简介

Xiangjing Lai(赖向京),南京邮电大学研究员,硕士生导师,2012年6月于华中科技大学计算机学院获博士学位,师从黄文奇教授;2013年10月至2015年10月于法国昂热大学(University of Angers, France)从事博士后研究工作,师从法国大学研究院院士、法国国家特级教授 Jin-Kao Hao 博士;自2014年起,与冯诺依曼理论奖获得者、美国工程院院士、禁忌搜索算法提出者 Fred Glover 博士进行合作研究;2018年入选江苏省“六大人才高峰”高层次人才。

文案&排版:朱正雄(华中科技大学管理学院本科一年级)

指导老师:秦虎老师(华中科技大学管理学院)

审稿:周航(华中科技大学管理学院本科二年级)

如对文中内容有疑问,欢迎交流。PS:部分资料来自网络。


如有需求,可以联系:

秦虎老师(华中科技大学管理学院:professor.qin@qq.com)

朱正雄(华中科技大学管理学院本科一年级:2627889552@qq.com)

周航(华中科技大学管理学院本科二年级:zh20010728@126.com)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据魔术师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Part 1 Max-Minsum Dispersion Problem
  • Part 2 禁忌搜索算法(TS)再回顾
    • 2.1 TS算法介绍
    • Part 3 具体算法介绍
      • 3.1 初始解生成
        • 3.2 邻域动作
          • 3.2.1 参数约束的重要性
          • 3.2.2 参数约束交换邻域
        • 3.3 去重优化
          • 3.4 哈希函数
            • 3.4.1 确定禁忌状态
            • 3.4.2 函数定义
            • 3.4.3 初始化与更新
            • 3.4.4 计算复杂度
        • Part 4 核心代码分享
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档