喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号【程序猿声】

00 目录

  • 局部搜索再次科普
  • 变邻域搜索
  • 造轮子写代码

01 局部搜索科普三连

虽然之前做的很多篇启发式的算法都有跟大家提过局部搜索这个概念,为了加深大家的印象,在变邻域主角登场之前还是给大家科普一下相关概念。热热身嘛~

1.1 局部搜索是什么玩意儿?

官方一点:局部搜索是解决最优化问题的一种启发式算法。对于某些计算起来非常复杂的最优化问题,比如各种NP完全问题,要找到最优解需要的时间随问题规模呈指数增长,因此诞生了各种启发式算法来退而求其次寻找次优解,是一种近似算法(Approximate algorithms),以时间换精度的思想。局部搜索就是其中的一种方法。

通俗一点:局部搜索算法是对一类算法的统称,符合其框架的算法很多,比如之前公众号推文中介绍的爬山法、模拟退火法和禁忌搜索算法都属于局部搜索算法。尽管各个算法在优化过程中的细节存在差异,但在优化流程上呈现出很大的共性。

它的基本原理是在临近解中迭代,使目标函数逐步优化,直至不能再优化为止。

1.2 局部搜索的过程

我们可以将局部搜索算法的统一框架描述为:

  1. 算法从一个或若干个初始解出发。
  2. 在算法参数控制下由当前状态的邻域中产生若干个候选解。
  3. 以某种策略在候选解中确定新的当前解。
  4. 伴随控制参数的调节,重复执行上述搜索过程,直至满足算法终止准则。
  5. 结束搜索过程并输出优化结果。

1.3 局部搜索的几大要素

局部搜索算法主要包含五大要素:

  1. 目标函数:用来判断解的优劣。
  2. 邻域的定义:根据不同问题,有着不同的邻域定义。
  3. 初始解的产生规则
  4. 新解的产生和接受规则
  5. 算法终止准则

其中前两个要素的定义和算法要解决的特定问题有关,而且不同的人对同一问题可能有完全不同的定义。后三个要素定义的不同则会产生各种不同的局部搜索算法,而它们的效率和最终解的质量也会有很大的差异。

02 变邻域搜索算法

2.1 什么是VNS?

对上面的局部搜索有一定的印象以后,理解变邻域搜索也不难了。其实说白了,变邻域搜索算法(VNS)就是一种改进型的局部搜索算法。它利用不同的动作构成的邻域结构进行交替搜索,在集中性和疏散性之间达到很好的平衡。其思想可以概括为“变则通”。

变邻域搜索依赖于以下事实:

  • 一个邻域结构的局部最优解不一定是另一个邻域结构的局部最优解。
  • 全局最优解是所有可能邻域的局部最优解。

它由主要由以下两个部分组成:

  • variable neighborhood descent (VND)
  • shaking procedure

大家别急,下面我们将会对这两个部分进行分析。然后,before that……

2.2 你们一定想知道邻域是什么?

官方一点:所谓邻域,简单的说即是给定点附近其他点的集合。在距离空间中,邻域一般被定义为以给定点为圆心的一个圆;而在组合优化问题中,邻域一般定义为由给定转化规则对给定的问题域上每结点进行转化所得到的问题域上结点的集合。

通俗一点:邻域就是指对当前解进行一个操作(这个操作可以称之为邻域动作)可以得到的所有解的集合。那么邻域的本质区别就在于邻域动作的不同了。

2.3 还是要说说邻域动作

邻域动作是一个函数,通过这个函数,对当前解s,产生其相应的邻居解集合。例如:对于一个bool型问题,其当前解为:s = 1001,当将邻域动作定义为翻转其中一个bit时,得到的邻居解的集合N(s)={0001,1101,1011,1000},其中N(s) ∈ S。同理,当将邻域动作定义为互换相邻bit时,得到的邻居解的集合N(s)={0101,1001,1010}。

2.4 variable neighborhood descent (VND)

VND其实就是一个算法框架,它的过程描述如下:

  1. 初始解S,定义M个邻域,记为Nk(k = 1, 2, 3……m),i = 1。
  2. 使用邻域结构Ni进行搜索,直到陷入局部最优解S′ 。
  3. 如果S′ 优于S,令S=S′,i=1; 否则,i++。
  4. 如果i≤m ,转步骤2。
  5. 输出最优解S。

我知道没图你们是不会点进来的……

VND的图解如下:

只说两点,再问自杀:

  • 当在本邻域搜索找不出一个比当前解更优的解的时候,我们就跳到下一个邻域继续进行搜索。如图中虚黑线所示。
  • 当在本邻域搜索找到了一个比当前解更优的解的时候,我们就跳回第一个邻域重新开始搜索。如图中红线所示。

之前我们把局部搜索比喻作爬山的过程,那么每变换一次邻域,也可以理解为切换了搜索的地形(landscape)。效果如下 :

每一次跳跃,得到都是一个新的世界……

伪代码描述如下:

2.5 shaking procedure

其实呀,这玩意儿。说白了就是一个扰动算子,类似于邻域动作的这么一个东西。通过这个算子,可以产生不同的邻居解。虽然名词很多看起来很高大上,扰动、抖动、邻域动作这几个本质上还是没有什么区别的。都是通过一定的规则,将一个解变换到另一个解而已。这里读者还是抓其本质,不要被表象所迷惑了就好。

2.6 VNS过程

在综合了前面这么多的知识以后,VNS的过程其实非常简单。可以描述为以下几步:

  1. 产生初始解s1。
  2. shaking s1,得到解s2。
  3. 对解s2进行VND,得到解s3。
  4. 如果达到边界条件,结束程序,输出最优解。否则跳回第二步。

结合伪代码,一目了然:

03 变邻域搜索解决TSP问题

本次代码还是基于求解TSP旅行商问题的。至于什么是TSP问题,小编这实在是不想科普啦……

代码是基于迭代搜索那个代码魔改过来的。其实看了这么多启发式算法解TSP问题的代码。想必各位都有了一个比较清晰的认识,其实呀。之前介绍的模拟退火、遗传算法、迭代搜索和现在的变邻域等等,是十分相似滴。最大的不同在于算法框架的不同而已,像什么扰动啦,邻域动作啦。代码基本是不变的。所以大家可以多联想,多思考,学习就是一个探求事物本质的过程嘛!

简要说说算法vnd里面两个邻域使用的算子:

  • two_opt_swap
    没啥好说的,区间反转。直接上图:

  • two_h_opt_swap
    还是要说一点,随机产生两点,塞进新排列头部。其余的按顺序往后逐个塞进去。嗯,来看图片~

看代码吧。

 
 ////////////////////////
 //TSP问题 变邻域搜索求解代码
 //基于Berlin52例子求解
 //作者:infinitor
 //时间:2018-04-12
 ////////////////////////

 #include <iostream>
 #include <cmath>
 #include <stdlib.h>
 #include <time.h>
 #include <vector>
 #include <windows.h>
 #include <memory.h>
 #include <string.h>
 #include <iomanip>
 #include <algorithm>
 #define DEBUG

 using namespace std;

 #define CITY_SIZE 52 //城市数量

 //城市坐标
 typedef struct candidate
 {
     int x;
     int y;
 }city, CITIES;

 //解决方案
 typedef struct Solution
 {
     int permutation[CITY_SIZE]; //城市排列
     int cost;                         //该排列对应的总路线长度
 }SOLUTION;

 //城市排列
 int permutation[CITY_SIZE];
 //城市坐标数组
 CITIES cities[CITY_SIZE];

 //berlin52城市坐标,最优解7542好像
 CITIES berlin52[CITY_SIZE] =
 {
 { , },{ , },{ , },{ , },{ , },
 { , },{ , },{ , },{ , },{ , },{ , },
 { , },{ , },{ , },{ , },{ , },{ , },
 { , },{ , },{ , },{ , },{ , },{ , },
 { , },{ , },{ , },{ , },{ , },{ , },
 { , },{ , },{ , },{ , },{ , },{ , },
 { , },{ , },{ , },{ , },{ , },{ , },
 { , },{ , },{ , },{ , },{ , },{ , },
 { , },{ , },{ , },{ , },{ , }
 };
 //优化值
  };

 //计算两个城市间距离
 int distance_2city(city c1, city c2)
 {
     ;
     distance = sqrt((double)((c1.x - c2.x)*(c1.x - c2.x) + (c1.y - c2.y)*(c1.y - c2.y)));

     return distance;
 }

 //根据产生的城市序列,计算旅游总距离
 //所谓城市序列,就是城市先后访问的顺序,比如可以先访问ABC,也可以先访问BAC等等
 //访问顺序不同,那么总路线长度也是不同的
 //p_perm 城市序列参数
 int cost_total(int * cities_permutation, CITIES * cities)
 {
     ;
     int c1, c2;
     //逛一圈,看看最后的总距离是多少
     ; i < CITY_SIZE; i++)
     {
         c1 = cities_permutation[i];
         ) //最后一个城市和第一个城市计算距离
         {
             c2 = cities_permutation[];
         }
         else
         {
             c2 = cities_permutation[i + ];
         }
         total_distance += distance_2city(cities[c1], cities[c2]);
     }

     return total_distance;
 }

 //获取随机城市排列
 void random_permutation(int * cities_permutation)
 {
     int i, r, temp;
     ; i < CITY_SIZE; i++)
     {
         cities_permutation[i] = i; //初始化城市排列,初始按顺序排
     }

     random_shuffle(cities_permutation, cities_permutation + CITY_SIZE); //随机化排序 

 }
 //对应two_opt_swap的去重
 int calc_delta1(int i, int k, int *tmp, CITIES * cities) {
     ;
     /*
     以下计算说明:
     对于每个方案,翻转以后没必要再次重新计算总距离
     只需要在翻转的头尾做个小小处理

     比如:
     有城市序列   1-2-3-4-5 总距离 = d12 + d23 + d34 + d45 + d51 = A
     翻转后的序列 1-4-3-2-5 总距离 = d14 + d43 + d32 + d25 + d51 = B
     由于 dij 与 dji是一样的,所以B也可以表示成 B = A - d12 - d45 + d14 + d25
     下面的优化就是基于这种原理
     */
     )
     {
         )
         {
             delta = ;
         }
         else
         {
             delta =
                 - distance_2city(cities[tmp[k]], cities[tmp[k + ]])
                 + distance_2city(cities[tmp[i]], cities[tmp[k + ]])
                 - distance_2city(cities[tmp[CITY_SIZE - ]], cities[tmp[i]])
                 + distance_2city(cities[tmp[CITY_SIZE - ]], cities[tmp[k]]);
         }

     }
     else
     {
         )
         {
             delta =
                 - distance_2city(cities[tmp[i - ]], cities[tmp[i]])
                 + distance_2city(cities[tmp[i - ]], cities[tmp[k]])
                 - distance_2city(cities[tmp[]], cities[tmp[k]])
                 + distance_2city(cities[tmp[i]], cities[tmp[]]);
         }
         else
         {
             delta =
                 - distance_2city(cities[tmp[i - ]], cities[tmp[i]])
                 + distance_2city(cities[tmp[i - ]], cities[tmp[k]])
                 - distance_2city(cities[tmp[k]], cities[tmp[k + ]])
                 + distance_2city(cities[tmp[i]], cities[tmp[k + ]]);
         }
     }

     return delta;
 }

 /*
 去重处理,对于Delta数组来说,对于城市序列1-2-3-4-5-6-7-8-9-10,如果对3-5应用了邻域操作2-opt , 事实上对于
 7-10之间的翻转是不需要重复计算的。 所以用Delta提前预处理一下。

 当然由于这里的计算本身是O(1) 的,事实上并没有带来时间复杂度的减少(更新操作反而增加了复杂度)
 如果delta计算 是O(n)的,这种去重操作效果是明显的。
 */
 //对应two_opt_swap的去重更新
 void Update1(int i, int k, int *tmp, CITIES * cities, int Delta[CITY_SIZE][CITY_SIZE]) {
     ) {
         i--; k++;
         for (int j = i; j <= k; j++) {
             ; l < CITY_SIZE; l++) {
                 Delta[j][l] = calc_delta1(j, l, tmp, cities);
             }
         }

         ; j < k; j++) {
             for (int l = i; l <= k; l++) {
                 if (j >= l) continue;
                 Delta[j][l] = calc_delta1(j, l, tmp, cities);
             }
         }
     }// 如果不是边界,更新(i-1, k + 1)之间的
     else {
         ; i < CITY_SIZE - ; i++)
         {
             ; k < CITY_SIZE; k++)
             {
                 Delta[i][k] = calc_delta1(i, k, tmp, cities);
             }
         }
     }// 边界要特殊更新 

 }

 // two_opt_swap算子
 void two_opt_swap(int *cities_permutation, int b, int c)
 {
     vector<int> v;
     ; i < b; i++)
     {
         v.push_back(cities_permutation[i]);
     }
     for (int i = c; i >= b; i--)
     {
         v.push_back(cities_permutation[i]);
     }
     ; i < CITY_SIZE; i++)
     {
         v.push_back(cities_permutation[i]);
     }

     ; i < CITY_SIZE; i++)
     {
         cities_permutation[i] = v[i];
     }

 }

 //邻域结构1 使用two_opt_swap算子
 void neighborhood_one(SOLUTION & solution, CITIES *cities)
 {
     ;
     ;

     int inital_cost = solution.cost; //初始花费
     ;

     //SOLUTION current_solution = solution;

     ; i < CITY_SIZE - ; i++)
     {
         ; k < CITY_SIZE; k++)
         {
             Delta1[i][k] = calc_delta1(i, k, solution.permutation, cities);
         }
     }

     do
     {
         count++;
         ; i < CITY_SIZE - ; i++)
         {
             ; k < CITY_SIZE; k++)
             {
                 )
                 {
                     //current_solution = solution;
                     two_opt_swap(solution.permutation, i, k);

                     now_cost = inital_cost + Delta1[i][k];
                     solution.cost = now_cost;

                     inital_cost = solution.cost;

                     Update1(i, k, solution.permutation, cities, Delta1);

                     count = ; //count复位

                 }

              }
           }
     }while (count <= max_no_improve);

 }

 //two_h_opt_swap的去重
 int calc_delta2(int i, int k, int *cities_permutation, CITIES * cities)
 {
     ;
     )
     {
         )
         {
             delta = ;
         }
         )
         {
             delta =
                 - distance_2city(cities[cities_permutation[i]], cities[cities_permutation[i + ]])
                 - distance_2city(cities[cities_permutation[k]], cities[cities_permutation[k - ]])
                 + distance_2city(cities[cities_permutation[k]], cities[cities_permutation[i+]])
                 + distance_2city(cities[cities_permutation[k - ]], cities[cities_permutation[i]]);
         }
         else
         {
             delta =
                 - distance_2city(cities[cities_permutation[i]], cities[cities_permutation[i + ]])
                 - distance_2city(cities[cities_permutation[k]], cities[cities_permutation[k - ]])
                 - distance_2city(cities[cities_permutation[k]], cities[cities_permutation[k + ]])
                 + distance_2city(cities[cities_permutation[k - ]], cities[cities_permutation[k + ]])
                 + distance_2city(cities[cities_permutation[i]], cities[cities_permutation[k]])
                 + distance_2city(cities[cities_permutation[k]], cities[cities_permutation[i + ]]);
         }
     }
     else
     {
         )
         {
             delta = ;
         }
         )
         {
             delta =
                 - distance_2city(cities[cities_permutation[i]], cities[cities_permutation[i + ]])
                 - distance_2city(cities[cities_permutation[]], cities[cities_permutation[k]])
                 - distance_2city(cities[cities_permutation[k]], cities[cities_permutation[k-]])
                 + distance_2city(cities[cities_permutation[k]], cities[cities_permutation[i + ]])
                 + distance_2city(cities[cities_permutation[k-]], cities[cities_permutation[]])
                 + distance_2city(cities[cities_permutation[i]], cities[cities_permutation[k]]);
         }
         else
         {
             delta =
                 - distance_2city(cities[cities_permutation[i]], cities[cities_permutation[i + ]])
                 - distance_2city(cities[cities_permutation[k]], cities[cities_permutation[k + ]])
                 - distance_2city(cities[cities_permutation[k]], cities[cities_permutation[k - ]])
                 + distance_2city(cities[cities_permutation[i]], cities[cities_permutation[k]])
                 + distance_2city(cities[cities_permutation[k]], cities[cities_permutation[i + ]])
                 + distance_2city(cities[cities_permutation[k - ]], cities[cities_permutation[k + ]]);

         }
     }

     return delta;

 }

 //two_h_opt_swap算子
 void two_h_opt_swap(int *cities_permutation, int a, int d)
 {
     int n = CITY_SIZE;
     vector<int> v;
     v.push_back(cities_permutation[a]);
     v.push_back(cities_permutation[d]);
     // i = 1 to account for a already added
     ; i < n; i++)
     {
         int idx = (a + i) % n;
         // Ignore d which has been added already
         if (idx != d)
         {
             v.push_back(cities_permutation[idx]);
         }
     }

     ; i < v.size(); i++)
     {
         cities_permutation[i] = v[i];
     }

 }

 //邻域结构2 使用two_h_opt_swap算子
 void neighborhood_two(SOLUTION & solution, CITIES *cities)
 {
     ;
     ;
     int inital_cost = solution.cost; //初始花费
     ;
     ;

     do
     {
         count++;
         ; i < CITY_SIZE - ; i++)
         {
             ; k < CITY_SIZE; k++)
             {

                 delta = calc_delta2(i, k, solution.permutation, cities);

                 )
                 {
                     //cout<<"delta = " <<delta<<endl; 

                     two_h_opt_swap(solution.permutation, i, k);

                     now_cost = inital_cost + delta;
                     solution.cost = now_cost;

                     inital_cost = solution.cost;

                     count = ; //count复位
                 }
             }
         }
     } while (count <= max_no_improve);
 }

 //VND
 //best_solution最优解
 //current_solution当前解
 void variable_neighborhood_descent(SOLUTION & solution, CITIES * cities)
 {

     SOLUTION current_solution = solution;
     ;
     cout  <<"=====================VariableNeighborhoodDescent=====================" << endl;
     while(true)
     {
         switch (l)
         {
         :
             neighborhood_one(current_solution, cities);
             cout << setw() << setiosflags(ios::left)  <<) << setiosflags(ios::left) << "  solution = " << solution.cost << endl;
             if (current_solution.cost < solution.cost)
             {
                 solution = current_solution;
                 l = ;
             }
             break;
         :
             neighborhood_two(current_solution, cities);
             cout << setw() << setiosflags(ios::left) << ) << setiosflags(ios::left) << "  solution = " << solution.cost << endl;
             if (current_solution.cost < solution.cost)
             {
                 solution = current_solution;
                 l = ;
             }
             break;

         default:
             return;
         }
         l++;

     }

 }

 //将城市序列分成4块,然后按块重新打乱顺序。
 //用于扰动函数
 void double_bridge_move(int * cities_permutation)
 {
      + rand() % (CITY_SIZE / );
      + rand() % (CITY_SIZE / );
      + rand() % (CITY_SIZE / );

     int i;
     vector<int> v;
     //第一块
     ; i < pos1; i++)
     {
         v.push_back(cities_permutation[i]);
     }

     //第二块
     for (i = pos3; i < CITY_SIZE; i++)
     {
         v.push_back(cities_permutation[i]);
     }
     //第三块
     for (i = pos2; i < pos3; i++)
     {
         v.push_back(cities_permutation[i]);
     }

     //第四块
     for (i = pos1; i < pos2; i++)
     {
         v.push_back(cities_permutation[i]);
     }

     ; i < (int)v.size(); i++)
     {
         cities_permutation[i] = v[i];
     }

 }

 //抖动
 void shaking(SOLUTION &solution, CITIES *cities)
 {
     double_bridge_move(solution.permutation);
     solution.cost = cost_total(solution.permutation, cities);
 }

 void variable_neighborhood_search(SOLUTION & best_solution, CITIES * cities)
 {

     ;

     , it = ;

     SOLUTION current_solution = best_solution;

     //算法开始
     do
     {
         cout << endl <<  << "  times" << endl;
         count++;
         it++;
         shaking(current_solution, cities);

         variable_neighborhood_descent(current_solution, cities); 

         if (current_solution.cost < best_solution.cost)
         {
             best_solution = current_solution;
             count = ;
         }

         cout << "\t\t全局best_solution = " << best_solution.cost << endl;

     } while (count <= max_iterations);

 }

 int main()
 {

     srand((unsigned) time());

     SOLUTION best_solution;

     random_permutation(best_solution.permutation);
     best_solution.cost = cost_total(best_solution.permutation, berlin52);

     cout << "初始总路线长度 = " << best_solution.cost << endl;

     variable_neighborhood_search(best_solution, berlin52);

     cout << endl << endl << "搜索完成! 最优路线总长度 = " << best_solution.cost << endl;
     cout << "最优访问城市序列如下:" << endl;
     ; i < CITY_SIZE; i++)
     {
         cout << setw() << setiosflags(ios::left) << best_solution.permutation[i];
     }

     cout << endl << endl;

     ;
 }

程序结果:

04 变邻域搜索解决01背包问题

//TO DO

  1#include <iostream>  2#include <vector>  3#include <ctime>  4#include <iomanip>  5using namespace std;  6  7// 物品的数量 每一个物品有0和1两种选择 0代表选择当前物品 1代表不选择当前物品  8const int n = 100;  9 10//算法最大迭代次数 11const int Max_Iteration = 1000; 12 13//邻域数量 14const int MaxFlip = 3; 15int flip = 1; 16 17 18//背包最大容量 19const int maxWeight = 5 * n; 20 21//记录已经检查的背包数量 22int solutionsChecked = 0; 23 24//物品对应价值&&重量 25int values[n] = { 0 }; 26int weights[n] = { 0 }; 27 28//随机数种子 29const int seed = 5113; //2971 30 31/************************************************************************/ 32/*  33    解决方案类: 34 35*/ 36/************************************************************************/ 37 38typedef struct Knapsack_Problem_Solution 39{ 40    int selection[n] = { 0 };  //当前方案的物品选择情况 selection[i] == 0 or 1 <==> 不选择 or 选择 第i个物品 41    int total_values = 0;      //当前方案下物品总价值 42    int total_weights = 0;    //当前方案下物品总重量 43}KP_Solution; 44 45//对selection[n]进行评价,计算total_values和total_weights 46void Evaluate_Solution(KP_Solution & x) 47{ 48    x.total_values = 0; 49    x.total_weights = 0; 50    for (int i = 0; i < n; i++) 51    { 52        x.total_values += x.selection[i] * values[i]; 53        x.total_weights += x.selection[i] * weights[i]; 54    } 55 56    if (x.total_weights > maxWeight) 57    { 58        x.total_values = maxWeight - x.total_weights; //超过背包最大容纳重量,价值设置为负数 59    } 60 61} 62 63 64//邻居解集合 65vector<KP_Solution> nbrhood; 66 67void MySwap(int &a, int &b) 68{ 69    int temp = a; 70    a = b; 71    b = temp; 72} 73 74//利用邻域动作生成邻居解 75void neighborhood(KP_Solution &x, int flip) 76{ 77    //邻域动作1 78    if (flip == 1) 79    { 80        nbrhood.clear(); 81        for (int i = 0; i < n; i++) 82        { 83            nbrhood.push_back(x); 84            if (nbrhood[i].selection[i] == 1) 85            { 86                nbrhood[i].selection[i] = 0; 87            } 88            else 89            { 90                nbrhood[i].selection[i] = 1; 91            } 92        } 93    } 94    //邻域动作2 95    else if (flip == 2) 96    { 97        nbrhood.clear(); 98        int a = -1; 99        for (int i = 0; i < n; i++)100        {101            for (int j = i; j < n; j++)102            {103                if (i != j)104                {105                    a += 1;106                    nbrhood.push_back(x);107108                    if (nbrhood[a].selection[i] == 1)109                    {110                        nbrhood[a].selection[i] = 0;111                    }112                    else113                    {114                        nbrhood[a].selection[i] = 1;115                    }116117                    if (nbrhood[a].selection[j] == 1)118                    {119                        nbrhood[a].selection[j] = 0;120                    }121                    else122                    {123                        nbrhood[a].selection[j] = 1;124                    }125126                }127            }128        }129    }130    //邻域动作3131    else132    {133        nbrhood.clear();134        for (int i = 0; i < n; i++)135        {136            nbrhood.push_back(x);137            if ( i < 3)138            {139                MySwap(nbrhood[i].selection[i], nbrhood[i].selection[n + i - 3]);140            }141            else142            {143                MySwap(nbrhood[i].selection[i], nbrhood[i].selection[i - 3]);144            }145        }146    }147148149}150//随机生成价值和重量151void Rand_Value_Weight()152{153    srand(seed);154    for (int i = 0; i < n; i++)155    {156        values[i] = rand() % 90 + 10; // 10 - 100157        weights[i] = rand() % 15 + 5; // 5 - 20158    }159}160161//随机生成解决方案162void Random_Solution(KP_Solution &x)163{164    x.total_values = 0;165    x.total_weights = 0;166    srand((unsigned int)time(NULL));167    for (int i = 0; i < n; i++)168    {169        double rate = (rand() % 100) / 100.0;170        if ( rate < 0.8 )171        {172            x.selection[i] = 0;173        }174        else175        {176            x.selection[i] = 1;177        }178    }179}180181void Variable_Neighborhood_Descent(KP_Solution &x)182{183    int flip = 1;184    KP_Solution x_curr;185    while ( flip < MaxFlip + 1)186    {187        neighborhood(x, flip);188        x_curr = nbrhood[0];189        Evaluate_Solution(x_curr);190191        for(unsigned int i = 1; i < nbrhood.size(); i++)192        {193            solutionsChecked += 1;194195            Evaluate_Solution(nbrhood[i]);196197            if (nbrhood[i].total_values > x_curr.total_values)198            {199                x_curr = nbrhood[i];200            }201        }202        //邻域复位203        if (x_curr.total_weights > x.total_weights)204        {205            x = x_curr;206            flip = 1;207        }208        else209        {210            flip += 1;211        }212    }213}214215216217218void Shaking_Procdure(KP_Solution &x)219{220    srand((unsigned int)time(NULL));221222    int num = rand() % (n / 10) + 3; // 3 - 8223    for (int i = 0; i < num; i++)224    {225        int pos = rand() % n;226        if (x.selection[i] == 0)227        {228            x.selection[i] = 1;229        }230        else231        {232            x.selection[i] = 0;233        }234    }235236    Evaluate_Solution(x);237}238239void Variable_Neighborhood_Search(KP_Solution &x, int iteration)240{241    KP_Solution best = x;242    Variable_Neighborhood_Descent(best);243    for (int i = 0; i < iteration; i++)244    {245        Shaking_Procdure(x);246247        Variable_Neighborhood_Descent(x);248        if (best.total_values < x.total_values)249        {250            best = x;251        }252    }253254    x = best;255}256257int main()258{259    KP_Solution kpx;260261    Rand_Value_Weight();262263    Random_Solution(kpx);264265    Variable_Neighborhood_Search(kpx, Max_Iteration);266267    cout << "石头重量为:" << endl;268269    for (int i = 0; i < n; i++)270    {271        cout << setw(2) <<weights[i] << "  ";272        if ((i + 1) % 25 == 0)273        {274            cout << endl;275        }276    }277278    cout << "\n石头价值为:" << endl;279280    for (int i = 0; i < n; i++)281    {282        cout << values[i] << "  ";283        if ((i + 1) % 25 == 0)284        {285            cout << endl;286        }287    }288289    cout << endl << "最终结果: 已检查的总方案数 = " << solutionsChecked << endl;290    cout << "背包最大容量为:" << maxWeight << endl;291    cout << "找到最大价值为: " << kpx.total_values << endl;292    cout << "背包当前重量为: " << kpx.total_weights << endl;293294    for (int i = 0; i < n; i++)295    {296        cout << kpx.selection[i] << "  ";297        if ((i+1) % 25 == 0)298        {299            cout << endl;300        }301    }302303    return 0;304}

程序结果:

【智能算法】变邻域搜索算法(Variable Neighborhood Search,VNS)超详细解析和TSP代码实例以及01背包代码实例的更多相关文章

  1. 【算法】变邻域搜索算法(Variable Neighborhood Search,VNS)超详细一看就懂的解析

    更多精彩尽在微信公众号[程序猿声] 变邻域搜索算法(Variable Neighborhood Search,VNS)一看就懂的解析 00 目录 局部搜索再次科普 变邻域搜索 造轮子写代码 01 局部 ...

  2. 变邻域搜索(Variable neighborhood search)

    变邻域搜索(Variable neighborhood search)VNS是Hansen等提出的一种元启发近似算法,它通过在不同的邻域结构内跳转搜索, 能够避免陷入局部最优解. 算法主要分为两部分: ...

  3. MIP启发式算法:Variable neighborhood search

    *本文主要记录和分享学习到的知识,算不上原创. *参考文章见链接. 本文主要讲述启发式算法中的变邻域搜索(Variable neighborhood search).变邻域搜索的特色在于邻域结构的可变 ...

  4. 【优化算法】变邻域搜索算法(VNS)求解TSP(附C++详细代码及注释)

    00 前言 上次变邻域搜索的推文发出来以后,看过的小伙伴纷纷叫好.小编大受鼓舞,连夜赶工,总算是完成了手头上的一份关于变邻域搜索算法解TSP问题的代码.今天,就在此给大家双手奉上啦,希望大家能ENJO ...

  5. 干货 | 自适应大邻域搜索(Adaptive Large Neighborhood Search)入门到精通超详细解析-概念篇

    01 首先来区分几个概念 关于neighborhood serach,这里有好多种衍生和变种出来的胡里花俏的算法.大家在上网搜索的过程中可能看到什么Large Neighborhood Serach, ...

  6. 【优化算法】变邻域搜索算法解决0-1背包问题(Knapsack Problem)代码实例 已

    01 前言 经过小编这几天冒着挂科的风险,日日修炼,终于赶在考试周中又给大家更新了一篇干货文章.关于用变邻域搜索解决0-1背包问题的代码.怎样,大家有没有很感动? 02 什么是0-1背包问题? 0-1 ...

  7. 【优化算法】Greedy Randomized Adaptive Search算法 超详细解析,附代码实现TSP问题求解

    01 概述 Greedy Randomized Adaptive Search,贪婪随机自适应搜索(GRAS),是组合优化问题中的多起点元启发式算法,在算法的每次迭代中,主要由两个阶段组成:构造(co ...

  8. 【智能算法】粒子群算法(Particle Swarm Optimization)超详细解析+入门代码实例讲解

    喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 01 算法起源 粒子群优化算法(PSO)是一种进化计算技术(evolutionary computation),1995 年由E ...

  9. 【Java面试题】2 Java中使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?超详细解析

    /* * 问题:使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变 * 答: * 使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的 ...

随机推荐

  1. springmvc高级知识点

  2. 【bzoj1018】[SHOI2008]堵塞的交通traffic

    1018: [SHOI2008]堵塞的交通traffic Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 2887  Solved: 954[Submit ...

  3. Shiro的 rememberMe 功能使用指导(为什么rememberMe设置了没作用?)

    UsernamePasswordToken token = new UsernamePasswordToken(loginForm.getUsername(),loginForm.getPasswor ...

  4. 下载Redis

    1.下载当前Redis 官网:https://redis.io/ 当前稳定版本是4.0.11,如下图,点Download it下面的链接进行下载 2.下载历史版本的Resis 网址: http://d ...

  5. linux下scsi共享磁盘的简单搭建

    linux下scsi共享磁盘的简单搭建 Scsi 共享磁盘需要我先有空余的分区,或者可以在虚拟机里面添加一块磁盘,安装所需的软件我在虚拟机里面添加了一块硬盘,分了一个主分区,sdb1 1G,将这个用s ...

  6. ZIP压缩格式与RAR压缩格式

    早已习惯了安装系统之后必须安装winrar,压缩文件也已经习惯了rar格式,这种习惯的力量真的挺可怕的.在工作中你的同事可能没有安装winrar,或者他们不喜欢安装盗版软件,这时候你给他们发送过去的是 ...

  7. loj2512 [BJOI2018]链上二次求和

    传送门 分析 咕咕咕 代码 #include<iostream> #include<cstdio> #include<cstring> #include<st ...

  8. Smarty3——变量修饰器

    变量修饰器可以用于变量, 自定义函数或者字符串. 使用修饰器,需要在变量的后面加上|(竖线)并且跟着修饰器名称. 修饰器可能还会有附加的参数以便达到效果. 参数会跟着修饰器名称,用:(冒号)分开. 同 ...

  9. c# 二分查找法(2分钟算法)

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...

  10. Part4_lesson1---Bootloader设计蓝图

    1.bootloader的作用 2.u-boot是bootloader业界的老大 u-boot分为自主模式和开发模式 3.建立U-boot工程 uboot不能在window下面进行解压,因为在wind ...