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

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

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

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
    还是要说一点,随机产生两点,塞进新排列头部。其余的按顺序往后逐个塞进去。嗯,来看图片~

看代码吧。

 
  1 ////////////////////////
  2 //TSP问题 变邻域搜索求解代码
  3 //基于Berlin52例子求解
  4 //作者:infinitor
  5 //时间:2018-04-12
  6 ////////////////////////
  7 
  8 
  9 #include 
 10 #include 
 11 #include 
 12 #include 
 13 #include 
 14 #include 
 15 #include 
 16 #include <string.h>
 17 #include 
 18 #include  
 19 #define DEBUG
 20 
 21 using namespace std;
 22 
 23 #define CITY_SIZE 52 //城市数量
 24 
 25 
 26 //城市坐标
 27 typedef struct candidate
 28 {
 29     int x;
 30     int y;
 31 }city, CITIES;
 32 
 33 //解决方案
 34 typedef struct Solution
 35 {
 36     int permutation[CITY_SIZE]; //城市排列
 37     int cost;                         //该排列对应的总路线长度
 38 }SOLUTION;
 39 
 40 //城市排列
 41 int permutation[CITY_SIZE];
 42 //城市坐标数组
 43 CITIES cities[CITY_SIZE];
 44 
 45 
 46 //berlin52城市坐标,最优解7542好像
 47 CITIES berlin52[CITY_SIZE] =
 48 { 
 49 { 565,575 },{ 25,185 },{ 345,750 },{ 945,685 },{ 845,655 },
 50 { 880,660 },{ 25,230 },{ 525,1000 },{ 580,1175 },{ 650,1130 },{ 1605,620 },
 51 { 1220,580 },{ 1465,200 },{ 1530,5 },{ 845,680 },{ 725,370 },{ 145,665 },
 52 { 415,635 },{ 510,875 },{ 560,365 },{ 300,465 },{ 520,585 },{ 480,415 },
 53 { 835,625 },{ 975,580 },{ 1215,245 },{ 1320,315 },{ 1250,400 },{ 660,180 },
 54 { 410,250 },{ 420,555 },{ 575,665 },{ 1150,1160 },{ 700,580 },{ 685,595 },
 55 { 685,610 },{ 770,610 },{ 795,645 },{ 720,635 },{ 760,650 },{ 475,960 },
 56 { 95,260 },{ 875,920 },{ 700,500 },{ 555,815 },{ 830,485 },{ 1170,65 },
 57 { 830,610 },{ 605,625 },{ 595,360 },{ 1340,725 },{ 1740,245 } 
 58 };
 59 //优化值
 60 int Delta1[CITY_SIZE][CITY_SIZE] = { 0 };
 61 
 62 
 63 //计算两个城市间距离
 64 int distance_2city(city c1, city c2)
 65 {
 66     int distance = 0;
 67     distance = sqrt((double)((c1.x - c2.x)*(c1.x - c2.x) + (c1.y - c2.y)*(c1.y - c2.y)));
 68 
 69     return distance;
 70 }
 71 
 72 //根据产生的城市序列,计算旅游总距离
 73 //所谓城市序列,就是城市先后访问的顺序,比如可以先访问ABC,也可以先访问BAC等等
 74 //访问顺序不同,那么总路线长度也是不同的
 75 //p_perm 城市序列参数
 76 int cost_total(int * cities_permutation, CITIES * cities)
 77 {
 78     int total_distance = 0;
 79     int c1, c2;
 80     //逛一圈,看看最后的总距离是多少
 81     for (int i = 0; i < CITY_SIZE; i++)
 82     {
 83         c1 = cities_permutation[i];
 84         if (i == CITY_SIZE - 1) //最后一个城市和第一个城市计算距离
 85         {
 86             c2 = cities_permutation[0];
 87         }
 88         else
 89         {
 90             c2 = cities_permutation[i + 1];
 91         }
 92         total_distance += distance_2city(cities[c1], cities[c2]);
 93     }
 94 
 95     return total_distance;
 96 }
 97 
 98 //获取随机城市排列
 99 void random_permutation(int * cities_permutation)
100 {
101     int i, r, temp;
102     for (i = 0; i < CITY_SIZE; i++)
103     {
104         cities_permutation[i] = i; //初始化城市排列,初始按顺序排
105     }
106     
107     random_shuffle(cities_permutation, cities_permutation + CITY_SIZE); //随机化排序 
108 
109 }
110 //对应two_opt_swap的去重
111 int calc_delta1(int i, int k, int *tmp, CITIES * cities) {
112     int delta = 0;
113     /*
114     以下计算说明:
115     对于每个方案,翻转以后没必要再次重新计算总距离
116     只需要在翻转的头尾做个小小处理
117 
118     比如:
119     有城市序列   1-2-3-4-5 总距离 = d12 + d23 + d34 + d45 + d51 = A
120     翻转后的序列 1-4-3-2-5 总距离 = d14 + d43 + d32 + d25 + d51 = B
121     由于 dij 与 dji是一样的,所以B也可以表示成 B = A - d12 - d45 + d14 + d25
122     下面的优化就是基于这种原理
123     */
124     if (i == 0)
125     {
126         if (k == CITY_SIZE - 1)
127         {
128             delta = 0;
129         }
130         else
131         {
132             delta = 0
133                 - distance_2city(cities[tmp[k]], cities[tmp[k + 1]])
134                 + distance_2city(cities[tmp[i]], cities[tmp[k + 1]])
135                 - distance_2city(cities[tmp[CITY_SIZE - 1]], cities[tmp[i]])
136                 + distance_2city(cities[tmp[CITY_SIZE - 1]], cities[tmp[k]]);
137         }
138 
139     }
140     else
141     {
142         if (k == CITY_SIZE - 1)
143         {
144             delta = 0
145                 - distance_2city(cities[tmp[i - 1]], cities[tmp[i]])
146                 + distance_2city(cities[tmp[i - 1]], cities[tmp[k]])
147                 - distance_2city(cities[tmp[0]], cities[tmp[k]])
148                 + distance_2city(cities[tmp[i]], cities[tmp[0]]);
149         }
150         else
151         {
152             delta = 0
153                 - distance_2city(cities[tmp[i - 1]], cities[tmp[i]])
154                 + distance_2city(cities[tmp[i - 1]], cities[tmp[k]])
155                 - distance_2city(cities[tmp[k]], cities[tmp[k + 1]])
156                 + distance_2city(cities[tmp[i]], cities[tmp[k + 1]]);
157         }
158     }
159 
160     return delta;
161 }
162 
163 
164 /*
165 去重处理,对于Delta数组来说,对于城市序列1-2-3-4-5-6-7-8-9-10,如果对3-5应用了邻域操作2-opt , 事实上对于
166 7-10之间的翻转是不需要重复计算的。 所以用Delta提前预处理一下。
167 
168 当然由于这里的计算本身是O(1) 的,事实上并没有带来时间复杂度的减少(更新操作反而增加了复杂度)
169 如果delta计算 是O(n)的,这种去重操作效果是明显的。
170 */
171 //对应two_opt_swap的去重更新
172 void Update1(int i, int k, int *tmp, CITIES * cities, int Delta[CITY_SIZE][CITY_SIZE]) {
173     if (i && k != CITY_SIZE - 1) {
174         i--; k++;
175         for (int j = i; j <= k; j++) {
176             for (int l = j + 1; l < CITY_SIZE; l++) {
177                 Delta[j][l] = calc_delta1(j, l, tmp, cities);
178             }
179         }
180 
181         for (int j = 0; j < k; j++) {
182             for (int l = i; l <= k; l++) {
183                 if (j >= l) continue;
184                 Delta[j][l] = calc_delta1(j, l, tmp, cities);
185             }
186         }
187     }// 如果不是边界,更新(i-1, k + 1)之间的 
188     else {
189         for (i = 0; i < CITY_SIZE - 1; i++)
190         {
191             for (k = i + 1; k < CITY_SIZE; k++)
192             {
193                 Delta[i][k] = calc_delta1(i, k, tmp, cities);
194             }
195         }
196     }// 边界要特殊更新 
197 
198 }
199 
200 
201 // two_opt_swap算子 
202 void two_opt_swap(int *cities_permutation, int b, int c) 
203 {
204     vector<int> v;
205     for (int i = 0; i < b; i++) 
206     {
207         v.push_back(cities_permutation[i]);
208     }
209     for (int i = c; i >= b; i--) 
210     {
211         v.push_back(cities_permutation[i]);
212     }
213     for (int i = c + 1; i < CITY_SIZE; i++) 
214     {
215         v.push_back(cities_permutation[i]);
216     }
217 
218     for (int i = 0; i < CITY_SIZE; i++)
219     {
220         cities_permutation[i] = v[i];
221     }
222 
223 }
224 
225 //邻域结构1 使用two_opt_swap算子
226 void neighborhood_one(SOLUTION & solution, CITIES *cities)
227 {
228     int i, k, count = 0;
229     int max_no_improve = 60;
230 
231     int inital_cost = solution.cost; //初始花费
232     int now_cost = 0;
233 
234     //SOLUTION current_solution = solution;
235 
236     for (int i = 0; i < CITY_SIZE - 1; i++)
237     {
238         for (k = i + 1; k < CITY_SIZE; k++)
239         {
240             Delta1[i][k] = calc_delta1(i, k, solution.permutation, cities);
241         }
242     }
243 
244     do 
245     {
246         count++;
247         for (i = 0; i < CITY_SIZE - 1; i++)
248         {
249             for (k = i + 1; k < CITY_SIZE; k++)
250             {
251                 if (Delta1[i][k] < 0)
252                 {
253                     //current_solution = solution;
254                     two_opt_swap(solution.permutation, i, k);
255 
256                     now_cost = inital_cost + Delta1[i][k];
257                     solution.cost = now_cost;
258 
259                     inital_cost = solution.cost;
260 
261                     Update1(i, k, solution.permutation, cities, Delta1);
262 
263                     count = 0; //count复位
264                     
265                 }
266 
267              }
268           }
269     }while (count <= max_no_improve);
270 
271 }
272 
273 //two_h_opt_swap的去重
274 int calc_delta2(int i, int k, int *cities_permutation, CITIES * cities)
275 {
276     int delta = 0;
277     if (i == 0)
278     {
279         if ( k == i+1)
280         {
281             delta = 0;
282         }
283         else if ( k == CITY_SIZE -1)
284         {
285             delta = 0
286                 - distance_2city(cities[cities_permutation[i]], cities[cities_permutation[i + 1]])
287                 - distance_2city(cities[cities_permutation[k]], cities[cities_permutation[k - 1]])
288                 + distance_2city(cities[cities_permutation[k]], cities[cities_permutation[i+1]])
289                 + distance_2city(cities[cities_permutation[k - 1]], cities[cities_permutation[i]]);
290         }
291         else
292         {
293             delta = 0
294                 - distance_2city(cities[cities_permutation[i]], cities[cities_permutation[i + 1]])
295                 - distance_2city(cities[cities_permutation[k]], cities[cities_permutation[k - 1]])
296                 - distance_2city(cities[cities_permutation[k]], cities[cities_permutation[k + 1]])
297                 + distance_2city(cities[cities_permutation[k - 1]], cities[cities_permutation[k + 1]])
298                 + distance_2city(cities[cities_permutation[i]], cities[cities_permutation[k]])
299                 + distance_2city(cities[cities_permutation[k]], cities[cities_permutation[i + 1]]);
300         }
301     }
302     else
303     {
304         if (k == i + 1)
305         {
306             delta = 0;
307         }
308         else if ( k == CITY_SIZE - 1)
309         {
310             delta = 0
311                 - distance_2city(cities[cities_permutation[i]], cities[cities_permutation[i + 1]])
312                 - distance_2city(cities[cities_permutation[0]], cities[cities_permutation[k]])
313                 - distance_2city(cities[cities_permutation[k]], cities[cities_permutation[k-1]])
314                 + distance_2city(cities[cities_permutation[k]], cities[cities_permutation[i + 1]])
315                 + distance_2city(cities[cities_permutation[k-1]], cities[cities_permutation[0]])
316                 + distance_2city(cities[cities_permutation[i]], cities[cities_permutation[k]]);
317         }
318         else
319         {
320             delta = 0
321                 - distance_2city(cities[cities_permutation[i]], cities[cities_permutation[i + 1]])
322                 - distance_2city(cities[cities_permutation[k]], cities[cities_permutation[k + 1]])
323                 - distance_2city(cities[cities_permutation[k]], cities[cities_permutation[k - 1]])
324                 + distance_2city(cities[cities_permutation[i]], cities[cities_permutation[k]])
325                 + distance_2city(cities[cities_permutation[k]], cities[cities_permutation[i + 1]])
326                 + distance_2city(cities[cities_permutation[k - 1]], cities[cities_permutation[k + 1]]);
327 
328         }
329     }
330 
331     return delta;
332 
333 }
334 
335 
336 
337 //two_h_opt_swap算子
338 void two_h_opt_swap(int *cities_permutation, int a, int d) 
339 {
340     int n = CITY_SIZE;
341     vector<int> v;
342     v.push_back(cities_permutation[a]);
343     v.push_back(cities_permutation[d]);
344     // i = 1 to account for a already added
345     for (int i = 1; i < n; i++) 
346     {
347         int idx = (a + i) % n;
348         // Ignore d which has been added already
349         if (idx != d) 
350         {
351             v.push_back(cities_permutation[idx]);
352         }
353     }
354 
355     for (int i = 0; i < v.size(); i++)
356     {
357         cities_permutation[i] = v[i];
358     }
359 
360 }
361 
362 
363 //邻域结构2 使用two_h_opt_swap算子
364 void neighborhood_two(SOLUTION & solution, CITIES *cities)
365 {
366     int i, k, count = 0;
367     int max_no_improve = 60;
368     int inital_cost = solution.cost; //初始花费
369     int now_cost = 0;
370     int delta = 0;
371 
372     do
373     {
374         count++;
375         for (i = 0; i < CITY_SIZE - 1; i++)
376         {
377             for (k = i + 1; k < CITY_SIZE; k++)
378             {
379                 
380                 delta = calc_delta2(i, k, solution.permutation, cities);
381 
382                 if (delta < 0)
383                 {
384                     //cout<<"delta = " <
385 
386                     two_h_opt_swap(solution.permutation, i, k);
387 
388                     now_cost = inital_cost + delta;
389                     solution.cost = now_cost;
390 
391                     inital_cost = solution.cost;
392 
393                     count = 0; //count复位
394                 }
395             }
396         }
397     } while (count <= max_no_improve);
398 }
399 
400 
401 //VND
402 //best_solution最优解
403 //current_solution当前解
404 void variable_neighborhood_descent(SOLUTION & solution, CITIES * cities)
405 {
406 
407     SOLUTION current_solution = solution;
408     int l = 1;
409     cout  <<"=====================VariableNeighborhoodDescent=====================" << endl;
410     while(true)
411     {
412         switch (l)
413         {
414         case 1:
415             neighborhood_one(current_solution, cities);
416             cout << setw(45) << setiosflags(ios::left)  <<"Now in neighborhood_one , current_solution = " << current_solution.cost << setw(10) << setiosflags(ios::left) << "  solution = " << solution.cost << endl;
417             if (current_solution.cost < solution.cost)
418             {
419                 solution = current_solution;
420                 l = 0;
421             }
422             break;
423         case 2:
424             neighborhood_two(current_solution, cities);
425             cout << setw(45) << setiosflags(ios::left) << "Now in neighborhood_two , current_solution = " << current_solution.cost << setw(10) << setiosflags(ios::left) << "  solution = " << solution.cost << endl;
426             if (current_solution.cost < solution.cost)
427             {
428                 solution = current_solution;
429                 l = 0;
430             }
431             break;
432 
433         default:
434             return;
435         }
436         l++;
437 
438     }
439 
440 }
441 
442 //将城市序列分成4块,然后按块重新打乱顺序。
443 //用于扰动函数
444 void double_bridge_move(int * cities_permutation)
445 {
446     int pos1 = 1 + rand() % (CITY_SIZE / 4);
447     int pos2 = pos1 + 1 + rand() % (CITY_SIZE / 4);
448     int pos3 = pos2 + 1 + rand() % (CITY_SIZE / 4);
449 
450     int i;
451     vector<int> v;
452     //第一块
453     for (i = 0; i < pos1; i++)
454     {
455         v.push_back(cities_permutation[i]);
456     }
457 
458     //第二块
459     for (i = pos3; i < CITY_SIZE; i++)
460     {
461         v.push_back(cities_permutation[i]);
462     }
463     //第三块
464     for (i = pos2; i < pos3; i++)
465     {
466         v.push_back(cities_permutation[i]);
467     }
468 
469     //第四块
470     for (i = pos1; i < pos2; i++)
471     {
472         v.push_back(cities_permutation[i]);
473     }
474 
475 
476     for (i = 0; i < (int)v.size(); i++)
477     {
478         cities_permutation[i] = v[i];
479     }
480 
481 
482 }
483 
484 //抖动
485 void shaking(SOLUTION &solution, CITIES *cities)
486 {
487     double_bridge_move(solution.permutation);
488     solution.cost = cost_total(solution.permutation, cities);
489 }
490 
491 
492 void variable_neighborhood_search(SOLUTION & best_solution, CITIES * cities)
493 {
494 
495     int max_iterations = 5;
496 
497     int count = 0, it = 0;
498 
499     SOLUTION current_solution = best_solution;
500 
501     //算法开始
502     do 
503     {
504         cout << endl << "\t\tAlgorithm VNS iterated  " << it+1 << "  times" << endl;
505         count++;
506         it++;
507         shaking(current_solution, cities);
508 
509         variable_neighborhood_descent(current_solution, cities); 
510 
511         if (current_solution.cost < best_solution.cost)
512         {
513             best_solution = current_solution;
514             count = 0;
515         }
516 
517         cout << "\t\t全局best_solution = " << best_solution.cost << endl;
518 
519     } while (count <= max_iterations);
520 
521 
522 }
523 
524 
525 int main()
526 {
527 
528     srand((unsigned) time(0));
529 
530     SOLUTION best_solution;
531 
532     random_permutation(best_solution.permutation);
533     best_solution.cost = cost_total(best_solution.permutation, berlin52);
534 
535     cout << "初始总路线长度 = " << best_solution.cost << endl;
536 
537     variable_neighborhood_search(best_solution, berlin52);
538 
539     cout << endl << endl << "搜索完成! 最优路线总长度 = " << best_solution.cost << endl;
540     cout << "最优访问城市序列如下:" << endl;
541     for (int i = 0; i < CITY_SIZE; i++)
542     {
543         cout << setw(4) << setiosflags(ios::left) << best_solution.permutation[i];
544     }
545 
546     cout << endl << endl;
547 
548     return 0;
549 }

 

 

 

程序结果:

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

//TO DO

  1#include 
2#include 
3#include 
4#include 
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 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);
107
108                    if (nbrhood[a].selection[i] == 1)
109                    {
110                        nbrhood[a].selection[i] = 0;
111                    }
112                    else
113                    {
114                        nbrhood[a].selection[i] = 1;
115                    }
116
117                    if (nbrhood[a].selection[j] == 1)
118                    {
119                        nbrhood[a].selection[j] = 0;
120                    }
121                    else
122                    {
123                        nbrhood[a].selection[j] = 1;
124                    }
125
126                }
127            }
128        }
129    }
130    //邻域动作3
131    else
132    {
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            else
142            {
143                MySwap(nbrhood[i].selection[i], nbrhood[i].selection[i - 3]);
144            }
145        }
146    }
147
148
149}
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 - 100
157        weights[i] = rand() % 15 + 5// 5 - 20
158    }
159}
160
161//随机生成解决方案
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        else
175        {
176            x.selection[i] = 1;
177        }
178    }
179}
180
181void 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);
190
191        for(unsigned int i = 1; i < nbrhood.size(); i++)
192        {
193            solutionsChecked += 1;
194
195            Evaluate_Solution(nbrhood[i]);
196
197            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        else
209        {
210            flip += 1;
211        }
212    }
213}
214
215
216
217
218void Shaking_Procdure(KP_Solution &x)
219
{
220    srand((unsigned int)time(NULL));
221
222    int num = rand() % (n / 10) + 3// 3 - 8
223    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        else
231        {
232            x.selection[i] = 0;
233        }
234    }
235
236    Evaluate_Solution(x);
237}
238
239void 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);
246
247        Variable_Neighborhood_Descent(x);
248        if (best.total_values < x.total_values)
249        {
250            best = x;
251        }
252    }
253
254    x = best;
255}
256
257int main()
258
{
259    KP_Solution kpx;
260
261    Rand_Value_Weight();
262
263    Random_Solution(kpx);
264
265    Variable_Neighborhood_Search(kpx, Max_Iteration);
266
267    cout << "石头重量为:" << endl;
268
269    for (int i = 0; i < n; i++)
270    {
271        cout << setw(2) <"  ";
272        if ((i + 1) % 25 == 0)
273        {
274            cout << endl;
275        }
276    }
277
278    cout << "\n石头价值为:" << endl;
279
280    for (int i = 0; i < n; i++)
281    {
282        cout << values[i] << "  ";
283        if ((i + 1) % 25 == 0)
284        {
285            cout << endl;
286        }
287    }
288
289    cout << endl << "最终结果: 已检查的总方案数 = " << solutionsChecked << endl;
290    cout << "背包最大容量为:" << maxWeight << endl;
291    cout << "找到最大价值为: " << kpx.total_values << endl;
292    cout << "背包当前重量为: " << kpx.total_weights << endl;
293
294    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    }
302
303    return 0;
304}

程序结果:

 

posted @ 2018-06-29 22:43 短短的路走走停停 阅读( ...) 评论( ...) 编辑 收藏

你可能感兴趣的:(【智能算法】变邻域搜索算法(Variable Neighborhood Search,VNS)超详细解析和TSP代码实例以及01背包代码实例...)