1. 程式人生 > >遺傳演算法上機系列之用遺傳演算法求函式最值問題(附自己寫的程式碼)

遺傳演算法上機系列之用遺傳演算法求函式最值問題(附自己寫的程式碼)

本文基於下面的最值問題進行求解: maxf(x1,x2)=21.5+x1sin(4πx1)+x2sin(20πx2)\ max f(x_1,x_2)=21.5+x_1sin(4\pi x_1)+x_2sin(20\pi x_2) 3.0x112.1\ -3.0\le x_1\le 12.1 4.1x25.8\ 4.1\le x_2\le 5.8 精度要求ϵ=104\epsilon=10^{-4}

遺傳演算法的基本步驟

step1:按某種編碼方式產生初始種群
p(0)\ p(0),設種群代數t=0\ t=0

常用編碼方式:

二進位制編碼、實數編碼、整數編碼、排列編碼、有限狀態機編碼、樹編碼……(本例中我們採用二進位制編碼,需要對應解碼)

二進位制編碼

x[a,b]\ x \in[a,b],精度要求不小於10α\ 10^{-\alpha}. 編碼公式:2m11<(ba)10α2m1\ 2^{m-1}-1<(b-a)*10^\alpha\le2^{m-1},根據取值範圍即可求出共需要多少位二進位制數才能滿足精度要求 解碼公式:y=a+(ba)x

2m1\ y=a+(b-a)*\frac{x}{2^m-1}(其中x\ x為無符號二進位制換算成十進位制之後的值)

本例中,按照精度要求及取值範圍,x1\ x_1需要18位二進位制編碼,x2\ x_2需要15位二進位制編碼。 以下為初始化部分程式碼:

//初始化種群
void init()
{
	double p=0.5;
	//srand(time(0));
	for(int i=0;i<NUMBER;i++)
	{
		string s;
		for(int j=0;j<33;j++)
		{
			double r = rand();
		    if(r>RAND_MAX/2)
				s.append("1");
			else
				s.append("0");
		}
		pop.push_back(s);
	}
}

step2:計算種群p(t)\ p(t)中每個個體的適應度值

即按照解碼公式進行解碼之後帶入函式中計算f(x1,x2)\ f(x_1,x_2)的值,即為本例的適應度值。相關程式碼如下(individual為自定義結構體,包含當前計算個體對應的x1,x2,f\ x_1,x_2,f三個值:

//計算適應度值
individual Fitness(string s)
{
	individual T;
	double x1a = -3.0;
	double x1b = 12.1;
	double x2a = 4.1;
	double x2b = 5.8;
	string s1 = s.substr(0,18);
	string s2 = s.substr(18,15);
	int t1 = Transform(s1);
	int t2 = Transform(s2);
	T.x1 = x1a+(x1b-x1a)*t1/(pow(2.0,18)-1);
	T.x2 = x2a+(x2b-x2a)*t2/(pow(2.0,15)-1);
	T.fit = 21.5+T.x1*sin(4*PI*T.x1) +T.x2*sin(20*PI*T.x2);
	return T;
}

step3:按某種規則p(t)\ p(t)中選擇一個子群體p(t)\ p&#x27;(t)作為產生後代的父母,用交叉運算元作用於p(t)\ p&#x27;(t)產生交叉後代集合O(t)\ O&#x27;(t),再用變異運算元作用於O(t)\ O&#x27;(t)產生變異後代集合O(t)\ O(t),計算O(t)\ O(t)中每個後代的適應度;

某種規則:本例使用輪盤賭選擇(根據每個個體適應度所佔總適應度的比例,以及產生隨機數的大小,選擇出一部分個體進入交叉池,這樣適應度大的的個體會有更大的可能性被選為父母進行交叉,優良基因得以保留下去) 下面是輪盤賭選擇部分程式碼:

//輪盤賭選擇
vector<string> choose(vector<string> population,int x)
{
	//srand(time(0));
	double fitness_sum=0;
	vector<double> q;
	vector<individual> indi_vec;
	vector<string> result;
	double p=0;
	vector<double> random;
	for (int i = 0; i < x; i++)
	{
		double r = rand();
		random.push_back(r / (double)RAND_MAX);
	}
	for(int i=0;i<population.size();i++)
	{
		individual Indi=Fitness(population[i]);
		indi_vec.push_back(Indi);
		fitness_sum=fitness_sum+Indi.fit;	
	}
	for(int i=0;i<population.size();i++)
	{
		individual Indi=indi_vec[i];
		p=p+Indi.fit/fitness_sum;
		if(i==population.size()-1)
			q.push_back(1.0);
		else
			q.push_back(p);
	}
	for(int i=0;i<random.size();i++)
	{
		for(int j=0;j<q.size();j++)
		{
			if (random[i]>q[j] && random[i] <= q[j + 1])
			{
				result.push_back(population[j + 1]);
				break;
			}		
		}
	}
	return result;
}

交叉運算元:單點交叉、兩點交叉、多點交叉、均勻交叉、部分匹配交叉、順序交叉、迴圈交叉、二維交叉…… 本例使用兩點交叉做為交叉運算元,在一對個體上選擇兩個相同的點位,切斷之後進行交換即產生兩個後代,下面是交叉部分程式碼:

//兩點交叉
vector<string> crossover(vector<string>population)
{
	//srand(time(0));
	vector<string> result;
	for(int i=0;i+1<population.size();i=i+2)
	{
		string s1=population[i];
		string s2=population[i+1];
		double r = rand();
		if (r<pc*RAND_MAX)
		{
			int i1 = (int)(rand()/RAND_MAX)*33;
			int i2 = (int)(rand() / RAND_MAX) * 33;
			if(i1<i2)
			{
				string sub1=s1.substr(i1,i2-i1);
				string sub2=s2.substr(i1,i2-i1);
				s1.replace(i1,i2-i1,sub2);
				s2.replace(i1,i2-i1,sub1);
				result.push_back(s1);
				result.push_back(s2);
			}
			else{
				if (i1 > i2)
				{
					string sub1 = s1.substr(i2, i1 - i2);
					string sub2 = s2.substr(i2, i1 - i2);
					s1.replace(i2, i1 - i2, sub2);
					s2.replace(i2, i1 - i2, sub1);
					result.push_back(s1);
					result.push_back(s2);
				}
				else
				{
					result.push_back(s1);
					result.push_back(s2);
				}
			}
		}
		else
		{
			result.push_back(s1);
			result.push_back(s2);
		}
	}
	return result;
}

變異運算元:單點變異、對換變異、移位變異、插入便宜、均勻變異、正態變異、非一致變異…… 本例使用單點變異作為變異運算元,即產生隨機數,將對應位上的二進位制編碼取反即可,下面為變異部分程式碼:

//單點變異
string mutation(string s)
{
	//srand(time(0));
	for(int i=0;i<33;i++)
	{
		double r = rand();
		if (r<pm*RAND_MAX)
		{
			if(s[i]=='0')
				s[i]='1';
			else
				s[i]='0';
		}
	}
	return s;
}

step4:用選擇運算元從集合p(t)UO(t)\ p(t)UO(t)中選出下一代種群p(t+1)\ p(t+1),令t=t+1\ t=t+1;

此步驟同樣用輪盤賭選擇作為選擇運算元選出進入下一代中的個體

step5:如演算法滿足終止條件,則輸出p(t+1)\ p(t+1)中具有最大適應度值的個體作為最優解,中止演算法。否則,進入步驟2.

終止條件:要麼迭代代數達到限制範圍,要麼很多代後的最好個體沒有繼續改進即可停止。

以下為全部程式碼(一般在執行到300+代時即可達到最優解):

#include "stdafx.h"
#include <string>
#include <time.h>
#include <windows.h>
#include <math.h>
#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

#define NUMBER 20
#define PI 3.14159265358979323

vector<string> pop;
double pc = 0.5;
double pm = 0.1;
string max_s;

//個體結構體
struct individual
{
	double x1=0;
	double x2=0;
	double fit=0;
};

//初始化種群
void init()
{
	double p=0.5;
	//srand(time(0));
	for(int i=0;i<NUMBER;i++)
	{
		string s;
		for(int j=0;j<33;j++)
		{
			double r = rand();
		    if(r>RAND_MAX/2)
				s.append("1");
			else
				s.append("0");
		}
		pop.push_back(s);
	}
}

//二進位制字串轉換成整形
int Transform(string s)
{
    int n =0;
    for(int i = 0; i<s.size(); i++)
    {
        n = n*2 + s[i]-'0';
    }
    return n;
}

//解碼並計算適應度
individual Fitness(string s)
{
	individual T;
	double x1a = -3.0;
	double x1b = 12.1;
	double x2a = 4.1;
	double x2b = 5.8;
	string s1 = s.substr(0,18);
	string s2 = s.substr(18,15);
	int t1 = Transform(s1);
	int t2 = Transform(s2);
	T.x1 = x1a+(x1b-x1a)*t1/(pow(2.0,18)-1);
	T.x2 = x2a+(x2b-x2a)*t2/(pow(2.0,15)-1);
	T.fit = 21.5+T.x1*sin(4*PI*T.x1) +T.x2*sin(20*PI*T.x2);
	return T;
}

//輪盤賭選擇
vector<string> choose(vector<string> population,int x)
{
	//srand(time(0));
	double fitness_sum=0;
	vector<double> q;
	vector<individual> indi_vec;
	vector<string> result;
	double p=0;
	vector<double> random;
	for (int i = 0; i < x; i++)
	{
		double r = rand();
		random.push_back(r / (double)RAND_MAX);
	}
	for(int i=0;i<population.size();i++)
	{
		individual Indi=Fitness(population[i]);
		indi_vec.push_back(Indi);
		fitness_sum=fitness_sum+Indi.fit;	
	}
	for(int i=0;i<population.size();i++)
	{
		individual Indi=indi_vec[i];
		p=p+Indi.fit/fitness_sum;
		if(i==population.size()-1)
			q.push_back(1.0);
		else
			q.push_back(p);
	}
	for(int i=0;i<random.size();i++)
	{
		for(int j=0;j<q.size();j++)
		{
			if (random[i]>q[j] && random[i] <= q[j + 1])
			{
				result.push_back(population[j + 1]);
				break;
			}		
		}
	}
	return result;
}

//兩點交叉
vector<string> crossover(vector<string>population)
{
	//srand(time(0));
	vector<string> result;
	for(int i=0;i+1<population.size();i=i+2)
	{
		string s1=population[i];
		string s2=population[i+1];
		double r = rand();
		if (r<pc*RAND_MAX)
		{
			int i1 = (int)(rand()/RAND_MAX)*33;
			int i2 = (int)(rand() / RAND_MAX) * 33;
			if(i1<i2)
			{
				string sub1=s1.substr(i1,i2-i1);
				string sub2=s2.substr(i1,i2-i1);
				s1.replace(i1,i2-i1,sub2);
				s2.replace(i1,i2-i1,sub1);
				result.push_back(s1);
				result.push_back(s2);
			}
			else{
				if (i1 > i2)
				{
					string sub1 = s1.substr(i2, i1 - i2);
					string sub2 = s2.substr(i2, i1 - i2);
					s1.replace(i2, i1 - i2, sub2);
					s2.replace(i2, i1 - i2, sub1);
					result.push_back(s1);
					result.push_back(s2);
				}
				else
				{
					result.push_back(s1);
					result.push_back(s2);
				}
			}
		}
		else
		{
			result.push_back(s1);
			result.push_back(s2);
		}
	}
	return result;
}

//單點變異
string mutation(string s)
{
	//srand(time(0));
	for(int i=0;i<33;i++)
	{
		double r = rand();
		if (r<pm*RAND_MAX)
		{
			if(s[i]=='0')
				s[i]='1';
			else
				s[i]='0';
		}
	}
	return s;
}

//返回本輪適應度值最大的個體
individual print_(vector<string> generation)
{
	individual max;
	for (int i = 0; i < generation.size(); i++)
	{
		individual Ind = Fitness(generation[i]);
		if (Ind.fit>max.fit)
		{
			max = Ind;
			max_s = generation[i];
		}
	}
	return max;
}

int main(int argc, _TCHAR* argv[])
{
	srand(time(0));
	individual Max;
	init();
	int j = 0;
	double MAX = 0;
	while (j < 5000)
	{
		vector<string> pop1, pop2, pop3, pop_new;		
		pop1 = choose(pop,NUMBER);
		pop2 = crossover(pop1);
		double max1 = 0;
		double max2 = 0;
		string s1;
		string s2;
		for (int i = 0; i < pop2.size(); i++)
		{
			string s = mutation(pop2[i]);
			pop3.push_back(s);
			double d = Fitness(pop2[i]).fit;
			if ( d> max1)
			{
				s1 = pop2[i];
				max1 = d;
			}
		}
		for (int i = 0; i < pop3.size(); i++)
		{
			double d = Fitness(pop3[i]).fit;
			if (d > max2)
			{
				s2 = pop3[i];
				max2 = d;
			}
		}
		pop_new = choose(pop, NUMBER-2);
		pop_new.push_back(s1);
		pop_new.push_back(s2);
		pop = pop_new;
		individual max = print_(pop);
		if (max.fit > Max.fit)
		{
			cout << j + 1 << ":" << max.x1 << ";" << max.x2 << ";" << max.fit << endl;
			Max = max;
			pop.push_back(max_s);
		}
		else
			cout << j + 1 << ":" << Max.x1 << ";" << Max.x2 << ";" << Max.fit << endl;
		j++;
	}
	system("pause");
	return 0;
}