此文僅用於記錄自己的遺傳算法的學習過程,對代碼做了微微的改動,加了點注釋,可能存在錯誤。
參考:https://blog.csdn.net/kyq0417/article/details/84345094
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GA { class Program { #region 全局變量 /// <summary> /// 染色體信息 /// </summary> private class Chromosome { /// <summary> /// 用6bit對染色體進行編碼,第1個元素表示正負,2-5以此存儲二進制的的信息,最大表示31 /// </summary> public int[] bits = new int[6]; /// <summary> /// 適應值 /// </summary> public int fitValue; /// <summary> /// 選擇概率 /// </summary> public double fitValuePercent; /// <summary> /// 累積概率 /// </summary> public double totalProbability; public Chromosome Clone()//染色體賦值,復制 { Chromosome c = new Chromosome(); for (int i = 0; i < bits.Length; i++)//逐個染色體賦值 { c.bits[i] = bits[i]; } c.fitValue = fitValue; c.fitValuePercent = fitValuePercent; c.totalProbability = totalProbability; return c; } } /// <summary> /// 變異幾率 /// </summary> public static double varProbability = 0.02; /// <summary> /// 染色體組; /// </summary> private static List<Chromosome> chromosomes = new List<Chromosome>(); //父代 private static List<Chromosome> chromosomesChild = new List<Chromosome>(); //子代 private static Random random = new Random(); /// <summary> /// 選擇類型 /// </summary> private enum ChooseType { Bubble,//冒泡; Roulette,//輪盤賭; } private static ChooseType chooseType = ChooseType.Roulette; //選擇賭輪盤的方法 #endregion /// <summary> /// Main入口函數; /// </summary> /// <param name="args"></param> private static void Main(string[] args) { Console.WriteLine("遺傳算法"); Console.WriteLine("求函數最大值函數為y = x*x-31x的最大值,-31<=x<=31"); int totalTime = 30; // 迭代次數,根據結果調整 Console.WriteLine("迭代次數為: " + totalTime); Console.WriteLine("初始化: "); //初始化; Init(); // 輸出初始化數據; Print(); for (int i = 0; i < totalTime; i++) { Console.WriteLine("當前迭代次數: " + i); //重新計算fit值;; UpdateFitValue(); // 挑選染色體; Console.WriteLine("挑選:"); switch (chooseType) { case ChooseType.Bubble: // 排序; Console.WriteLine("排序:"); ChooseChromosome(); break; default: //輪盤賭; Console.WriteLine("輪盤賭:"); UpdateNext(); break; } Print(true); //交叉得到新個體 Console.WriteLine("交叉:"); CrossOperate(); Print(); //變異 Console.WriteLine("變異:"); VariationOperate(); Print(); } //挑選出最大適應值 int maxFit = chromosomes[0].fitValue; for (int i = 1; i < chromosomes.Count; i++) { if (chromosomes[i].fitValue > maxFit) { maxFit = chromosomes[i].fitValue; } } Console.WriteLine("最大值為: " + maxFit); Console.ReadKey(); } #region 打印輸出 /// <summary> /// 打印; /// </summary> private static void Print(bool bLoadPercent = false) { Console.WriteLine("========================="); for (int i = 0; i < chromosomes.Count; i++) { Console.Write("第" + i + "條" + " 基因: "); for (int j = 0; j < chromosomes[i].bits.Length; j++) { Console.Write(" " + chromosomes[i].bits[j]); } int x = DeCode(chromosomes[i].bits); Console.Write(" x: " + x); Console.Write(" y: " + chromosomes[i].fitValue); if (bLoadPercent) { Console.Write(" 選擇概率: " + chromosomes[i].fitValuePercent); } Console.WriteLine(); } Console.WriteLine("========================="); } #endregion #region 初始化 /// <summary> /// 初始化 /// </summary> private static void Init() { chromosomes.Clear(); int length = 30; // 染色體數量 int totalFit = 0; for (int i = 0; i < length; i++) { Chromosome chromosome = new Chromosome(); for (int j = 0; j < chromosome.bits.Length; j++) { // 隨機出0或者1; int bitValue = random.Next(0, 2); chromosome.bits[j] = bitValue; } //獲得十進制的值; int x = DeCode(chromosome.bits); int y = GetFitValue(x); chromosome.fitValue = y; chromosomes.Add(chromosome); //算出total fit; if (chromosome.fitValue <= 0) { totalFit += 0; } else { totalFit += chromosome.fitValue; } } } #endregion #region 解碼,二進制裝換 /// <summary> /// 解碼,二進制裝換; /// </summary> /// <param name="bits"></param> /// <returns></returns> private static int DeCode(int[] bits) { int result = bits[1] * 16 + bits[2] * 8 + bits[3] * 4 + bits[4] * 2 + bits[5] * 1;//第一個元素判斷正負,后5個元素二進制轉換十進制,所能表達的最大十進制數值為31 //通過第一個元素判斷正負; if (bits[0] == 0) { return result; } else { return -result; } } #endregion #region 獲取fitValue(適應度) /// <summary> /// 獲取fitValue,返回值越大說明適應度越高 /// </summary> /// <param name="x"></param> /// <returns></returns> private static int GetFitValue(int x) { return x * x - 31 * x;//目標函數 y= x*x -31*x } #endregion #region 選擇 /// <summary> /// 更新下一代; /// 基於輪盤賭選擇方法,進行基因型的選擇; /// </summary> private static void UpdateNext() { // 獲取總的fit; double totalFitValue = 0; for (int i = 0; i < chromosomes.Count; i++) { //適應度為負數的取0(前提知道函數的最大值一定大於0) if (chromosomes[i].fitValue <= 0) { totalFitValue += 0; } else { totalFitValue += chromosomes[i].fitValue; } } Console.WriteLine("累計適應度(totalFitValue) :" + totalFitValue); //算出每個的fit percent; for (int i = 0; i < chromosomes.Count; i++) { if (chromosomes[i].fitValue <= 0) //殺掉適應度為負的基因 { chromosomes[i].fitValuePercent = 0; } else { chromosomes[i].fitValuePercent = chromosomes[i].fitValue / totalFitValue;//計算第i個基因的適應值(第i個基於的適應值/累計適應值) } Console.WriteLine("第" + i + " 個體選擇概率(fitValuePercent):" + chromosomes[i].fitValuePercent); } ////計算累積概率 //// 第一個的累計概率就是自己的概率,循環完后,便可得到累計概率的值,便於后續的賭輪盤算法 chromosomes[0].totalProbability = chromosomes[0].fitValuePercent; double probability = chromosomes[0].totalProbability; for (int i = 1; i < chromosomes.Count; i++) { if (chromosomes[i].fitValuePercent != 0) //只對出現概率概率不為0的染色體進行操作 { chromosomes[i].totalProbability = chromosomes[i].fitValuePercent + probability; probability = chromosomes[i].totalProbability; } } chromosomesChild.Clear(); //輪盤賭選擇方法,用於選出前兩個; for (int i = 0; i < chromosomes.Count; i++) { double chooseNum = random.NextDouble(); //生成0.0-1.0之間的隨機數,用於選擇 Console.WriteLine("挑選的數值:" + chooseNum); //判斷挑選的數值落在區間位置,從而確定選擇的染色體 if (chooseNum < chromosomes[0].totalProbability) { chromosomesChild.Add(chromosomes[0].Clone()); } else { for (int j = 0; j < chromosomes.Count - 1; j++) //-1是因為去除了第1個染色體 { if (chromosomes[j].totalProbability <= chooseNum && chooseNum <= chromosomes[j + 1].totalProbability) //判斷區間 { chromosomesChild.Add(chromosomes[j + 1].Clone()); //j是從0開始的, } } } } for (int i = 0; i < chromosomes.Count; i++) { chromosomes[i] = chromosomesChild[i]; //子代變父代,便於進入下一次循環 } } /// <summary> /// 選擇染色體; /// </summary> private static void ChooseChromosome() { // 從大到小排序; chromosomes.Sort((a, b) => { return b.fitValue.CompareTo(a.fitValue); }); } #endregion #region 交叉 /// <summary> /// 交叉操作 /// </summary> private static void CrossOperate() { //選擇交叉位置 int bitNum = chromosomes[0].bits.Length; //獲取染色體位數 int a = random.Next(0, bitNum ); //生成0-染色體位數-1之間的隨機數, int b = random.Next(0, bitNum ); if (a > b) //交換,排序 { int temp = a; a = b; b = temp; } Console.WriteLine("交叉范圍:" + a + "— " + b); //交叉位置處進行交叉操作(第1條和第2條進行交叉,兩兩一對,以此類推,染色體個數優先選擇偶數個) for (int j = 0; j < chromosomes.Count; j = j + 2) { for (int i = a; i <= b; i++) //從第一個隨機數開始交叉 到第二個隨機數結束 { int temp = chromosomes[j].bits[i]; chromosomes[j].bits[i] = chromosomes[j + 1].bits[i]; chromosomes[j + 1].bits[i] = temp; } //對交叉后生成的兩條新染色體(二進制轉碼十進制)重新計算適應值 chromosomes[j].fitValue = GetFitValue(DeCode(chromosomes[j].bits)); chromosomes[j + 1].fitValue = GetFitValue(DeCode(chromosomes[j + 1].bits)); } } #endregion #region 變異 /// <summary> /// 變異操作 /// </summary> private static void VariationOperate() { int chromoNum = chromosomes.Count; //染色體數量 int geneNum = chromosomes[0].bits.Length - 1; //單個染色體基因數-1,去除用於判斷正負的一個基因 int varSite = random.Next(1, chromoNum * geneNum + 1); //在所有的基因數范圍中選擇一個隨機數 if (varSite <= chromoNum *geneNum *varProbability) //通過判斷隨機數是否小於等於 基因總數*變異概率 ,判斷是否發生突變 //每條染色體有5個決定基因(排除第一個決定正負的基因) { Console.WriteLine("開始變異"); int row = random.Next(0, chromoNum ); //確定要突變的染色體編號 int col = random.Next(0, geneNum ); //確定要突變的基因 Console.WriteLine("變異的位置 :第" + row + "條染色體的第" + col + "個基因"); //0變1,1變0 if (chromosomes[row].bits[col] == 0) { chromosomes[row].bits[col] = 1; } else { chromosomes[row].bits[col] = 0; } chromosomes[row].fitValue = GetFitValue(DeCode(chromosomes[row].bits)); } } #endregion #region 重新計算fit值 /// <summary> /// 重新計算fit值; /// </summary> private static void UpdateFitValue() { for (int i = 0; i < chromosomes.Count; i++) { chromosomes[i].fitValue = GetFitValue(DeCode(chromosomes[i].bits)); } } #endregion } }