原文链接:https://cloud.tencent.com/developer/article/1146127

1.1 什么是局部搜索算法?

局部搜索是解决最优化问题的一种启发式算法。因为对于很多复杂的问题,求解最优解的时间可能是极其长的。因此诞生了各种启发式算法来退而求其次寻找次优解或近似最优解,局部搜索就是其中一种。它是一种近似算法(Approximate algorithms)。

局部搜索算法是从爬山法改进而来的。简单来说,局部搜索算法是一种简单的贪心搜索算法,该算法每次从当前解的邻域解空间中选择一个最好邻居作为下次迭代的当前解,直到达到一个局部最优解(local optimal solution)。局部搜索从一个初始解出发,然后搜索解的邻域,如有更优的解则移动至该解并继续执行搜索,否则就停止算法获得局部最优解。

1.2 算法思想过程

局部搜索会先从一个初始解开始,通过邻域动作。产生初始解的邻居解,然后根据某种策略选择邻居解。一直重复以上过程,直到达到终止条件。

不同局部搜索算法的区别就在于:邻域动作的定义以及选择邻居解的策略。这也是决定算法好坏的关键之处。

1.3 什么又是邻域动作?

其实邻域动作就是一个函数。那么,通过这个函数,针对当前解s,产生s对应的邻居解的一个集合。比如:

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

02 简单局部搜索

在开始我们的迭代局部搜索之前,还是先来给大家科普几个简单局部搜索算法。他们也是基于个体的启发式算法(Single solution)。

2.1 爬山法(HILL-CLIMBING)

请阅读推文 干货 | 用模拟退火(SA, Simulated Annealing)算法解决旅行商问题

2.2 模拟退火(SIMULATED ANNEALING)

请阅读推文 干货 | 用模拟退火(SA, Simulated Annealing)算法解决旅行商问题

2.3 禁忌搜索算法(Tabu Search)

请阅读推文 干货 | 十分钟掌握禁忌搜索算法求解带时间窗的车辆路径问题(附C++代码和详细代码注释) 及 干货|十分钟快速复习禁忌搜索(c++版)

03 迭代局部搜索(Iterated Local Search, ILS)

3.1 介绍

迭代局部搜索属于探索性局部搜索方法(EXPLORATIVE LOCAL SEARCH METHODS)的一种。它在局部搜索得到的局部最优解上,加入了扰动,然后再重新进行局部搜索。

3.2 过程描述

注:下文的局部搜索(或者LocalSearch)指定都是内嵌的局部搜索。类似于上面介绍的几种……

迭代局部搜索过程:

*初始状态:best_solution(最优解)、current_solution(当前解)。

*从初始解(best_solution)中进行局部搜索,找到一个局部最优解s1(best_solution)。

*扰动s1(best_solution),获得新的解s2(current_solution)。

*从新解s2(current_solution)中进行局部搜索,再次找到一个局部最优解s3(best_solution)。

*基于判断策略,对s3(current_solution)好坏进行判断。选择是否接受s3(current_solution)作为新的best_solution。

*直到达到边界条件,不然跳回第二步一直循环搜索。

其图解如下:

伪代码如下:

04 代码时间

以下代码用于求解TSP旅行商问题。

【注:代码和程序基于win32平台跑的,点击阅读原文可以直接下载或者移步留言区获取下载链接】

我就不做IO了,相信需要的朋友做个IO也不是什么难事吧……


//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>#define DEBUGusing namespace std;#define CITY_SIZE 52 //城市数量//城市坐标
typedef struct candidate
{int x;int y;
}city, CITIES;//优化值
int **Delta; //解决方案
typedef struct Solution
{int permutation[CITY_SIZE]; //城市排列int cost;                        //该排列对应的总路线长度
}SOLUTION;
// 计算邻域操作优化值
int calc_delta(int i, int k, int *tmp, CITIES * cities);//计算两个城市间距离
int distance_2city(city c1, city c2);//根据产生的城市序列,计算旅游总距离
int cost_total(int * cities_permutation, CITIES * cities);//获取随机城市排列, 用于产生初始解
void random_permutation(int * cities_permutation);//颠倒数组中下标begin到end的元素位置, 用于two_opt邻域动作
void swap_element(int *p, int begin, int end);//邻域动作 反转index_i <-> index_j 间的元素
void two_opt_swap(int *cities_permutation, int *new_cities_permutation, int index_i, int index_j);//本地局部搜索,边界条件 max_no_improve
void local_search(SOLUTION & best, CITIES * cities, int max_no_improve);//将城市序列分成4块,然后按块重新打乱顺序。
//用于扰动函数
void double_bridge_move(int *cities_permutation, int * new_cities_permutation);//扰动
void perturbation(CITIES * cities, SOLUTION &best_solution, SOLUTION &current_solution);//迭代搜索
void iterated_local_search(SOLUTION & best, CITIES * cities, int max_iterations, int max_no_improve);// 更新Delta
void Update(int i, int k,  int *tmp, CITIES * cities);//城市排列
int permutation[CITY_SIZE];
//城市坐标数组
CITIES cities[CITY_SIZE];//berlin52城市坐标,最优解7542好像
CITIES berlin52[CITY_SIZE] = { { 565,575 },{ 25,185 },{ 345,750 },{ 945,685 },{ 845,655 },
{ 880,660 },{ 25,230 },{ 525,1000 },{ 580,1175 },{ 650,1130 },{ 1605,620 },
{ 1220,580 },{ 1465,200 },{ 1530,5 },{ 845,680 },{ 725,370 },{ 145,665 },
{ 415,635 },{ 510,875 },{ 560,365 },{ 300,465 },{ 520,585 },{ 480,415 },
{ 835,625 },{ 975,580 },{ 1215,245 },{ 1320,315 },{ 1250,400 },{ 660,180 },
{ 410,250 },{ 420,555 },{ 575,665 },{ 1150,1160 },{ 700,580 },{ 685,595 },
{ 685,610 },{ 770,610 },{ 795,645 },{ 720,635 },{ 760,650 },{ 475,960 },
{ 95,260 },{ 875,920 },{ 700,500 },{ 555,815 },{ 830,485 },{ 1170,65 },
{ 830,610 },{ 605,625 },{ 595,360 },{ 1340,725 },{ 1740,245 } };int main()
{srand(1);int max_iterations = 600;int max_no_improve = 50;//初始化指针数组 Delta = new int*[CITY_SIZE];for (int i = 0; i < CITY_SIZE; i ++)Delta[i] = new int[CITY_SIZE];SOLUTION best_solution;iterated_local_search(best_solution, berlin52, max_iterations, max_no_improve);cout << endl<<endl<<"搜索完成! 最优路线总长度 = " << best_solution.cost << endl;cout << "最优访问城市序列如下:" << endl;for (int i = 0; i < CITY_SIZE;i++){cout << setw(4) << setiosflags(ios::left) << best_solution.permutation[i];}cout << endl << endl;return 0;
}//计算两个城市间距离
int distance_2city(city c1, city c2)
{int distance = 0;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 total_distance = 0;int c1, c2;//逛一圈,看看最后的总距离是多少for (int i = 0; i < CITY_SIZE; i++){c1 = cities_permutation[i];if (i == CITY_SIZE - 1) //最后一个城市和第一个城市计算距离{c2 = cities_permutation[0];}else{c2 = cities_permutation[i + 1];}total_distance += distance_2city(cities[c1], cities[c2]);}return total_distance;
}//获取随机城市排列
void random_permutation(int * cities_permutation)
{int i, r, temp;for (i = 0; i < CITY_SIZE; i++){cities_permutation[i] = i; //初始化城市排列,初始按顺序排}for (i = 0; i < CITY_SIZE; i++){//城市排列顺序随机打乱r = rand() % (CITY_SIZE - i) + i;temp = cities_permutation[i];cities_permutation[i] = cities_permutation[r];cities_permutation[r] = temp;}
}//颠倒数组中下标begin到end的元素位置
void swap_element(int *p, int begin, int end)
{int temp;while (begin < end){temp = p[begin];p[begin] = p[end];p[end] = temp;begin++;end--;}
}//邻域动作 反转index_i <-> index_j 间的元素
void two_opt_swap(int *cities_permutation, int *new_cities_permutation, int index_i, int index_j)
{for (int i = 0; i < CITY_SIZE; i++){new_cities_permutation[i] = cities_permutation[i];}swap_element(new_cities_permutation, index_i, index_j);
}int calc_delta(int i, int k,  int *tmp, CITIES * cities){int delta = 0;/*以下计算说明:对于每个方案,翻转以后没必要再次重新计算总距离只需要在翻转的头尾做个小小处理比如:有城市序列   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下面的优化就是基于这种原理*/if (i == 0){if (k == CITY_SIZE - 1){delta = 0;}else{delta = 0- distance_2city(cities[tmp[k]], cities[tmp[k + 1]])+ distance_2city(cities[tmp[i]], cities[tmp[k + 1]])- distance_2city(cities[tmp[CITY_SIZE - 1]], cities[tmp[i]])+ distance_2city(cities[tmp[CITY_SIZE - 1]], cities[tmp[k]]);}}else{if (k == CITY_SIZE - 1){delta = 0- distance_2city(cities[tmp[i - 1]], cities[tmp[i]])+ distance_2city(cities[tmp[i - 1]], cities[tmp[k]])- distance_2city(cities[tmp[0]], cities[tmp[k]])+ distance_2city(cities[tmp[i]], cities[tmp[0]]);}else{delta = 0- distance_2city(cities[tmp[i - 1]], cities[tmp[i]])+ distance_2city(cities[tmp[i - 1]], cities[tmp[k]])- distance_2city(cities[tmp[k]], cities[tmp[k + 1]])+ distance_2city(cities[tmp[i]], cities[tmp[k + 1]]);}}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)的,这种去重操作效果是明显的。
*/void Update(int i, int k,  int *tmp, CITIES * cities){if (i && k != CITY_SIZE - 1){i --; k ++;for (int j = i; j <= k; j ++){for (int l = j + 1; l < CITY_SIZE; l ++){Delta[j][l] = calc_delta(j, l, tmp, cities);}}for (int j = 0; j < k; j ++){for (int l = i; l <= k; l ++){if (j >= l) continue;Delta[j][l] = calc_delta(j, l, tmp, cities);}}}// 如果不是边界,更新(i-1, k + 1)之间的 else{for (i = 0; i < CITY_SIZE - 1; i++){for (k = i + 1; k < CITY_SIZE; k++){Delta[i][k] = calc_delta(i, k, tmp, cities);}}  }// 边界要特殊更新 } //本地局部搜索,边界条件 max_no_improve
//best_solution最优解
//current_solution当前解
void local_search(SOLUTION & best_solution, CITIES * cities, int max_no_improve)
{int count = 0;int i, k;int inital_cost = best_solution.cost; //初始花费int now_cost = 0;SOLUTION *current_solution = new SOLUTION; //为了防止爆栈……直接new了,你懂的for (i = 0; i < CITY_SIZE - 1; i++){for (k = i + 1; k < CITY_SIZE; k++){Delta[i][k] = calc_delta(i, k, best_solution.permutation, cities);}}do{//枚举排列for (i = 0; i < CITY_SIZE - 1; i++){for (k = i + 1; k < CITY_SIZE; k++){//邻域动作two_opt_swap(best_solution.permutation, current_solution->permutation, i, k);now_cost = inital_cost + Delta[i][k];current_solution->cost = now_cost;if (current_solution->cost < best_solution.cost){count = 0; //better cost found, so resetfor (int j = 0; j < CITY_SIZE; j++){best_solution.permutation[j] = current_solution->permutation[j];}best_solution.cost = current_solution->cost;inital_cost = best_solution.cost;Update(i, k, best_solution.permutation, cities);}}}count++;} while (count <= max_no_improve);
}//将城市序列分成4块,然后按块重新打乱顺序。
//用于扰动函数
void double_bridge_move(int *cities_permutation, int * new_cities_permutation)
{int temp_perm[CITY_SIZE];int pos1 = 1 + rand() % (CITY_SIZE / 4);int pos2 = pos1 + 1 + rand() % (CITY_SIZE / 4);int pos3 = pos2 + 1 + rand() % (CITY_SIZE / 4);int i;vector<int> v;//第一块for (i = 0; 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]);}for (i = 0; i < (int)v.size(); i++){new_cities_permutation[i] = v[i];}    }//扰动
void perturbation(CITIES * cities, SOLUTION &best_solution, SOLUTION &current_solution)
{double_bridge_move(best_solution.permutation, current_solution.permutation);current_solution.cost = cost_total(current_solution.permutation, cities);
}//迭代搜索
//max_iterations用于迭代搜索次数
//max_no_improve用于局部搜索边界条件
void iterated_local_search(SOLUTION & best_solution, CITIES * cities, int max_iterations, int max_no_improve)
{SOLUTION *current_solution = new SOLUTION;//获得初始随机解random_permutation(best_solution.permutation);best_solution.cost = cost_total(best_solution.permutation, cities);local_search(best_solution, cities, max_no_improve); //初始搜索for (int i = 0; i < max_iterations; i++){perturbation(cities, best_solution, *current_solution); //扰动+判断是否接受新解local_search(*current_solution, cities, max_no_improve);//继续局部搜索//找到更优解if (current_solution->cost < best_solution.cost){for (int j = 0; j < CITY_SIZE; j++){best_solution.permutation[j] = current_solution->permutation[j];}best_solution.cost = current_solution->cost;}cout << setw(13) << setiosflags(ios::left) <<"迭代搜索 " << i << " 次\t" << "最优解 = " << best_solution.cost << " 当前解 = " << current_solution->cost << endl;}}

迭代局部搜索算法(Iterated local search)相关推荐

  1. 【智能算法】迭代局部搜索(Iterated Local Search, ILS)详解

    更多精彩尽在微信公众号[程序猿声] 迭代局部搜索(Iterated Local Search, ILS) 00 目录 局部搜索算法 简单局部搜索 迭代局部搜索 01 局部搜索算法 1.1 什么是局部搜 ...

  2. 基于迭代局部搜索的改进麻雀搜索算法

    文章目录 一.理论基础 1.麻雀搜索算法 2.基于迭代局部搜索的麻雀搜索算法(ISSA) 2.1 可变螺旋因子 2.2 改进的迭代局部搜索 2.2.1 第一种改进(SSA方法) 2.2.2 第二种改进 ...

  3. 零基础学启发式算法(2)-局部搜索(Local Search)和爬山算法(Hill Climbing)

    一.局部搜索(Local Search) 局部搜索是一种近似算法(Approximate algorithms),是一种简单的贪心搜索算法.从一个候选解开始,持续地在其邻域中搜索,直至邻域中没有更好的 ...

  4. 局部邻域搜索-爬山法,模拟退火,禁忌,迭代局部搜索,变邻域局部搜索的简单阐释

    原文来源: 局部搜索算法 - JiePro - 博客园 https://www.cnblogs.com/JiePro/p/Metaheuristics_0.html 局部搜索算法 目录: 1.数学定义 ...

  5. 5.4 Penalty-Based Local Search Algorithms基于惩罚的局部搜索算法

    另一种扩展迭代改进策略的方法是,当搜索过程即将停滞在一个局部极小值时,修改该评估函数[71].这种方法也称为动态本地搜索(Dynamic Local Search, DLS)[52]. 基于惩罚的算法 ...

  6. 【人工智能】—局部搜索算法、爬山法、模拟退火、局部剪枝、遗传算法

    Local search algorithms (局部搜索算法) 局部搜索算法 内存限制 局部搜索算法 示例:n-皇后 爬山算法 随机重启爬山 模拟退火算法 局部剪枝搜索 遗传算法 小结 局部搜索算法 ...

  7. 转 | 禁忌搜索算法(Tabu Search)求解带时间窗的车辆路径规划问题详解(附Java代码)

    以下文章来源于数据魔术师 ,作者周航 欲下载本文相关的代码及算例,请关注公众号[程序猿声],后台回复[TSVRPJAVA]不包括[]即可 前言 大家好呀! 眼看这9102年都快要过去了,小编也是越来越 ...

  8. java 路径规划_转 | 禁忌搜索算法(Tabu Search)求解带时间窗的车辆路径规划问题详解(附Java代码)...

    以下文章来源于数据魔术师 ,作者周航 欲下载本文相关的代码及算例,请关注公众号[程序猿声],后台回复[TSVRPJAVA]不包括[]即可 前言 大家好呀! 眼看这9102年都快要过去了,小编也是越来越 ...

  9. 人工智能中的局部搜索算法

      在局部搜索算法中,我们不再关心从初始节点到目标节点之间的路径,而是考虑从当前节点出发,移动到它的邻近状态,直到到达合理的目标状态.相比于前面所说的无信息搜索算法和有信息搜索算法,局部搜索算法往往能 ...

  10. 旅行商问题(TSP)与局部搜索算法

    本篇为在University of Birmingham 学习Advanced Nature-Inspired Search and Optimisation课程中的笔记之一 This is one ...

最新文章

  1. [C#] Delegate, Multicase delegate, Event
  2. python panda读取csv_python pandas 中文件的读写——read_csv()读取文件
  3. [综述类] 一文道尽深度学习中的数据增强方法(上)
  4. Spring-Data-JPA 动态查询黑科技
  5. js 中使用 时间datetime 类型到前端iOS 不兼容问题
  6. 免插件为WordPress文章中标签添加内链
  7. AJax错误WebForm1没有定义的javascript错误的解决方法
  8. Android源码分析(三)-----系统框架设计思想
  9. sublime text3安装、注册及常用插件
  10. 4 安卓安装路径_安卓逆向——APK安装流程
  11. 如何把Netflix数据集转换成Movielens格式?
  12. 计算机专硕毕业论文写什么,最新硕士毕业论文进度安排怎么写
  13. laya龙骨换装_FairyGUI - 骨骼动画
  14. 智加助力解放完成“自动变道”国标验证,唯一量产自动驾驶重卡过考
  15. 工作中jQuery常用实例-主要功能总结整理
  16. 7-7 厘米换算英尺英寸 (10分)
  17. 狗汪汪玩转无线电 -- GPS Hacking
  18. OSChina 周五乱弹 ——什么情况下两个人之间的距离能成为负数
  19. 如何优雅地下载计算机领域英文文献--dblp的食用方法(多图警告)
  20. Halcon_二维测量_Apply_bead_inspection_model

热门文章

  1. 【MFC】CTabSheet类之再改造
  2. java 调用存储过程无反应_java调用存储过程无法取得返回参数
  3. Revel 企业级 Go 应用开发框架
  4. JavaWeb课堂笔记
  5. 安装黑苹果目前最详细教程
  6. RxView学习及实现按钮防抖功能
  7. Cisco Packet Tracer路由器ip简单配置(网关)
  8. 行业寒冬之下,房多多赴美上市能否安然过冬?
  9. Pytest + Allure 测试报告定制
  10. 河套学院2018级计算机一级考试,河套学院2018-2019学年本科生就业率