C# 遺傳算法入門學習


  此文僅用於記錄自己的遺傳算法的學習過程,對代碼做了微微的改動,加了點注釋,可能存在錯誤。
參考: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
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM