程序运行截图
操作视频
扩展阅读
TSP问题
旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
简介
TSP问题是一个组合优化问题。该问题可以被证明具有NPC计算复杂性。因此,任何能使该问题的求解得以简化的方法,都将受到高度的评价和关注。
旅行推销员问题是图论中最著名的问题之一,即“已给一个n个点的完全图,每条边都有一个长度,求总长度最短的经过每个顶点正好一次的封闭回路”。Edmonds,Cook和Karp等人发现,这批难题有一个值得注意的性质,对其中一个问题存在有效算法时,每个问题都会有有效算法。 [1]
迄今为止,这类问题中没有一个找到有效算法。倾向于接受NP完全问题(NP-Complete或NPC)和NP难题(NP-Hard或NPH)不存在有效算法这一猜想,认为这类问题的大型实例不能用精确算法求解,必须寻求这类问题的有效的近似算法。
此类问题中,经典的还有 子集和问题; Hamilton回路问题;最大团问题。
非对称和对称
在对称TSP问题中,两座城市之间来回的距离是相等的,形成一个无向图。这种对称性将解的数量减少了一半。在非对称TSP问题中,可能不是双向的路径都存在,或是来回的距离不同,形成了有向图。交通事故、单行道和出发与到达某些城市机票价格不同等都是打破这种对称性的例子。
相关问题
1.图论中的一个等价形式是:给定一个加权完全图(顶点表示城市,边表示道路,权重就会是道路的成本或距离), 求一权值最小的哈密尔顿回路。
2. 返回到起始城市的要求不会改变问题的计算复杂度,见哈密顿路径问题。
3. 另一个相关问题是瓶颈旅行商问题(bottleneck TSP):求加权图中权重最大的边最小的哈密尔顿回路。问题在运输和物流之外都有相当广泛的实际意义。一个典型的例子是在印刷电路板制造中:规划打孔机在PCB版上钻孔的路线。在机械加工或钻孔应用中,“城市”是需要加工的部分或需要钻的(不同大小)的孔,而“旅行成本”包括更换机具所用的时间(单机作业排序问题)。
4. 广义旅行商问题,又称“旅行政客问题”,处理“国家”中有(一个或多个)“城市”,而旅行商需要在每个“国家”访问恰好一座“城市”。其中一种应用是在求解下料问题时,想要最小化刀具改变次数中。另一种应用与半导体制造业中的打孔有关。令人惊喜的是,Behzad与Modarres证明了广义旅行商问题可以转化为相同城市数量的标准旅行商问题 ,只是要改变距离矩阵。 [2]
优先顺序旅行推销员问题处理城市之间存在访问次序的问题。
5. 旅行购买者问题涉及购买一系列产品的购买者。他可以在若干城市购买这些产品,但价格会有不同,也不是所有城市都有售相同的商品。目标是在城市的子集中间找到一条路径,使得总成本(旅行成本 + 购买成本)最小,并且能够买到所有需求的商品。
部分源码
// 同时运行遗传算法的函数
void GA()
{
// 标准参数
unsigned int pop_size = 1000;//种群规模
double mutation_rate = 0.001;//变异概率
double crossover_rate = 0.25;//交叉概率
unsigned elitism_count = 10;//精英数
unsigned tournament_size = 500;//比赛人数
unsigned max_generations = 2000;//最大迭代次数
unsigned cull_count = 10;//剔除数
unsigned chromosome_size = cities_list.size();//染色体长度:城市节点数
unsigned generation;//代
GAPopulation pop (pop_size, chromosome_size, &cities_list);//实例化种群:1000人,染色体长度,城市列表
auto start_time = std::chrono::high_resolution_clock::now();//计时开始
generation = 1;//第一代
while (generation < max_generations) {
std::cout << "Generation: " << generation;
GAIndividual localBest = pop.getFittest();//局部最佳
mutex.lock();//多个线程同时运行访问全局变量best,加互斥锁
if (best < localBest)
best = localBest;//更新全局最佳,路径最短
else//全局最佳比局部最佳还要好
pop.insertIndividual(best);//插入局部全局最佳
mutex.unlock();
/*
// Print fittest individual
std::cout << "Best solution: "
<< pop.bestDescription() << " "
<< pop.bestFitness() << std::endl;
*/
std::cout << " Best fitness: " << pop.bestFitness() << std::endl;//输出种群最佳适应度值
pop.mutate(mutation_rate);//种群一定概率变异
pop = GAPopulation(pop, crossover_rate, elitism_count, tournament_size, cull_count);//迭代种群:留下精英,剔除尾巴(新的随机个体取代),其余随机找个伴侣交叉。
++generation;
}
// 完成后停止计时器
auto end_time = std::chrono::high_resolution_clock::now();
// 通知结束统计
std::cout << "Found solution in " << generation << " generations" << std::endl;//迭代次数
std::cout << "Best solution: " << pop.bestDescription() << std::endl;//最佳染色体描述
std::cout << "Fitness: " << pop.bestFitness() << std::endl;//最佳适应度
// Inform how long it took to run things
auto dur = std::chrono::duration<double>(end_time - start_time);//迭代持续时间
std::cout << "Algorithm ran for " << dur.count() << " seconds" << std::endl;
}
// Function to join each thread in the list and report results
//加入列表中每个线程 并报告结果的函数
void wrapUp (std::vector<std::thread> &threads)
{
for (auto &thread : threads)
thread.join();//等待所有线程结束
std::cout << "Absolute best solution: "
<< best.chromosomeAsString()
<< " " << best.fitness() << std::endl;//输出最佳染色体描述和最佳适应度值
}
参考
https://baike.baidu.com/item/TSP%E9%97%AE%E9%A2%98/840008
The End