多起點(diǎn)局部搜索(multi-start local search)
Iterated local search(迭代局部搜索)
- 優(yōu)化
- 用于擾動(dòng)的double_bridge_move函數(shù);
- 產(chǎn)生隨機(jī)初始解的方法、delta數(shù)組的計(jì)算;
- 將距離表示為double型以提高精度;
局部搜索 解決最優(yōu)化 是啟發(fā)式算法
- 啟發(fā)式算法來退而求其次尋找次優(yōu)解或近似最優(yōu)解,局部搜索就是其中一種。它是一種近似算法(Approximate algorithms)
- 鄰域動(dòng)作的定義以及選擇鄰居解的策略
- 鄰域動(dòng)作:
針對當(dāng)前解s,產(chǎn)生s對應(yīng)的鄰居解的一個(gè)集合。
- 鄰域動(dòng)作:
對于一個(gè)bool型問題,其當(dāng)前解為:s = 1001,
當(dāng)將鄰域動(dòng)作定義為翻轉(zhuǎn)其中一個(gè)bit時(shí),
得到的鄰居解的集合N(s)={0001,1101,1011,1000},其中N(s) ∈ S。
同理,當(dāng)將鄰域動(dòng)作定義為互換相鄰bit時(shí),
得到的鄰居解的集合N(s)={0101,1001,1010}.
簡單局部搜索
- 爬山法
- 模擬退火
- 禁忌搜索算法
迭代局部搜索
在局部搜索得到的局部最優(yōu)解上,加入了擾動(dòng),然后再重新進(jìn)行局部搜索

image.png

image.png

image.png
求解TSP旅行商問題

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png
////////////////////////
//TSP問題 迭代局部搜索求解代碼
//基于Berlin52例子求解
//作者:infinitor
//時(shí)間:2018-04-12
//修改:舟寒丶
//修改時(shí)間:2019-12-2
////////////////////////
#include <iostream>
#include <cmath>
#include <stdlib.h>
#include <time.h>
#include <vector>
#include <windows.h>
#include <memory.h>
#include <string.h>
#include <iomanip>
#define DEBUG
using namespace std;
#define CITY_SIZE 52 //城市數(shù)量
//城市坐標(biāo)
typedef struct candidate
{
int x;
int y;
}city, CITIES;
//優(yōu)化值
double **Delta;
//解決方案
typedef struct Solution
{
int permutation[CITY_SIZE]; //城市排列
double cost; //該排列對應(yīng)的總路線長度
}SOLUTION;
// 計(jì)算鄰域操作優(yōu)化值
double calc_delta(int i, int k, int *tmp, CITIES * cities);
//計(jì)算兩個(gè)城市間距離
double distance_2city(city c1, city c2);
//根據(jù)產(chǎn)生的城市序列,計(jì)算旅游總距離
double cost_total(int * cities_permutation, CITIES * cities);
//獲取隨機(jī)城市排列, 用于產(chǎn)生初始解
void random_permutation(int * cities_permutation);
//顛倒數(shù)組中下標(biāo)begin到end的元素位置, 用于two_opt鄰域動(dòng)作
void swap_element(int *p, int begin, int end);
//鄰域動(dòng)作 反轉(zhuǎn)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);
//判斷接受準(zhǔn)則
bool AcceptanceCriterion(int *cities_permutation, int *new_cities_permutation, CITIES * p_cities);
//將城市序列分成4塊,然后按塊重新打亂順序。
//用于擾動(dòng)函數(shù)
void double_bridge_move(int *cities_permutation, int * new_cities_permutation,CITIES * cities);
//擾動(dòng)
void perturbation(CITIES * cities, SOLUTION &best_solution, SOLUTION ¤t_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];
//城市坐標(biāo)數(shù)組
CITIES cities[CITY_SIZE];
//berlin52城市坐標(biāo),最優(yōu)解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;
//初始化指針數(shù)組
Delta = new double*[CITY_SIZE];
for (int i = 0; i < CITY_SIZE; i ++)
Delta[i] = new double[CITY_SIZE];
SOLUTION best_solution;
iterated_local_search(best_solution, berlin52, max_iterations, max_no_improve);
cout << endl<<endl<<"搜索完成!最優(yōu)路線總長度 = " << best_solution.cost << endl;
cout << "最優(yōu)訪問城市序列如下:" << endl;
for (int i = 0; i < CITY_SIZE;i++)
{
cout << setw(4) << setiosflags(ios::left) << best_solution.permutation[i];
}
cout << endl << endl;
return 0;
}
//計(jì)算兩個(gè)城市間距離
double distance_2city(city c1, city c2)
{
double distance = 0;
distance = sqrt((double)((c1.x - c2.x)*(c1.x - c2.x) + (c1.y - c2.y)*(c1.y - c2.y)));
return distance;
}
//根據(jù)產(chǎn)生的城市序列,計(jì)算旅游總距離
//所謂城市序列,就是城市先后訪問的順序,比如可以先訪問ABC,也可以先訪問BAC等等
//訪問順序不同,那么總路線長度也是不同的
//p_perm 城市序列參數(shù)
double cost_total(int * cities_permutation, CITIES * cities)
{
double total_distance = 0;
int c1, c2;
//逛一圈,看看最后的總距離是多少
for (int i = 0; i < CITY_SIZE; i++)
{
c1 = cities_permutation[i];
if (i == CITY_SIZE - 1) //最后一個(gè)城市和第一個(gè)城市計(jì)算距離
{
c2 = cities_permutation[0];
}
else
{
c2 = cities_permutation[i + 1];
}
total_distance += distance_2city(cities[c1], cities[c2]);
}
return total_distance;
}
//獲取隨機(jī)城市排列
void random_permutation(int * cities_permutation)
{
int n=CITY_SIZE;
int temp[CITY_SIZE];
for(int i=0;i<CITY_SIZE;i++)
temp[i]=i;
for(int i=0;i<CITY_SIZE-1;i++)
{
int r=rand()%n;
cities_permutation[i]=temp[r];
temp[r]=temp[n-1];
n--;
}
cities_permutation[CITY_SIZE-1]=temp[0];
}
//顛倒數(shù)組中下標(biāo)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--;
}
}
//鄰域動(dòng)作 反轉(zhuǎn)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);
}
double calc_delta(int i, int k, int *tmp, CITIES * cities)
{
/*
以下計(jì)算說明:
對于每個(gè)方案,翻轉(zhuǎn)以后沒必要再次重新計(jì)算總距離
只需要在翻轉(zhuǎn)的頭尾做個(gè)小小處理
比如:
有城市序列 1-2-3-4-5 總距離 = d12 + d23 + d34 + d45 + d51 = A
翻轉(zhuǎn)后的序列 1-4-3-2-5 總距離 = d14 + d43 + d32 + d25 + d51 = B
由于 dij 與 dji是一樣的,所以B也可以表示成 B = A - d12 - d45 + d14 + d25
下面的優(yōu)化就是基于這種原理
*/
double delta=0;
if((i==0)&&(k==CITY_SIZE-1))
delta=0;
else
{
int i2=(i-1+CITY_SIZE)%CITY_SIZE;
int k2=(k+1)%CITY_SIZE;
delta = 0
- distance_2city(cities[tmp[i2]], cities[tmp[i]])
+ distance_2city(cities[tmp[i2]], cities[tmp[k]])
- distance_2city(cities[tmp[k]], cities[tmp[k2]])
+ distance_2city(cities[tmp[i]], cities[tmp[k2]]);
}
return delta;
}
/*
去重處理,對于Delta數(shù)組來說,對于城市序列1-2-3-4-5-6-7-8-9-10,如果對3-5應(yīng)用了鄰域操作2-opt , 事實(shí)上對于
7-10之間的翻轉(zhuǎn)是不需要重復(fù)計(jì)算的。所以用Delta提前預(yù)處理一下。
當(dāng)然由于這里的計(jì)算本身是O(1) 的,事實(shí)上并沒有帶來時(shí)間復(fù)雜度的減少(更新操作反而增加了復(fù)雜度)
如果delta計(jì)算 是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最優(yōu)解
//current_solution當(dāng)前解
void local_search(SOLUTION & best_solution, CITIES * cities, int max_no_improve)
{
int count = 0;
int i, k;
double inital_cost = best_solution.cost; //初始花費(fèi)
double 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++)
{
//鄰域動(dòng)作
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 reset
for (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);
}
//判斷接受準(zhǔn)則
bool AcceptanceCriterion(int *cities_permutation, int *new_cities_permutation, CITIES * cities)
{
int AcceptLimite=500;
int c1=cost_total(cities_permutation, cities);
int c2=cost_total(new_cities_permutation, cities)-500;
if (c1<c2)
return false;
else
return true;
}
//將城市序列分成4塊,然后按塊重新打亂順序。
//用于擾動(dòng)函數(shù)
void double_bridge_move(int *cities_permutation, int * new_cities_permutation,CITIES * cities)
{
/*對這一段函數(shù),我們提出修改。因?yàn)閷?shí)際上按原代碼的方法并不能將
第一大塊有效的擾動(dòng)。同時(shí),固定的擾動(dòng)方法在第二次擾動(dòng)后會(huì)大致恢
復(fù)原來的情形,只有少部分改變。我們提出一種將位置標(biāo)簽pos放入數(shù)組,
再進(jìn)行隨記排序來擾動(dòng)的方案。同時(shí)去掉vector,這太傻了。
再補(bǔ)充一下,我認(rèn)為除3比除4更好。
順便修改了一下acceptance。*/
int pos[5];
pos[0]= 0;
pos[1]= rand()%(CITY_SIZE/3)+1;
pos[2]= rand()%(CITY_SIZE/3)+CITY_SIZE/3;
pos[3]= rand()%(CITY_SIZE/3)+(CITY_SIZE/3)*2;
pos[4]= CITY_SIZE;
int n=4;
int random_order[4],temp[4];
for(int i=0;i<4;i++)
temp[i]=i;
for(int i=0;i<3;i++)
{
int r=rand()%n;
random_order[i]=temp[r];
temp[r]=temp[n-1];
n--;
}
random_order[3]=temp[0];
int deadprotect=0;
do
{
int i=0;
for (int j=pos[random_order[0]];j<pos[random_order[0]+1];j++)
{
new_cities_permutation[i]=cities_permutation[j];
i++;
}
for (int j=pos[random_order[1]];j<pos[random_order[1]+1];j++)
{
new_cities_permutation[i]=cities_permutation[j];
i++;
}
for (int j=pos[random_order[2]];j<pos[random_order[2]+1];j++)
{
new_cities_permutation[i]=cities_permutation[j];
i++;
}
for (int j=pos[random_order[3]];j<pos[random_order[3]+1];j++)
{
new_cities_permutation[i]=cities_permutation[j];
i++;
}
deadprotect++;
//cout<<deadprotect;
//cout<<endl;
if (deadprotect==5) break;
}while(AcceptanceCriterion(cities_permutation, new_cities_permutation, cities));
}
//擾動(dòng)
void perturbation(CITIES * cities, SOLUTION &best_solution, SOLUTION ¤t_solution)
{
double_bridge_move(best_solution.permutation, current_solution.permutation,berlin52);
current_solution.cost = cost_total(current_solution.permutation, cities);
}
//迭代搜索
//max_iterations用于迭代搜索次數(shù)
//max_no_improve用于局部搜索邊界條件
void iterated_local_search(SOLUTION & best_solution, CITIES * cities, int max_iterations, int max_no_improve)
{
SOLUTION *current_solution = new SOLUTION;
//獲得初始隨機(jī)解
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); //擾動(dòng)+判斷是否接受新解
local_search(*current_solution, cities, max_no_improve);//繼續(xù)局部搜索
//找到更優(yōu)解
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" << "最優(yōu)解 = " << best_solution.cost << " 當(dāng)前解 = " << current_solution->cost << endl;
}
}