智能優化算法——遺傳算法原理(附代碼)

目錄

         基本概念

         算法模型

         相關生物學術語

         遺傳算法的基本操作

         參數分析

         C++程序測試Sphere函數

         總結

         visual studio2017c++源代碼

         源文件下載地址


  • 基本概念

遺傳算法(genetic algorithm,GA)是模擬達爾文生物進化論的自然選擇遺傳學機理的生物進化過程的計算模型,是一種通過模擬自然進化過程搜索最優解的方法。

主要特點是直接對結構對象進行操作,不存在求導和函數連續性的限定;具有內在的隱並行性和更好的全局尋優能力;採用概率化的尋優方法,對參數編碼不需要任何先驗知識,採用平行搜索避免陷入局部優化。

遺傳算法從代表問題的潛在解集的一個種羣開始,而種羣由基因編碼的一定數目的個體組成。個體是染色體帶有特徵的實體,染色體由多個基因組成,也可以把基因描述成未編碼之前的優化變量。每一代,根據個體適應度選擇個體,藉助自然遺傳學的遺傳算子進行組合交叉變異更新種羣,整個過程模擬自然進化,末代種羣的最優個體通過解碼就是問題的最優解。

  • 算法模型

                                                         遺傳算法流程圖

  • 相關生物學術語

  1. 基因型(genotype):性狀染色體的內部表現;

  2. 表現型(phenotype):染色體決定的性狀的外部表現,或者說,根據基因型形成的個體的外部表現;

  3. 進化(evolution):種羣逐漸適應生存環境,品質不斷得到改良。生物的進化是以種羣的形式進行的。

  4. 適應度(fitness):度量某個物種對於生存環境的適應程度。

  5. 選擇(selection):以一定的概率從種羣中選擇若干個個體。一般,選擇過程是一種基於適應度的優勝劣汰的過程。

  6. 複製(reproduction):細胞分裂時,遺傳物質DNA通過複製而轉移到新產生的細胞中,新細胞就繼承了舊細胞的基因。

  7. 交叉(crossover):兩個染色體的某一相同位置處DNA被切斷,前後兩串分別交叉組合形成兩個新的染色體。也稱基因重組或雜交;

  8. 變異(mutation):複製時可能(很小的概率)產生某些複製差錯,變異產生新的染色體,表現出新的性狀。

  9. 編碼(coding):DNA中遺傳信息在一個長鏈上按一定的模式排列。遺傳編碼可看作從表現型到基因型的映射。

  10. 解碼(decoding):基因型到表現型的映射。

  11. 個體(individual):指染色體帶有特徵的實體;

  12. 種羣(population):個體的集合,該集合內個體數稱爲種羣

  • 遺傳算法的基本操作

編碼

表現型轉爲基因型,在算法裏爲實數轉化爲二進制,解碼反之。


舉個例子:對於x∈[-1, 2] ,結果精確到6位小數,十進制實數與二進制編碼之間應滿足怎樣的數學關係?

1.將二進制串\left ( b_{21} b_{20}b_{19}\cdots b_{0}\right )_{2}轉換爲十進制爲:

                                       x{}'=\left ( b_{21} b_{20}b_{19}\cdots b_{0}\right )_{2}=(\sum_{i=0}^{21}b_{i}\cdot 2^{i})_{10}                                  (1)

2.將二進制串對應的實數x爲

                                             x=-1.0+\frac{x{}'}{2^{22}-1}\times 3                                                  (2)

    例如(1000101110110101000111)表示0.637197,因爲

                                        x{}'=(\sum_{i=0}^{21}b_{i}\cdot 2^{i})_{10}=2288967                                              (3)

                                  x=-1.0+\frac{2288967}{2^{22}-1}\times 3=0.637197                                       (4)

選擇

選擇(複製)操作把當前種羣的染色體按與適應值成正比例的概率複製到新的種羣中,主要思想: 適應值較高的染色體有較大的選擇(複製)機會。

輪盤法:

  1. 將種羣中所有染色體的個體相加求總和,個體適應值按其比例轉化爲選擇概率Ps;
  2. 產生一個在0與總和之間的的隨機數m;
  3. 從種羣中第一個個體開始,將其適應值與後續個體的適應值相加,直到累加和等於或大於m,更新種羣中的個體。

設種羣的規模爲N,x_{i}爲種羣的第i個個體,F(x)爲適應度函數,則個體x_{i}被選擇的概率:

                                               P_{s}=\frac{F(x_{i}))}{\sum_{j=1}^{N}F(x_{j})}                                                       (5)

  從上面可以得出,遺傳算法的適應度函數的值要取正值,並且優化模型應該是求最大值和非負的目標函數。

交叉

遺傳交叉(雜交、交配、有性重組)操作發生在兩個染色體之間,由兩個被稱之爲雙親的父代染色體,經雜交以後,產生兩個具有雙親的部分基因的新的染色體,從而檢測搜索空間中新的點。

單點交叉方法:在雙親的父代染色體中以一定概率隨機產生一個交叉點位置,在交叉點位置分離雙親染色體互換交叉點位置右邊的基因碼產生兩個子代個體。本文采用兩點交叉,確定方法類似。

變異

以變異概率Pm改變染色體的某一個基因,當以二進制編碼時,變異的基因由0變成1,或者由1變成0。

停止準則

  1. 種羣中個體的最大適應值超過預設定值
  2. 種羣中個體的平均適應值超過預設定值
  3. 種羣中個體的進化代數超過預設定值
  • 參數分析

種羣規模N

當規模太小時,會出現近親交配,產生病態基因;種羣規模較大,難以收斂,穩健性下降。一般取0—100。

變異概率

當變異概率太小時,種羣的多樣性下降太快,導致有效基因的迅速丟失且不容易修補;當變異概率太大是,高階模式被破壞的概率隨之增大。一般取0.0001—2。

交叉概率

更新種羣的重要方式,交配概率太大破壞已有的有利模式,隨機性增大,容易錯失最優個體;交配概率太小不能有效更新種羣,一般取0.4—0.99。

進化代數

進化代數太小,算法不容易收斂,種羣還沒有成熟;代數太大,進化沒有意義,浪費時間和資源。一般取100—500。

  • C++程序測試Sphere函數

                                                                f\left ( x \right )=\sum_{i=1}^{D}x_{i}^{2}

通過程序計算,迭代200步,用origin整理如下:

從上圖可以看書,收斂用時還是比較少,在50步左右就收斂了。

  • 總結

遺傳算法的優點:

  1. 與問題領域無關、快速隨機的搜索能力。
  2. 潛在的並行性,多個個體的同時比較,魯棒性強。
  3. 使用概率機制進行迭代,具有隨機性
  4. 具有可擴展性,容易與其他算法結合。

遺傳算法的缺點:

  1. 編程實現比較雜,需要對問題進行編碼,得到最優解還需要解碼。
  2. 另外三個算子的實現也有許多參數,如交叉率和變異率,並且這些參數的選擇嚴重影響解的品質,而目前這些參數的選擇大部分是依靠經驗
  3. 算法的搜索速度比較慢,要得要較精確的解需要較多的時間
  4. 算法的並行機制的潛在能力沒有得到充分的利用。
  • visual studio2017c++源代碼

pch.h頭文件:

// Geneti_algorithm.cpp : 遺傳算法實現過程。更新於2020.5.3
//開發人員:陳帥   開始日期:2019.8.5-8.8         郵箱:[email protected]
#ifndef PCH_H
#define PCH_H
#include <iostream>
# include <fstream>
#include <iomanip>
#include <math.h>
#include <vector>
#include<random>
#include<ctime>
using namespace std;
//產生隨機小數或整數
class RandomNumber {
public:
	RandomNumber() {
		srand(time(0));    //析構函數,在對象創建時數據成員執行初始化操作
	}
	int integer(int begin, int end)
	{
		return rand() % (end - begin + 1) + begin;
	}
	double decimal(double a, double b)
	{
		return double(rand() % 10000) / 10000 * (b - a) + a;
	}
};
//ga用於定義優化變量範圍,以及遺傳算法過程中的選擇、交叉、變異算子、解碼、個體適應度計算等函數。
class ga
{
private:
       //==========================遺傳算法參數設置=============================
	int  N_genetic ;                //種羣規模,太小產生病態基因;種羣規模太大,難以收斂,一般0-100
	double M_pgentic ;            //變異概率,與種羣多樣性有關,一般0.0001-0.2
	double C_pgentic ;             //交叉概率,概率太大,容易錯失最優個體,太小布恩那個有效更新種羣,一般0.4-0.99.
	int E_gentic ;                 //進化代數,太小,算法不容易收斂,太大增加時間和資源浪費,一般100-500.
	int L_variable;                //個體變量的字符串長度(基因數)
	double precision;
	int N_variable ;                 //個體變量的個數
public:
	vector<vector<double>>x_i;          //優化變量
	vector<double>x_best;               //最優個體
	vector<vector<int>>x_binary;        //個體染色體
	vector<double> fitness;             //個體適應度;由於適應度函數要比較排序並在此基礎計算選擇概率,適應度函數的值應該取正值。
	double best_fitness;                //種羣最優適應度
	vector<double> sumfitness;          //前面個體適應度和
	vector<double> P_i;                 //個體被選擇的概率	
	vector<double>x_low = { -600 };     //優化變量最小值
	vector<double>x_high = { 600 };     //優化變量最大值
	void initialize();//初始化,產生初始種羣
	vector<double> Real_trans(vector<int>x_binary);   //二進制轉換爲實數
	void SetParameters();               //設置算法參數
	void Optimization_iteration();
	void select_operator();             //選擇算子
	void crossover_operator();          //交叉算子
	void mutate_operator();             //變異算子	
};
double function(vector<double> x); //目標函數
#endif //PCH_H

Genetic_algorithm.cpp主函數

#include "pch.h"
int main()
{
	ga GA;          //定義全局遺傳算法相關函數
	//=========================設置算法參數====================
	GA.SetParameters();
	//========================初始化並賦值=====================
	GA.initialize();
	//=====================優化迭代並輸出結果=======================
	GA.Optimization_iteration();
}

ga_function.cpp函數文件:

#include "pch.h"
//=============================設置參數====================================
void ga::SetParameters()
{
	N_genetic = 50;                //種羣規模,太小產生病態基因;種羣規模太大,難以收斂,一般0-100
	M_pgentic = 0.25;            //變異概率,與種羣多樣性有關,一般0.0001-0.2
	C_pgentic = 0.5;             //交叉概率,概率太大,容易錯失最優個體,太小布恩那個有效更新種羣,一般0.4-0.99.
	E_gentic = 400;                 //進化代數,太小,算法不容易收斂,太大增加時間和資源浪費,一般100-500.
	precision=0.001;
	L_variable =  int(log((x_high[0] - x_low[0]) / precision + 1) / log(2));//個體變量的字符串長度(基因數)
	N_variable=2;
}
//***************************
//二進制轉換爲實數
//******************************
vector<double> ga::Real_trans(vector<int>x_binary)
{
	vector<int>x_decimal(N_variable);
	vector<double>x(N_variable);
	for (int j = 0; j < N_variable; j++)
	{
		for (int k = j * L_variable, l_gen = 0; k < (j + 1)*L_variable; k++, l_gen++)
		{
			x_decimal[j] = x_decimal[j] + x_binary[k] * pow(2, l_gen);
		}
		x[j] = x_low[0] + double(x_decimal[j]) / (pow(2, L_variable ) - 1)*(x_high[0] - x_low[0]);
	}
	return x;
}
//*******************
//初始化並賦值
//*******************
void ga::initialize()
{
	extern RandomNumber r;       //定義全局隨機數
	x_i.resize(N_genetic, vector<double>(N_variable));
	x_best.resize(N_variable);
	fitness.resize(N_genetic);
	x_binary.resize(N_genetic, vector<int>(N_variable*L_variable)); //優化變量二進制
	for (int i = 0; i < N_genetic; i++)
	{
   //================================基因編碼===============================================
		for (int j = 0; j < N_variable*L_variable; j++)
		{
			x_binary[i][j] = r.integer(0,1);
			cout<< x_binary[i][j];
		}
		cout << endl;
		x_i[i]=Real_trans(x_binary[i]);
		fitness[i] =1/function(x_i[i]);
	}
	x_best = x_i[0];                      //初始化最優個體
	best_fitness = fitness[0];
	for (int i = 1; i < N_genetic; i++)
	{
		if (best_fitness < fitness[i])
		{
			best_fitness = fitness[i];
			x_best = x_i[i];
		}
	}
}
void ga::Optimization_iteration()
{
	clock_t startTime, endTime; //定義程序開始運行時間和結束時間
	startTime = clock();        //計時開始
	ofstream out("遺傳算法優化結果.txt");
	for (int i = 0; i < E_gentic; i++)
	{
		select_operator();  //選擇父代更新個體
		crossover_operator();  //有交配權的所有父代進行交叉
		mutate_operator();     //個體變異
		for (int j = 0; j< N_genetic; j++)
		{
			fitness[j] = 1 / function(x_i[j]);
			if (best_fitness < fitness[j])
			{
				best_fitness = fitness[j];
				x_best = x_i[j];
			}
		}   //種羣選優
		out << i << fixed << setw(12) << setprecision(5) <<1/ best_fitness << endl;

	}
	out << "最優變量:" << endl;
	for (int i = 0; i < N_variable; i++)
	{
		out << "x" << i << "=" << fixed << setw(12) << setprecision(5) << x_best[i] << endl;//輸出最優變量
	}
	out << "最優值=" << fixed << setw(12) << setprecision(5) << 1 / best_fitness << endl;
	endTime = clock();//計時結束
	out << "run time:" << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
	out.close();
}

//*******************
//選擇算子函數
//*******************
void ga::select_operator()
{
	
	double totalfit = 0, p_i;
	sumfitness.resize(N_genetic);
	P_i.resize(N_genetic);
	extern RandomNumber r;       //隨機數
	vector<vector<int>>new_x_binary(N_genetic, vector<int>(N_variable*L_variable));//儲存選擇產生的新個體
	for (int i = 0; i < N_genetic; i++)
	{
		sumfitness[i] = totalfit + fitness[i];
		totalfit = totalfit + fitness[i];
	}
	//計算個體概率
	for (int i = 0; i < N_genetic; i++)
	{
		P_i[i] = sumfitness[i] / totalfit;
	}
	//選擇父代
	for (int i = 0; i < N_genetic; i++)
	{
		p_i = r.decimal(0, 1.0);
		//利用輪盤法選擇個體
		if (p_i <= P_i[0])
			new_x_binary[i] = x_binary[0];
		else
		{
			for (int j = 0; j < N_genetic - 1; j++)
			{
				if (p_i > P_i[j]&&p_i <= P_i[j+1])
					new_x_binary[i] = x_binary[j + 1];
			}
		}
	}
	//更新個體
	x_binary = new_x_binary;
}
//*******************
//交叉算子函數,兩點交叉
//*******************
void ga::crossover_operator()
{
	int  cpoint1, cpoint2, t;                //交叉點cpoint1, cpoint2生成,t爲替換值
	double p_c;                              //隨機產生交叉概率
	extern RandomNumber r;       //隨機數
	for (int i = 0; i < N_genetic; i = i + 2)
	{//隨機產生兩個交叉點的數
		cpoint1 = r.integer(0, N_variable* L_variable-1);
		cpoint2 = r.integer(0, N_variable* L_variable-1);
		if (cpoint2 < cpoint1)
		{
			t = cpoint2; cpoint2 = cpoint1; cpoint1 = t;
		}
		p_c = r.decimal(0, 1.0);
		//交叉過程
		if (p_c < C_pgentic)
		{
			for (int j = cpoint1; j <= cpoint2; j++)
			{
				t = x_binary[i][j]; x_binary[i][j] = x_binary[i+1][j]; x_binary[i+1][j] = t;
			}
		}
	}	
}
//*******************
//變異算子函
//*******************
void ga::mutate_operator()
{
	int  mpoint;//變異點mpoint生成
	double p_m;
	extern RandomNumber r;       //定義全局隨機數
	for (int i = 0; i < N_genetic; ++i)
	{//隨機產生變異點
		mpoint = r.integer(0, N_variable* L_variable-1);
		p_m = r.decimal(0, 1.0);
		if (p_m < M_pgentic)
			if (x_binary[i][mpoint] == 0)
			{
				x_binary[i][mpoint] = 1;
			}
			else {
				x_binary[i][mpoint] = 0;
			}
	}
	//變異後的染色轉換爲實數
	for (int i = 0; i < N_genetic; i++)
	{
		x_i[i] = Real_trans(x_binary[i]);
	}
}
  • 源文件下載地址

https://download.csdn.net/download/weixin_41788456/11828560

          各位閱讀文章的朋友覺得文章可以給個好評或者點贊,大家覺得有問題可以在評論區指出來或者發郵箱[email protected]聯繫我!如需要轉載請附上鍊接,謝謝各位朋友!

(更新於2020.05.03)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章