10.1 - 遗传算法(旅行商问题C#求解)
遗传算法是模拟自然界生物进化过程的随机优化算法,通过模拟"自然选择"和"遗传机制"来寻找最优解,其中遗传机制主要依靠突变、交叉、选择这些仿生算子。
适应度 | 生存能力 | 解决方案的质量评分 |
选择 | 自然选择 | 选择优秀的个体进行繁殖 |
交叉 | 有性繁殖 | 组合两个个体的基因 |
变异 | 基因突变 | 随机改变个体的某些基因 |
一、旅行商问题
给定一系列城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路
为了简单起见:以直线上等间距的10个城市为例。最短距离就是18.
编程思路是:
1. 先随机生成比如20条路线;
2. 然后按距离排序,丢掉最长的10条;
3. 剩下(被选择)的10条通过突变因子和交叉因子再生成10条。
4. (当迭代次数未完成时)返回到2. 完成一次迭代。
【备注】如果按照50个人口来,差不多几次就可以得到最好的结果
1.1 突变因子
把两座随机指定的城市交换位置。
public Solution Mutate(Random random)
{
var i=random.Next(0,CityList.Count);
var j= random.Next(0, CityList.Count);
if (i == j)
{
return this;
}
var newCityList = new List<int>(CityList);
var temp = newCityList[i];
newCityList[i] = newCityList[j];
newCityList[j]= temp;
return new Solution(newCityList);
}
2.交叉算子
由父A母B的各一部分交叉组成;思路是:随机从B中取一段放到child中,然后把A中的所有不在子类的按顺序填充到child中。作为进化后的
//把另外一个的城市序列的一段拷贝过来,并且把本序列的其余部分按顺序拷贝到child中
public Solution CrossOver(Random random,Solution solution)
{
int length = this.CityList.Count;
// 随机选择两个交叉点
int point1 = random.Next(0, length - 1);
int point2 = random.Next(point1 + 1, length);
int[] child1 = new int[length];
FillArray(child1, -1);
// 复制中间段
for (int i = point1; i <= point2; i++)
{
child1[i] = solution.CityList[i];
}
FillRemainingPositions(this.CityList, child1, point1, point2);
return new Solution(child1);
}
//按顺序把parent中不在child中的元素填充过来。
private static void FillRemainingPositions(List<int> parent,
int[] child, int start, int end)
{
var remainElement = parent.Where(x => child.Contains(x) == false).Select(x => x).ToList();
if (remainElement.Any() == false)
return;
int j = 0;
for (int i = 0; i < child.Length; i++)
{
if (i >= start && i <= end) continue;
child[i] = remainElement[j++];
}
}
3.实际的一段截图如下:
二、代码
/// <summary>/// trip route,/// </summary>public class TripRoute{/// <summary>/// Map is distance between points, Map[2,3] means distance beween point2-point3./// </summary>public static double[,] Map { get;set; }public TripRoute(double[,] map){Map = map;}/// <summary>/// calculate the distance between the point series./// for example: 1-5-2-3; 1-5;5-2;2-3;/// </summary>/// <param name="solution"></param>public static void CalculateDistance(Solution solution){var result = 0.0;for(int i = 0; i <solution.CityList.Count-1; i++){result += Map[solution.CityList[i], solution.CityList[i + 1]];}result+= Map[0, solution.CityList.Count-1]; //最后回到原点solution.Distances = result;}}public class Solution :IComparable<Solution>{public List<int> CityList { get; set; }public double Distances { get; set; }/// <summary>/// Get a solution for travel route/// </summary>/// <param name="cityList"></param>public Solution(IEnumerable<int> cityList){CityList = new List<int>(cityList);TripRoute.CalculateDistance(this);}/// <summary>/// 变异\突变;/// 交换两个城市在序列中的位置/// </summary>/// <param name="random"></param>/// <returns></returns>public Solution Mutate(Random random){var i=random.Next(0,CityList.Count);var j= random.Next(0, CityList.Count);if (i == j){return this;}var newCityList = new List<int>(CityList);var temp = newCityList[i];newCityList[i] = newCityList[j];newCityList[j]= temp;return new Solution(newCityList);}/// <summary>/// 交叉算子,一个基因的前半部分和后一个基因的后一半部分组合/// </summary>/// <param name="random"></param>/// <param name="solution"></param>/// <returns></returns>public Solution CrossOver(Random random,Solution solution){int length = this.CityList.Count;// 随机选择两个交叉点int point1 = random.Next(0, length - 1);int point2 = random.Next(point1 + 1, length);int[] child1 = new int[length];FillArray(child1, -1);// 复制中间段for (int i = point1; i <= point2; i++){child1[i] = solution.CityList[i];}FillRemainingPositions(this.CityList, child1, point1, point2);return new Solution(child1);} /// <summary>/// 按顺序把parent中不在child中的元素填充过来。/// </summary>/// <param name="parent"></param>/// <param name="child"></param>/// <param name="start"></param>/// <param name="end"></param>private static void FillRemainingPositions(List<int> parent,int[] child, int start, int end){var remainElement = parent.Where(x => child.Contains(x) == false).Select(x => x).ToList();if (remainElement.Any() == false)return;int j = 0;for (int i = 0; i < child.Length; i++){if (i >= start && i <= end) continue;child[i] = remainElement[j++];}}public static void FillArray<T>(T[] array, T value){for (int i = 0; i < array.Length; i++){array[i] = value;}}public int CompareTo(Solution other){if (other == null) return 1;return this.Distances.CompareTo(other.Distances);}public override string ToString(){StringBuilder sb = new StringBuilder();foreach(int i in this.CityList){sb.Append( string.Format("{0}, ", i));}sb.Append("距离:"+this.Distances.ToString());return sb.ToString();}}public class GeneticAlgorithmTsp{public int Iterations { get; set; }public TripRoute Tsp { get; set; }public List<Solution> Population { get; set; }public int Size;private static readonly Random random = new Random();public GeneticAlgorithmTsp(int iterations, TripRoute tsp, int size){Iterations = iterations;Tsp = tsp;Size = size;Population = new List<Solution>();}public Solution Execute(){InitPopulation();var i = 0;while(i<Iterations){var selected = ChooseBest();var newPopulation = Evolution(selected as List<Solution>);MergePopulation(newPopulation);i++;Logger.Record("----------------------");Logger.Record( string.Format("第{0}次迭代,种群数量{1},最优距离{2},种群如下", i,Population.Count , Population.First().Distances));foreach (var item in Population){Logger.Record(item.ToString());}Logger.Record("----------------------");}return Population.First();}/// <summary>/// 初始化种群/// </summary>private void InitPopulation(){var i = 0;while (i < Size){Population.Add(RandomSolution(TripRoute.Map.GetLength(0)));i++;}Logger.Record("初始化种群情况:");foreach(var item in Population){Logger.Record(item.ToString());}}/// <summary>/// 随机生成顺序/// </summary>/// <param name="n"></param>/// <returns></returns>private Solution RandomSolution(int n){var result = new List<int>();var range = Enumerable.Range(0, n).ToList();while (range.Count > 0){var index = random.Next(0, range.Count);result.Add(range[index]);range.RemoveAt(index);}return new Solution(result);}/// <summary>/// 人口合并/// </summary>/// <param name="newPopulation"></param>private void MergePopulation(IEnumerable<Solution> newPopulation){Population.AddRange(newPopulation);Population.Sort();Population = Population.GetRange(0, Size);}/// <summary>/// 使用交换或变异的办法产生新的元素/// </summary>/// <param name="selected"></param>/// <returns></returns>private IEnumerable<Solution> Evolution(List<Solution> selected){var result = new List<Solution>();for(var i=0;i<selected.Count;i++){result.Add(random.NextDouble() <= 0.4? selected[i].Mutate(random) : selected[i].CrossOver(random, selected[random.Next(0, selected.Count)]));}Logger.Record("-------------:");Logger.Record( string.Format("新进化出的人口,数量{0},情况如下:",result.Count));foreach(Solution s in result){Logger.Record(s.ToString());}return result;}/// <summary>/// 选择表现好的,即总数的一半/// </summary>/// <returns></returns>private IEnumerable<Solution> ChooseBest(){Population.Sort();Logger.Record("-------------:");Logger.Record("选择最好的一半,丢失的如下:");for(int i= Size / 2;i<Size;i++){Logger.Record(Population[i].ToString());}return Population.GetRange(0, Size / 2);}}static void Main(string[] args){var map = new double[,]{{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, //城市距离矩阵{ 1, 0, 1, 2, 3, 4, 5, 6, 7, 8 },{ 2, 1, 0, 1, 2, 3, 4, 5, 6, 7 },{ 3, 2, 1, 0, 1, 2, 3, 4, 5, 6 },{ 4, 3, 2, 1, 0, 1, 2, 3, 4, 5 },{ 5, 4, 3, 2, 1, 0, 1, 2, 3, 4 },{ 6, 5, 4, 3, 2, 1, 0, 1, 2, 3 },{ 7, 6, 5, 4, 3, 2, 1, 0, 1, 2 },{ 8, 7, 6, 5, 4, 3, 2, 1, 0, 1 },{ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }};var ga = new GeneticAlgorithmTsp(100, new TripRoute(map), 20);var best = ga.Execute();Console.WriteLine("best solution:");foreach (var d in best.CityList)Console.Write("{0}, ", d);Console.WriteLine("\n" + "fitness:{0}", best.Distances);Console.Read();return; }