实验9 高级搜索技术1
一、实验目的
(1)掌握高级搜索技术的相关理论,能根据实际情况选取合适的搜索方法;
(2)进一步熟悉爬山法搜索技术,掌握其在搜索过程中的优缺点;
(3)掌握模拟退火搜索的思想,能针对实际问题选取合适的温度;
(4)运用爬山法、模拟退火方法解决TSP问题,并对比两种方法的优缺点。
二、实验内容
1、现有一个商人,准备从广州出发,经过广东省的各个城市再回到广州,每个城市只经过一次,城市分布情况见图1所示。请使用模拟退火搜索算法和遗传算法寻找一条线路,使得商人按上述要求走过的路径之和最短,并比较两种方法所使用的时间和最终的行走路径的优越性。各个城市之间的距离可通过上网搜索确定,此处给出两种表示方法。
图1 广东省市级城市分布图
String[] citys={"广州","佛山","东莞","中山","珠海","深圳","惠州","河源","汕尾","梅州","潮州","汕头","揭阳","清远","韶关","云浮","茂名","湛江","阳江","江门","肇庆"};
int[][] Dist={
{max ,22 ,90 ,86 ,133 ,147 ,148 ,228 ,272 ,426 ,490 ,527 ,465 ,63 ,180 ,145 ,371 ,488 ,225 ,102 ,109 },//广州到各市距离
{22 ,max ,112 ,78 ,128 ,169 ,170 ,250 ,286 ,456 ,520 ,557 ,495 ,85 ,229 ,132 ,349 ,466 ,198 ,66 ,87 },//佛山到各市距离
{90 ,112 ,max ,92 ,132 ,57 ,162 ,134 ,207 ,340 ,404 ,441 ,379 ,153 ,297 ,202 ,461 ,578 ,274 ,122 ,199 },//东莞到各市距离
{86 ,78 ,92 ,max ,25 ,121 ,161 ,240 ,265 ,409 ,420 ,425 ,385 ,158 ,309 ,191 ,317 ,396 ,201 ,43 ,144 },//中山到各市距离
{133 ,128 ,132 ,25 ,max ,162 ,195 ,282 ,307 ,452 ,462 ,467 ,427 ,202 ,354 ,230 ,328 ,407 ,199 ,87 ,193 },//珠海到各市距离
{147 ,169 ,57 ,121 ,162 ,max ,90 ,177 ,169 ,343 ,351 ,333 ,317 ,198 ,324 ,265 ,420 ,499 ,303 ,144 ,218 },//深圳到各市距离
{148 ,170 ,162 ,161 ,195 ,90 ,max ,87 ,130 ,259 ,284 ,289 ,249 ,188 ,271 ,278 ,452 ,531 ,335 ,180 ,231 },//惠州到各市距离
{228 ,250 ,134 ,240 ,282 ,177 ,87 ,max ,219 ,190 ,269 ,286 ,246 ,211 ,251 ,335 ,534 ,613 ,418 ,266 ,288 },//河源到各市距离
{272 ,286 ,207 ,265 ,307 ,169 ,130 ,219 ,max ,221 ,200 ,182 ,165 ,317 ,400 ,407 ,568 ,647 ,452 ,293 ,360 },//汕尾到各市距离
{426 ,456 ,340 ,409 ,452 ,343 ,259 ,190 ,221 ,max ,135 ,155 ,110 ,398 ,360 ,522 ,708 ,787 ,592 ,436 ,475 },//梅州到各市距离
{490 ,520 ,404 ,420 ,462 ,351 ,284 ,269 ,200 ,135 ,max ,49 ,29 ,421 ,439 ,545 ,718 ,797 ,601 ,446 ,498 },//潮州到各市距离
{527 ,557 ,441 ,425 ,467 ,333 ,289 ,286 ,182 ,155 ,49 ,max ,46 ,426 ,466 ,550 ,723 ,802 ,606 ,450 ,503 },//汕头到各市距离
{465 ,495 ,379 ,385 ,427 ,317 ,249 ,246 ,165 ,110 ,29 ,46 ,max ,387 ,413 ,511 ,684 ,763 ,567 ,412 ,464 },//揭阳到各市距离
{63 ,85 ,153 ,158 ,202 ,198 ,188 ,211 ,317 ,398 ,421 ,426 ,387 ,max ,168 ,171 ,379 ,463 ,286 ,151 ,123 },//清远到各市距离
{180 ,229 ,297 ,309 ,354 ,324 ,271 ,251 ,400 ,360 ,439 ,466 ,413 ,168 ,max ,323 ,531 ,615 ,436 ,301 ,293 },//韶关到各市距离
{145 ,132 ,202 ,191 ,230 ,265 ,278 ,335 ,407 ,522 ,545 ,550 ,511 ,171 ,323 ,max ,244 ,328 ,182 ,148 ,56 },//云浮到各市距离
{371 ,349 ,461 ,317 ,328 ,420 ,452 ,534 ,568 ,708 ,718 ,723 ,684 ,379 ,531 ,244 ,max ,96 ,130 ,275 ,267 },//茂名到各市距离
{488 ,466 ,578 ,396 ,407 ,499 ,531 ,613 ,647 ,787 ,797 ,802 ,763 ,463 ,615 ,328 ,96 ,max ,208 ,354 ,348 },//湛江到各市距离
{225 ,198 ,274 ,201 ,199 ,303 ,335 ,418 ,452 ,592 ,601 ,606 ,567 ,286 ,436 ,182 ,130 ,208 ,max ,158 ,201 },//阳江到各市距离
{102 ,66 ,122 ,43 ,87 ,144 ,180 ,266 ,293 ,436 ,446 ,450 ,412 ,151 ,301 ,148 ,275 ,354 ,158 ,max ,104 },//江门到各市距离
{109 ,87 ,199 ,144 ,193 ,218 ,231 ,288 ,360 ,475 ,498 ,503 ,464 ,123 ,293 ,56 ,267 ,348 ,201 ,104 ,max }//肇庆到各市距离
};
# 21个城市的坐标
city_location = [(113.28, 23.12, '广州市'),
(113.59, 24.80, '韶关市'),
(114.08, 22.54, '深圳市'),
(113.55, 22.22, '珠海市'),
(116.70, 23.37, '汕头市'),
(113.12, 23.02, '佛山市'),
(113.09, 22.59, '江门市'),
(110.36, 21.27, '湛江市'),
(110.91, 21.65, '茂名市'),
(112.47, 23.05, '肇庆市'),
(114.41, 23.07, '惠州市'),
(116.11, 24.29, '梅州市'),
(115.36, 22.77, '汕尾市'),
(114.69, 23.74, '河源市'),
(111.97, 21.85, '阳江市'),
(113.05, 23.68, '清远市'),
(113.74, 23.04, '东莞市'),
(113.38, 22.52, '中山市'),
(116.63, 23.66, '潮州市'),
(116.35, 23.54, '揭阳市'),
(112.04, 22.92, '云浮市')
]
模拟退火算法:
#include<iostream>
#include<vector>
#include<string>
#include <queue>
#include <map>
#include<algorithm>
#include <chrono>
using namespace std;
#define SIZE 21
string citys[SIZE] = { "广州","佛山","东莞","中山","珠海","深圳","惠州","河源","汕尾","梅州","潮州","汕头","揭阳","清远","韶关","云浮","茂名","湛江","阳江","江门","肇庆" };
int Dist[21][21] = {
{INT_MAX ,22 ,90 ,86 ,133 ,147 ,148 ,228 ,272 ,426 ,490 ,527 ,465 ,63 ,180 ,145 ,371 ,488 ,225 ,102 ,109 },//广州到各市距离
{22 ,INT_MAX ,112 ,78 ,128 ,169 ,170 ,250 ,286 ,456 ,520 ,557 ,495 ,85 ,229 ,132 ,349 ,466 ,198 ,66 ,87 },//佛山到各市距离
{90 ,112 ,INT_MAX ,92 ,132 ,57 ,162 ,134 ,207 ,340 ,404 ,441 ,379 ,153 ,297 ,202 ,461 ,578 ,274 ,122 ,199 },//东莞到各市距离
{86 ,78 ,92 ,INT_MAX ,25 ,121 ,161 ,240 ,265 ,409 ,420 ,425 ,385 ,158 ,309 ,191 ,317 ,396 ,201 ,43 ,144 },//中山到各市距离
{133 ,128 ,132 ,25 ,INT_MAX ,162 ,195 ,282 ,307 ,452 ,462 ,467 ,427 ,202 ,354 ,230 ,328 ,407 ,199 ,87 ,193 },//珠海到各市距离
{147 ,169 ,57 ,121 ,162 ,INT_MAX ,90 ,177 ,169 ,343 ,351 ,333 ,317 ,198 ,324 ,265 ,420 ,499 ,303 ,144 ,218 },//深圳到各市距离
{148 ,170 ,162 ,161 ,195 ,90 ,INT_MAX ,87 ,130 ,259 ,284 ,289 ,249 ,188 ,271 ,278 ,452 ,531 ,335 ,180 ,231 },//惠州到各市距离
{228 ,250 ,134 ,240 ,282 ,177 ,87 ,INT_MAX ,219 ,190 ,269 ,286 ,246 ,211 ,251 ,335 ,534 ,613 ,418 ,266 ,288 },//河源到各市距离
{272 ,286 ,207 ,265 ,307 ,169 ,130 ,219 ,INT_MAX ,221 ,200 ,182 ,165 ,317 ,400 ,407 ,568 ,647 ,452 ,293 ,360 },//汕尾到各市距离
{426 ,456 ,340 ,409 ,452 ,343 ,259 ,190 ,221 ,INT_MAX ,135 ,155 ,110 ,398 ,360 ,522 ,708 ,787 ,592 ,436 ,475 },//梅州到各市距离
{490 ,520 ,404 ,420 ,462 ,351 ,284 ,269 ,200 ,135 ,INT_MAX,49 ,29 ,421 ,439 ,545 ,718 ,797 ,601 ,446 ,498 },//潮州到各市距离
{527 ,557 ,441 ,425 ,467 ,333 ,289 ,286 ,182 ,155 ,49 ,INT_MAX ,46 ,426 ,466 ,550 ,723 ,802 ,606 ,450 ,503 },//汕头到各市距离
{465 ,495 ,379 ,385 ,427 ,317 ,249 ,246 ,165 ,110 ,29 ,46 ,INT_MAX ,387 ,413 ,511 ,684 ,763 ,567 ,412 ,464 },//揭阳到各市距离
{63 ,85 ,153 ,158 ,202 ,198 ,188 ,211 ,317 ,398 ,421 ,426 ,387 ,INT_MAX ,168 ,171 ,379 ,463 ,286 ,151 ,123 },//清远到各市距离
{180 ,229 ,297 ,309 ,354 ,324 ,271 ,251 ,400 ,360 ,439 ,466 ,413 ,168 ,INT_MAX ,323 ,531 ,615 ,436 ,301 ,293 },//韶关到各市距离
{145 ,132 ,202 ,191 ,230 ,265 ,278 ,335 ,407 ,522 ,545 ,550 ,511 ,171 ,323 ,INT_MAX,244 ,328 ,182 ,148 ,56 },//云浮到各市距离
{371 ,349 ,461 ,317 ,328 ,420 ,452 ,534 ,568 ,708 ,718 ,723 ,684 ,379 ,531 ,244 ,INT_MAX ,96 ,130 ,275 ,267 },//茂名到各市距离
{488 ,466 ,578 ,396 ,407 ,499 ,531 ,613 ,647 ,787 ,797 ,802 ,763 ,463 ,615 ,328 ,96 ,INT_MAX ,208 ,354 ,348 },//湛江到各市距离
{225 ,198 ,274 ,201 ,199 ,303 ,335 ,418 ,452 ,592 ,601 ,606 ,567 ,286 ,436 ,182 ,130 ,208 ,INT_MAX ,158 ,201 },//阳江到各市距离
{102 ,66 ,122 ,43 ,87 ,144 ,180 ,266 ,293 ,436 ,446 ,450 ,412 ,151 ,301 ,148 ,275 ,354 ,158 ,INT_MAX ,104 },//江门到各市距离
{109 ,87 ,199 ,144 ,193 ,218 ,231 ,288 ,360 ,475 ,498 ,503 ,464 ,123 ,293 ,56 ,267 ,348 ,201 ,104 ,INT_MAX }//肇庆到各市距离
};
//计算路径长度
int pathLength(vector<int>& path) {
int length = 0;
for (int i = 0; i < path.size() - 1; i++) {
int src = path[i];
int des = path[i + 1];
length += Dist[src][des];
}
length += Dist[0][path[0]];
length += Dist[path[path.size() - 1]][0];
return length;
}
//模拟退火算法
vector<int> simulatedAnnealing(double temperature, double coolingRate, int maxIterations) {
srand(time(0)); //设置随机数种子
//随机生成初解
vector<int>currentPath;
for (int i = 1; i < SIZE; i++) {
currentPath.push_back(i);
}
random_shuffle(currentPath.begin(), currentPath.end()); //随机打乱
int currentLengh = pathLength(currentPath);
vector<int>bestPath = currentPath; //记录最优解
int bestLength = currentLengh;
//迭代扰动产生新解,随机交换两个位置
while (temperature > 1e-8 && maxIterations) {
int x = rand() % (SIZE - 1);
int y = rand() % (SIZE - 1);
while (x == y) y = rand() % (SIZE - 1);
swap(currentPath[x], currentPath[y]);
int newLength = pathLength(currentPath);
//如果新路径更短或者根据Metropolis准则决定是否接受新解
if (newLength < currentLengh || (rand() / (double)RAND_MAX) < (1 / (1 + exp(-1 * (newLength - currentLengh) / temperature)))) {
currentLengh = newLength;
if (currentLengh < bestLength) {
bestLength = currentLengh;
bestPath = currentPath;
}
}
else { //不接受
swap(currentPath[x], currentPath[y]);
}
maxIterations--; //减少迭代次数
temperature *= coolingRate; //降温
}
//返回最优解
return bestPath;
}
int main() {
auto start_time = chrono::high_resolution_clock::now();
// 参数设置
double initialTemperature = 1000.0; //初始温度
double coolingRate = 0.99; //冷却率
int maxIterations = 10000; //最多迭代次数
// 使用模拟退火搜索算法求解最优路径
vector<int> bestPath = simulatedAnnealing(initialTemperature, coolingRate, maxIterations);
cout << "Best path: ";
for (int i = 0; i < bestPath.size(); ++i) {
cout << citys[bestPath[i]] << " ";
}
cout << "广州" << endl;
cout << "Path length: " << pathLength(bestPath) << endl;
//计算运行时间
auto end_time = std::chrono::high_resolution_clock::now();
auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
std::cout << "程序执行时间为:" << elapsed_time.count() << "毫秒" << std::endl;
return 0;
}
结果:
遗传算法解决:
#include<iostream>
#include<vector>
#include<string>
#include <queue>
#include <map>
#include<algorithm>
#include <chrono>
using namespace std;
#define SIZE 21 //城市个数
#define POPULATION_SIZE 50 //种群规模
#define MAXITERATIONS 1000 //最大迭代次数
#define MUTATION_RATE 0.01 //变异率
#define ELITE_SIZE 5 //精英个数
string citys[SIZE] = {"广州","佛山","东莞","中山","珠海","深圳","惠州","河源","汕尾","梅州","潮州","汕头","揭阳","清远","韶关","云浮","茂名","湛江","阳江","江门","肇庆"};
int Dist[21][21] = {
{INT_MAX ,22 ,90 ,86 ,133 ,147 ,148 ,228 ,272 ,426 ,490 ,527 ,465 ,63 ,180 ,145 ,371 ,488 ,225 ,102 ,109 },//广州到各市距离
{22 ,INT_MAX ,112 ,78 ,128 ,169 ,170 ,250 ,286 ,456 ,520 ,557 ,495 ,85 ,229 ,132 ,349 ,466 ,198 ,66 ,87 },//佛山到各市距离
{90 ,112 ,INT_MAX ,92 ,132 ,57 ,162 ,134 ,207 ,340 ,404 ,441 ,379 ,153 ,297 ,202 ,461 ,578 ,274 ,122 ,199 },//东莞到各市距离
{86 ,78 ,92 ,INT_MAX ,25 ,121 ,161 ,240 ,265 ,409 ,420 ,425 ,385 ,158 ,309 ,191 ,317 ,396 ,201 ,43 ,144 },//中山到各市距离
{133 ,128 ,132 ,25 ,INT_MAX ,162 ,195 ,282 ,307 ,452 ,462 ,467 ,427 ,202 ,354 ,230 ,328 ,407 ,199 ,87 ,193 },//珠海到各市距离
{147 ,169 ,57 ,121 ,162 ,INT_MAX ,90 ,177 ,169 ,343 ,351 ,333 ,317 ,198 ,324 ,265 ,420 ,499 ,303 ,144 ,218 },//深圳到各市距离
{148 ,170 ,162 ,161 ,195 ,90 ,INT_MAX ,87 ,130 ,259 ,284 ,289 ,249 ,188 ,271 ,278 ,452 ,531 ,335 ,180 ,231 },//惠州到各市距离
{228 ,250 ,134 ,240 ,282 ,177 ,87 ,INT_MAX ,219 ,190 ,269 ,286 ,246 ,211 ,251 ,335 ,534 ,613 ,418 ,266 ,288 },//河源到各市距离
{272 ,286 ,207 ,265 ,307 ,169 ,130 ,219 ,INT_MAX ,221 ,200 ,182 ,165 ,317 ,400 ,407 ,568 ,647 ,452 ,293 ,360 },//汕尾到各市距离
{426 ,456 ,340 ,409 ,452 ,343 ,259 ,190 ,221 ,INT_MAX ,135 ,155 ,110 ,398 ,360 ,522 ,708 ,787 ,592 ,436 ,475 },//梅州到各市距离
{490 ,520 ,404 ,420 ,462 ,351 ,284 ,269 ,200 ,135 ,INT_MAX,49 ,29 ,421 ,439 ,545 ,718 ,797 ,601 ,446 ,498 },//潮州到各市距离
{527 ,557 ,441 ,425 ,467 ,333 ,289 ,286 ,182 ,155 ,49 ,INT_MAX ,46 ,426 ,466 ,550 ,723 ,802 ,606 ,450 ,503 },//汕头到各市距离
{465 ,495 ,379 ,385 ,427 ,317 ,249 ,246 ,165 ,110 ,29 ,46 ,INT_MAX ,387 ,413 ,511 ,684 ,763 ,567 ,412 ,464 },//揭阳到各市距离
{63 ,85 ,153 ,158 ,202 ,198 ,188 ,211 ,317 ,398 ,421 ,426 ,387 ,INT_MAX ,168 ,171 ,379 ,463 ,286 ,151 ,123 },//清远到各市距离
{180 ,229 ,297 ,309 ,354 ,324 ,271 ,251 ,400 ,360 ,439 ,466 ,413 ,168 ,INT_MAX ,323 ,531 ,615 ,436 ,301 ,293 },//韶关到各市距离
{145 ,132 ,202 ,191 ,230 ,265 ,278 ,335 ,407 ,522 ,545 ,550 ,511 ,171 ,323 ,INT_MAX,244 ,328 ,182 ,148 ,56 },//云浮到各市距离
{371 ,349 ,461 ,317 ,328 ,420 ,452 ,534 ,568 ,708 ,718 ,723 ,684 ,379 ,531 ,244 ,INT_MAX ,96 ,130 ,275 ,267 },//茂名到各市距离
{488 ,466 ,578 ,396 ,407 ,499 ,531 ,613 ,647 ,787 ,797 ,802 ,763 ,463 ,615 ,328 ,96 ,INT_MAX ,208 ,354 ,348 },//湛江到各市距离
{225 ,198 ,274 ,201 ,199 ,303 ,335 ,418 ,452 ,592 ,601 ,606 ,567 ,286 ,436 ,182 ,130 ,208 ,INT_MAX ,158 ,201 },//阳江到各市距离
{102 ,66 ,122 ,43 ,87 ,144 ,180 ,266 ,293 ,436 ,446 ,450 ,412 ,151 ,301 ,148 ,275 ,354 ,158 ,INT_MAX ,104 },//江门到各市距离
{109 ,87 ,199 ,144 ,193 ,218 ,231 ,288 ,360 ,475 ,498 ,503 ,464 ,123 ,293 ,56 ,267 ,348 ,201 ,104 ,INT_MAX }//肇庆到各市距离
};
//计算路径长度
int pathLength(vector<int>& path) {
int length = 0;
for (int i = 0; i < path.size() - 1; i++) {
int src = path[i];
int des = path[i + 1];
length += Dist[src][des];
}
length += Dist[0][path[0]];
length += Dist[path[path.size()-1]][0];
return length;
}
//随机创建一个种群
vector<vector<int>> generatePopulation() {
vector<vector<int>> population;
for (int i = 0; i < POPULATION_SIZE; i++) {
vector<int>individual;
for (int j = 1; j < SIZE; j++)
individual.push_back(j);
random_shuffle(individual.begin(), individual.end());
population.push_back(individual);
}
return population;
}
//获取种群中的精英
vector<vector<int>> getElites(vector<vector<int>>& population) {
vector<vector<int>>elites;
sort(population.begin(), population.end(), [](vector<int>& a, vector<int>& b) { return pathLength(a) < pathLength(b); });
for (int i = 0; i < ELITE_SIZE; i++)
elites.push_back(population[i]);
return elites;
}
//交叉,随机获取parent1中部分基因,然后剩下部分由parent2补充
vector<int> crossover(vector<int>& parent1, vector<int>parent2) {
vector<int>child(parent1.size(),-1);
//获取parent1中部分基因
int start = rand() % parent1.size();
int end = rand() % parent1.size();
if (start > end) swap(start, end);
for (int i = start; i <= end; i++)
child[i] = parent1[i];
//获取parent2中的基因
int index = 0;
for (int i = 0; i < parent1.size(); i++) {
if (child[i] == -1) {
while (find(child.begin(), child.end(), parent2[index]) != child.end()) index++;
child[i] = parent2[index];
}
}
return child;
}
//变异
void mutate(vector<int>& individual) {
for (int i = 0; i < SIZE - 1; i++) {
if ((double)rand() / RAND_MAX < MUTATION_RATE) {
int j = rand() % (SIZE - 1);
swap(individual[i], individual[j]);
}
}
}
//遗传算法
vector<int> geneticAlgorithm() {
//创建初始种群
vector<vector<int>> population = generatePopulation();
// 迭代产生新种群,选择、交叉、变异
for(int i=0;i<MAXITERATIONS;i++) {
//选择精英
vector<vector<int>> elites = getElites(population);
//进化生成新种群
vector<vector<int>> new_population;
while (new_population.size() < POPULATION_SIZE) {
//交叉
int x = rand() % ELITE_SIZE;
int y = rand() % ELITE_SIZE;
while(x==y) y = rand() % ELITE_SIZE;
vector<int>child = crossover(elites[x], elites[y]);
//变异
mutate(child);
//加入新种群
new_population.push_back(child);
}
population = new_population;
}
//返回最优解
sort(population.begin(), population.end(), [](vector<int>& a, vector<int>& b) { return pathLength(a) < pathLength(b); });
return population[0];
}
int main() {
auto start_time = chrono::high_resolution_clock::now();
srand(time(0)); //设置随机数种子
//运行遗传算法
vector<int> result = geneticAlgorithm();
//输出结果
cout << "Best path: ";
for (int i = 0; i < SIZE-1; i++) {
cout << citys[result[i]] << " ";
}
cout <<"广州" << endl;
cout << "Path length: " << pathLength(result) << endl;
//计算运行时间
auto end_time = std::chrono::high_resolution_clock::now();
auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
std::cout << "程序执行时间为:" << elapsed_time.count() << "毫秒" << std::endl;
return 0;
}
3、实验体会与总结
比较两种方法所使用的时间和最终的行走路径的优越性:
解的质量:
遗传算法找到的路径之和(2689)明显小于模拟退火算法找到的路径之和(4410),说明遗传算法在寻找最优解方面表现更好。
计算时间:
模拟退火算法的计算时间(3毫秒)远小于遗传算法的计算时间(297毫秒)。这可能是因为模拟退火算法在搜索过程中更快地收敛到一个局部最优解,而遗传算法需要更多的迭代来探索解空间并找到全局最优解。
适用性:
模拟退火算法适用于较简单的优化问题,特别是当解空间较小或问题具有单峰特性时。遗传算法适用于更复杂的优化问题,特别是当解空间较大、问题具有多峰特性或需要探索多个局部最优解时。