TSP问题的遗传算法解法
2009-07-14 18:25
447 查看
document.domain = "csdn.net";
TSP
问题的遗传算法解法
TSP
问题的意思是:
给定几个城市,旅行商必须决定一条最短的路线,使他能够访问到每个城市一次,然后返回到他的起点。
TSP
问题是一个非常具有迷惑性的简单问题。它的主要难度来源于此:当城市数目不断增加时,求解问题所需要的计算机力呈指数级增长。这意味着,假设在某台计算机上某个算法可以解决
50
个城市的
TSP
,但你再增加
10
个城市,采用同一个算法,就需要快一千倍的计算机才能解决它。在任何高速的机器上,采用同一算法,都会很快达到它的极限。
TSP
问题的主要特点是:任何解都是问题中所有城市的一个置换或者重新排列,因此,必须确保所有的基因组都代表一个有效的排列,也就是对所有城市的一个有效周游。在这里,我们选择整数表示城市,一个有效的周游就是维数为城市数目,而且元素两两互不相同的一个整数矢量。
1.相关类。
1.1 CmapTSP(类)
为了封装地图数据,城市座标以及适应性计算,创建CmapTSP 类。其中CoOrd是一个保存每个城市的x和y座标的简单结构。
1.2 SGenome(类)
基因组sgenome结构定义如下:
1.3 CgaTSP(类)
这个是遗传算法类。代码太长,只贴出主要的方法的代码。
2 其他需要注意的问题
2.1
选择一个适应性分数
需要一个适应性函数,能够为愈短的周游线路奖励愈高的分数。可以用周游路程长度作为此函数,但这样得到的数值分布的广度不够,使群中最好的和最差的染色体的分数差别不大。因此,采用适用性比例选择法时,使适应性分数高的基因组被选上是相当困难的。一种比较理想的办法是:记下每一代中的最差的周游路线长度,然后再对种群基因组作一轮循环,用最长的路程(即最差的)减去每个基因组的路程,得到的为基因组的适应性分数。这样做可以使结果的相对差异变大,使有轮盘赌选择时也会更有效。这样做也可以从种群中移除最差的染色体,因为它的适应性分数为0,在选择时根本不会被选中。
2.2 杂交和变异
本次选择的杂交方法是部分映射杂交,变异方法是交换变异。它们的具体方法,我会在下一篇《遗传算法优化》中详细讲解。
2.3 选择
这里还是采用轮盘赌选择法,但是仍有一点不同,为了使遗传算法能够较快的收敛,在每一个epoch中,在选择循环前,都就确保将前一代的中适应性最高的2的基因原样复制到新一代中,这代表适应性最好的基本组永远不会在随机过程上丢掉。这一技术通常称之为种子或者精英选择法。
3 本机运行结果
还是那句话,我应该学习一下C++的。
运行中:
![](http://p.blog.csdn.net/images/p_blog_csdn_net/bravetmac/EntryImages/20090714/tsp1.jpg)
运行结果:
TSP
问题的遗传算法解法
TSP
问题的意思是:
给定几个城市,旅行商必须决定一条最短的路线,使他能够访问到每个城市一次,然后返回到他的起点。
TSP
问题是一个非常具有迷惑性的简单问题。它的主要难度来源于此:当城市数目不断增加时,求解问题所需要的计算机力呈指数级增长。这意味着,假设在某台计算机上某个算法可以解决
50
个城市的
TSP
,但你再增加
10
个城市,采用同一个算法,就需要快一千倍的计算机才能解决它。在任何高速的机器上,采用同一算法,都会很快达到它的极限。
TSP
问题的主要特点是:任何解都是问题中所有城市的一个置换或者重新排列,因此,必须确保所有的基因组都代表一个有效的排列,也就是对所有城市的一个有效周游。在这里,我们选择整数表示城市,一个有效的周游就是维数为城市数目,而且元素两两互不相同的一个整数矢量。
1.相关类。
1.1 CmapTSP(类)
为了封装地图数据,城市座标以及适应性计算,创建CmapTSP 类。其中CoOrd是一个保存每个城市的x和y座标的简单结构。
class CmapTSP { private static double pi = 3.1415926535897; private IList<CoOrd> m_vecCityCoOrds = new List<CoOrd>(); //number of cities in the problem private int m_NumCities = 18; //client window dimensions private int m_MapWidth; private int m_MapHeight; //holds the length of the solution, if one is calculable. double m_dBestPossibleRoute; public CmapTSP() { this.CreateCitiesCircular(); this.CalculateBestPossibleRoute(); } //calculate the coordinates of each city (positioned in a circle) public void CreateCitiesCircular() { //first caculate the radius of spread and the origin int margin = 50; double radius; if (m_MapHeight < m_MapWidth) { radius = (m_MapHeight / 2) - margin; } else { radius = (m_MapWidth / 2) - margin; } CoOrd origin = new CoOrd(m_MapWidth / 2, m_MapHeight / 2); //calculate angle division between adjacent cities. double SegmentSize = 2 * pi / m_NumCities; double angle = 0; // vector<CoOrd> vecCities; while (angle < 2 * pi) { CoOrd ThisCity = new CoOrd(); ThisCity.x = (float)(radius * Math.Sin(angle) + origin.x); ThisCity.y = (float)(radius * Math.Cos(angle) + origin.y); m_vecCityCoOrds.Add(ThisCity); angle += SegmentSize; } } //calculates the distance between two cities public double CalculateA_to_B(CoOrd city1, CoOrd city2) { double xDist = city1.x - city2.x; double yDist = city1.y - city2.y; return Math.Sqrt(xDist * xDist + yDist * yDist); } public void CalculateBestPossibleRoute() { m_dBestPossibleRoute = 0; for (int city = 0; city < m_vecCityCoOrds.Count - 1; ++city) { m_dBestPossibleRoute += CalculateA_to_B(m_vecCityCoOrds[city], m_vecCityCoOrds[city + 1]); //add in a small amount to cover any precision errors we may have made } //add in the distance from the last to the first m_dBestPossibleRoute += CalculateA_to_B(m_vecCityCoOrds[m_vecCityCoOrds.Count - 1], m_vecCityCoOrds[0]); // m_dBestPossibleRoute = 480; } //calculates the tour length public double GetTourLength(IList<int> route) { double TotalDistance = 0; for (int i = 0; i < route.Count - 1; ++i) { int city1 = route[i]; int city2 = route[i + 1]; TotalDistance += CalculateA_to_B(m_vecCityCoOrds[city1], m_vecCityCoOrds[city2]); } //don't forget this is a closed loop so we need to add in the distance //from the last city visited back to the first int last = route[route.Count - 1]; int first = route[0]; TotalDistance += CalculateA_to_B(m_vecCityCoOrds[last], m_vecCityCoOrds[first]); return TotalDistance; } }
1.2 SGenome(类)
基因组sgenome结构定义如下:
public class SGenome { IList<int> _vecCities; //its fitness private double _dFitness; private static Random rand = new Random(); public SGenome() { _vecCities = new List<int>(); } public SGenome(int nc) { _vecCities = GrabPermutation(nc); } //creates a random tour of the cities private IList<int> GrabPermutation(int limit) { IList<int> vecPerm = new List<int>(); for (int i = 0; i < limit; i++) { //we use limit-1 because we want ints numbered from zero int NextPossibleNumber = rand.Next(0, limit); while (TestNumber(vecPerm, NextPossibleNumber)) { NextPossibleNumber = rand.Next(0, limit); } vecPerm.Add(NextPossibleNumber); } return vecPerm; } //used in GrabPermutation public bool TestNumber(IList<int> vec, int number) { for (int i = 0; i < vec.Count; ++i) { if (vec[i] == number) { return true; } } return false; } }
1.3 CgaTSP(类)
这个是遗传算法类。代码太长,只贴出主要的方法的代码。
//---------------------------MutateEM----------------------------- // // Mutates the chromosome by choosing two random genes and swapping // their position. //----------------------------------------------------------------- public void MutateEM(IList<int> chromo) { //return dependent upon mutation rate if (rand.NextDouble() > m_dMutationRate) { return; } //choose first gene int pos1 = rand.Next(0, chromo.Count); //choose second int pos2 = pos1; while (pos1 == pos2) { pos2 = rand.Next(0, chromo.Count); } //swap their positions swap(chromo, chromo[pos1], chromo[pos2]); } public void swap(IList<int> list, int i, int j) { int index1 = list.IndexOf(i); int index2 = list.IndexOf(j); list[index1] = j; list[index2] = i; } //-------------------------CrossoverPMX--------------------------------- // // crossover operator based on 'partially matched crossover' as // defined in the text //------------------------------------------------------------------- public void CrossoverPMX(IList<int> mum, IList<int> dad, IList<int> baby1, IList<int> baby2) { foreach (int m in mum) { baby1.Add(m); } foreach (int m in dad) { baby2.Add(m); } //just return dependent on the crossover rate or if the //chromosomes are the same. if ((rand.NextDouble() > m_dCrossoverRate) || (mum == dad)) { return; } //first we choose a section of the chromosome int beg = rand.Next(0, mum.Count - 1); int end = beg; //find an end while (end <= beg) { end = rand.Next(0, mum.Count); } //now we iterate through the matched pairs of genes from beg //to end swapping the places in each child for (int pos = beg; pos < end + 1; ++pos) { //these are the genes we want to swap int gene1 = mum[pos]; int gene2 = dad[pos]; if (gene1 != gene2) { swap(baby1, gene1, gene2); swap(baby2, gene1, gene2); } }//next pair } public SGenome RouletteWheelSelection() { double fSlice = rand.NextDouble() * m_dTotalFitness; double cfTotal = 0.0; int SelectedGenome = 0; for (int i = 0; i < m_iPopSize; ++i) { cfTotal += m_vecPopulation[i].dFitness; if (cfTotal > fSlice) { SelectedGenome = i; break; } } return m_vecPopulation[SelectedGenome]; } //methods used in the fitness functions public void CalculatePopulationsFitness() { //for each chromo for (int i = 0; i < m_iPopSize; ++i) { //calculate the tourlength for each chromosome double TourLength = m_pMap.GetTourLength(m_vecPopulation[i].vecCities); m_vecPopulation[i].dFitness = TourLength; //keep a track of the shortest route found each generation if (TourLength < m_dShortestRoute) { m_dShortestRoute = TourLength; m_iFittestGenome = i; } //keep a track of the worst tour each generation if (TourLength > m_dLongestRoute) { m_dLongestRoute = TourLength; } }//next chromo //Now we have calculated all the tour lengths we can assign //the fitness scores for (int j = 0; j < m_iPopSize; ++j) { m_vecPopulation[j].dFitness = m_dLongestRoute - m_vecPopulation[j].dFitness; m_dTotalFitness += m_vecPopulation[j].dFitness; } } public void Epoch() { //first reset variables and calculate the fitness of each genome Reset(); CalculatePopulationsFitness(); if ((m_dShortestRoute <= m_pMap.BestPossibleRoute())) { m_bStarted = false; return; } //create a temporary vector for the new population IList<SGenome> vecNewPop = new List<SGenome>(); //First add NUM_BEST_TO_ADD number of the last generation's //fittest genome(elitism) // NUM_BEST_TO_ADD = 2 for (int i = 0; i < 2; ++i) { vecNewPop.Add(m_vecPopulation[m_iFittestGenome]); } //now create the remainder of the population while (vecNewPop.Count != m_iPopSize) { //grab two parents SGenome mum = RouletteWheelSelection(); SGenome dad = RouletteWheelSelection(); //create 2 children SGenome baby1 = new SGenome(); SGenome baby2 = new SGenome(); //Breed them CrossoverPMX(mum.vecCities, dad.vecCities, baby1.vecCities, baby2.vecCities); //and mutate them MutateEM(baby1.vecCities); MutateEM(baby2.vecCities); //add them to new population vecNewPop.Add(baby1); vecNewPop.Add(baby2); } //copy into next generation m_vecPopulation = vecNewPop; //increment generation counter ++m_iGeneration; }
2 其他需要注意的问题
2.1
选择一个适应性分数
需要一个适应性函数,能够为愈短的周游线路奖励愈高的分数。可以用周游路程长度作为此函数,但这样得到的数值分布的广度不够,使群中最好的和最差的染色体的分数差别不大。因此,采用适用性比例选择法时,使适应性分数高的基因组被选上是相当困难的。一种比较理想的办法是:记下每一代中的最差的周游路线长度,然后再对种群基因组作一轮循环,用最长的路程(即最差的)减去每个基因组的路程,得到的为基因组的适应性分数。这样做可以使结果的相对差异变大,使有轮盘赌选择时也会更有效。这样做也可以从种群中移除最差的染色体,因为它的适应性分数为0,在选择时根本不会被选中。
2.2 杂交和变异
本次选择的杂交方法是部分映射杂交,变异方法是交换变异。它们的具体方法,我会在下一篇《遗传算法优化》中详细讲解。
2.3 选择
这里还是采用轮盘赌选择法,但是仍有一点不同,为了使遗传算法能够较快的收敛,在每一个epoch中,在选择循环前,都就确保将前一代的中适应性最高的2的基因原样复制到新一代中,这代表适应性最好的基本组永远不会在随机过程上丢掉。这一技术通常称之为种子或者精英选择法。
3 本机运行结果
还是那句话,我应该学习一下C++的。
运行中:
![](http://p.blog.csdn.net/images/p_blog_csdn_net/bravetmac/EntryImages/20090714/tsp1.jpg)
运行结果:
![](http://p.blog.csdn.net/images/p_blog_csdn_net/bravetmac/EntryImages/20090714/tsp2.jpg)
相关文章推荐
- 在matlab上实现遗传算法解决TSP旅行者问题
- 遗传算法解决TSP问题实现以及与最小生成树的对比
- 背包问题的遗传算法解法
- 遗传算法解决TSP问题实现以及与最小生成树的对比
- 遗传算法解决TSP问题实现以及与最小生成树的对比
- 利用遗传算法解决TSP问题
- TSP问题遗传算法通用Matlab程序
- 遗传算法解TSP问题 python实现
- 概率种群遗传算法解决TSP问题
- TSP问题的遗传算法--转载
- 【算法】 组合数问题非递归解法
- ACO蚁群算法解决TSP旅行商问题
- 遗传算法解迷宫问题
- 算法系列之六:最长公共子序列(LCS)问题(连续子序列)的三种解法
- 蚁群算法解TSP问题
- 贪婪算法求解TSP问题:
- 免疫算法求解TSP问题
- 【算法】逆序对问题的四种解法(归并排序,BST,树状数组,线段树)及变形
- 开开心心学算法--街区最短路径问题的二种解法
- 经典算法之天平称重问题(进制解法)