遗传算法求解优化问题实例一(20200923)

优化问题概述:

m a x f ( x 1 , x 2 ) = 21.5 + x 1 sin ⁡ ( 4 π x 1 ) + x 2 sin ⁡ ( 20 π x 2 ) maxf(x_1,x_2) = 21.5+x_1\sin(4{\pi}x_1) +x_2\sin(20{\pi}x_2) maxf(x1,x2)=21.5+x1sin(4πx1)+x2sin(20πx2)
− 3.0 ≤ x 1 ≤ 12.1 , 4.1 ≤ x 2 ≤ 5.8 -3.0≤x_1≤12.1, 4.1≤x_2≤5.8 3.0x112.1,4.1x25.8

遗传算法主要分为五步:

        首先必须了解种群结构:
        N: 种群的规模,即个体的数目
         L: 每个个体染色体中的基因数目(L=L1+L2)
        L1:个体染色体基因编码的前面L1位可用来表示x1
         L2:个体染色体基因编码的后L2位可用来表示x1
        输入数据:data.txt文件
        C++代码自定义类:

头文件 源文件
Ranseti Ranseti.h Ranseti.cpp
X X.h X.cpp
Zhongqun Zhongqun.h Zhongqn.cpp
Zajiaodui Zajiaodui.h Zajiaodui.cpp

最后运行main.cpp即可。

第一步,个体的染色体编码

        很明显,优化问题的可能解为实数对:(x1,x2),若设 aj ≤ xj ≤bj , 所要求的的精度为小数点后t位,这要求将区间[aj , bj]划分为至少(bj-aj)10t份,假设表示变量 xj的二进制串的长度用 lj表示,则l j可取为满足下列不等式的最小正整数m:
( b j − a j ) 1 0 t ≤ 2 m − 1 (b_j - a_j)10^t≤2^m-1 (bjaj)10t2m1
        即有 2 l j − 1 − 1 < ( b j − a j ) 1 0 t ≤ 2 l j − 1 2^{l_j-1}-1<(b_j - a_j)10^t\leq2^{l_j}-1 2lj11<(bjaj)10t2lj1
        将xj的二进制表示转换为十进制表示可按照下式进行计算:
x j = a j + d e c i m a l ( s u b s t r i n g j ) ) × b j − a j 2 l j − 1 x_j=a_j+decimal(substring_j))\times\frac{b_j-a_j}{2^{l_j}-1} xj=aj+decimal(substringj))×2lj1bjaj
        其中,decimal(substringj)表示变量xj的二进制子串substringj对应点额十进制数。
        这样一来,我们可以计算出变量x1和x2的二进制位串长度l1和l2后,便可以得到表示问题可能解(x1,x2)的二进制位串的长度 l=l1+l2 =18+15=33.因此,二进制串的前18位表示变量x1,后15位表示变量x2
例子:
        给定如下33位二进制位串:
010001001011010000 ⏟ 前 18 位 111110010100010 ⏟ 后 15 位 ⏞ 33 位 \overbrace{\underbrace{010001001011010000}_{前18位}\underbrace{111110010100010}_{后15位}}^{33位} 18 01000100101101000015 111110010100010 33
        那么前18位所表示变量x1的值为:
x 1 = − 3.0 + d e c i m a l ( 010001001011010000 ) × 12.1 − ( − 3.0 ) 2 18 − 1 = − 3.0 + 70352 × 15.1 2 18 − 1 = − 3.0 + 4.052426 = 1.052426 x_1=-3.0+decimal(010001001011010000)\times\frac{12.1-(-3.0)}{2^{18}-1}\\ =-3.0+70352\times\frac{15.1}{2^{18}-1}=-3.0+4.052426=1.052426 x1=3.0+decimal(010001001011010000)×218112.1(3.0)=3.0+70352×218115.1=3.0+4.052426=1.052426
x 2 = 4.1 + d e c i m a l ( 111110010100010 ) × 5.8 − 4.1 2 15 − 1 = 4.1 + 31906 × 1.7 2 15 − 1 = 4.1 + 1.655330 = 5.755330 x_2=4.1+decimal(111110010100010)\times\frac{5.8-4.1}{2^{15}-1}\\ =4.1+31906\times\frac{1.7}{2^{15}-1}=4.1+1.655330=5.755330 x2=4.1+decimal(111110010100010)×21515.84.1=4.1+31906×21511.7=4.1+1.655330=5.755330
所以,二进制位串(010001001011010000111110010100010)所表示问题的可能解为(x1,x2)=(1.052426,5.755330)
染色体类的头文件:Ranseti.h

// Ranseti.h
#ifndef RANSETI_H
#define RANSETI_H
#include "X.h"
const int L = 33;
const int L1 = 18;// -3.0≤ x1 ≤ 12.1
const int L2 = 15;// 4.1≤ x2 ≤ 5.8
class Ranseti
{
    
    
public:
	int shiti[L];
	Ranseti(); //初始化构造函数
	void shuchu();//打印函数
	void shuchu1();//第二种打印函数
	X f();//由染色体计算X的函数,X包含x1和x2
};
#endif

染色体类的源文件:Ranseti.cpp

// Ranseti.cp
# include "Ranseti.h"
# include <iostream>
using namespace std;

Ranseti::Ranseti()
{
	for (int i = 0; i < L; i++)
		shiti[i] = 0;
}

void Ranseti::shuchu()
{
	for (int i = 0; i < L; i++)
		std::cout << shiti[i];
	std::cout << endl;
}

void Ranseti::shuchu1()
{
	for (int i = 0; i < L; i++)
	{
		if (i % 4 == 0) std::cout << "   ";
		std::cout << shiti[i];
	}
	std::cout << endl;
}

X Ranseti::f()
{
		X Xgeti;
		for (int j = 0; j < L1; j++)
			Xgeti.x[0] += shiti[j] * pow(2, L1 - 1 - j);
		Xgeti.x[0] = -3.0 + Xgeti.x[0] * 15.1 / (pow(2, L1) - 1);//染色体的前L1位二进制对应的变换值
		for (int j = L1; j < L; j++)
			Xgeti.x[1] += shiti[j] * pow(2, L - 1 - j);
		Xgeti.x[1] = 4.1 + Xgeti.x[1] * 1.7 / (pow(2, L2) - 1);//染色体的后L2位二进制对应的变换值
		return Xgeti;
}

第二步,产生初始种群

        假定初始种群的规模为N=20,随机产生初始种群如下:
v1=(100110100000001111111010011011111),
v2=(111000100100110111001010100011010),
v3=(000010000011001000001010111011101),
v4=(100011000101101001111000001110010),
v5=(000111011001010011010111111000101),
v6=(000101000010010101001010111111011),
v7=(001000100000110101111011011111011),
v8=(100001100001110100010110101100111),
v9=(010000000101100010110000001111100),
v10=(000001111000110000011010000111011),
v11=(011001111110110101100001101111000),
v12=(110100010111101101000101010000000),
v13=(111011111010001000110000001000110),
v14=(010010011000001010100111100101001),
v15=(111011101101110000100011111011110),
v16=(110011110000011111100001101001011),
v17=(011010111111001111010001101111101),
v18=(011101000000001110100111110101101),
v19=(000101010011111111110000110001100),
v2=(101110010110011110011000101111110)
种群类的头文件:Zhongqun.h

// Zhongqun.h
#ifndef ZHONGQUN_H
#define ZHONGQUN_H
#include "Ranseti.h"
#define N 20
class Zhongqun
{
    
    
public:
	Ranseti r[N];
	void shuchu();//打印函数
	void shuchu1();//第二种打印方式
};
#endif

种群类的源文件:Zhongqun.cpp

// Zhongqun.cpp
#include "Ranseti.h"
#include "Zhongqun.h"
#include <iostream>
using namespace std;

void Zhongqun::shuchu()
{
	for (int i = 0; i < N; i++)
	{
		r[i].shuchu();
	}
}

void Zhongqun::shuchu1()
{
	for (int i = 0; i < N; i++)
	{
		std::cout << "V" << i + 1 << "  ";
		r[i].shuchu1();
	}
}

第三步,计算染色体个体的适应值

(1)首先,需要将二进制位串的染色体转换为所表示问题的可能解(x1,x2)
(2)然后,计算个体x=(x1,x2)的适应值eval(x1,x2)
个体类的头文件:X.h

// X.h
#ifndef X_H
#define X_H
class X
{
    
    
public:
	double x[2];
	X(); //初始化构造函数
	double eval(); //根据x1和x2来计算个体适应值的函数
};
#endif

个体类的源文件:X.cpp

// X.cpp
#include "X.h"
#define _USE_MATH_DEFINES //在math.h前定义
#include <math.h>
X::X()
{
	for (int i = 0; i < 2; i++)
		x[i] = 0;
}

double X::eval()//计算染色体个体适应值的函数
{
	return 21.5 + x[0]*sin(4 * M_PI*x[0]) + x[1] *sin(20 * M_PI*x[1]);
}

第四步,父体选择(轮盘赌选择法)

        轮盘赌选择(roulette wheel selection)是遗传算法中使用最多的选择策略之一。它通过模拟博彩游戏中的轮盘赌,将一个轮盘划分为N个扇形区域,每个扇形表示种群中的一个染色体,而每个扇形的面积与它所表示的染色体的适应值成正比,如下图所示。为了选择种群中的额个体,设想有一个指针指向轮盘,转动轮盘,当轮盘停止后,指针所指向的染色体被选择。因为一个染色体的适应值越大表示该染色体的扇形面积就越大,因此它被选择的可能性也就越大。

在这里插入图片描述

图 表示6个染色体的轮盘

轮盘赌选择可以如下实现:
(1)计算种群中所有染色体适应值之和,
F = ∑ k = 1 N e v a l ( v k ) F = \sum_{k=1}^Neval(v_k) F=k=1Neval(vk)
(2)计算每个染色体的选择概率,
p k = e v a l ( v k ) F , k = 1 , 2 , . . . , N p_k = \frac{eval(v_k)}{F},k=1,2,...,N pk=Feval(vk),k=1,2,...,N
(3)计算每个染色体的累计概率,
q k = ∑ j = 1 k p j , k = 1 , 2 , . . . , N q_k = \sum_{j=1}^kp_j,k=1,2,...,N qk=j=1kpjk=1,2,...,N
(4)转动轮盘N次,从中选择N个染色体。
选择过程可如下实现:用[0,1]中的一个随机数r来模拟转动一次轮盘,轮盘停止转动后指针所指向的位置。若r≤q1,这说明指针指向第一个扇形,这时选择第一个染色体v1,一般若qk-1<r≤qk,这说明指针指向第k个扇形,这时选择第k个染色体vk

第五步,遗传算子

        遗传算子有两种:杂交算子变异算子

        杂交

        (1)杂交算子:使用单点杂交,该方法对两个父体进行杂交,杂交后产生两个后代个体。
        单点杂交过程如下:设二进制位串的长度为L,首先随机地产生一个整数pos作为杂交点的额位置,pos∈[1,L-1],然后将两个附体在该杂交点右边的子串进行交换,产生两个后代个体。
        例如,给定两个父体如下:
v 1 = ( 10011011010010110 ∣ 1000000010111001 ) v_1=(10011011010010110 | 1000000010111001) v1=(100110110100101101000000010111001)
v 2 = ( 00101101010000110 ∣ 0010110011001100 ) v_2=(00101101010000110 | 0010110011001100) v2=(001011010100001100010110011001100)
假设再叫点的位置为17,那么交换两个父体第17个基因右边的子串后,所得到的两个后代如下:
v 1 ′ = ( 10011011010010110 ∣ 0010110011001100 ) v_1'=(10011011010010110 | 0010110011001100) v1=(100110110100101100010110011001100)
v 2 ′ = ( 00101101010000110 ∣ 1000000010111001 ) v_2'=(00101101010000110 | 1000000010111001) v2=(001011010100001101000000010111001)
杂交对类的头文件:Zajiaodui.h

// Zajiaodui.h
#ifndef ZAJIAODUI_H
#define ZAJIAODUI_H
#include "Ranseti.h"
class Zajiaodui
{
    
    
public:
	Ranseti r[2];
	Zajiaodui();//构造函数
	void shuchu();//打印函数
	void shuchu1();//第二种打印函数
};
#endif

杂交对类的源文件:Zajiaodui.cpp

//Zajiaodui.cpp
#include "Zajiaodui.h"
#include "Ranseti.h"

Zajiaodui::Zajiaodui()
{
	for (int i = 0; i < L; i++)
	{
		r[0].shiti[i] = 0;
		r[1].shiti[i] = 0;
	}
}

void Zajiaodui::shuchu()
{
	r[0].shuchu();
	r[1].shuchu();
}

void Zajiaodui::shuchu1()
{
	r[0].shuchu1();
	r[1].shuchu1();
}

        变异

        (2)变异算子:变异算子的目的在于引入种群中染色体的多样性,防止算法的过早收敛。
        变异算子以某一预先指定的概率pm对种群中染色体的每个基因进行变异。当染色体的某一基因被选择进行变异时,若该位位1,则变为0,否则变为1.
        例如,给定下列染色体v1
v 1 = ( 10011011010010110 1 ⏟ 第 18 位 010000010111001 ) v_1 = (10011011010010110\underbrace{1}_{第18位}010000010111001) v1=(1001101101001011018 1010000010111001)
        若选择v1的第18个基因进行变异,因为该位基因为1,所以将其变为0,得到一个新的染色体v’1,
v 1 ′ = ( 10011011010010110 0 ⏟ 第 18 位 010000010111001 ) v'_1 = (10011011010010110\underbrace{0}_{第18位}010000010111001) v1=(1001101101001011018 0010000010111001)
        概率pm是期望改变的种群中染色体的基因个数与基因总数的百分比。本例中,种群中的基因总数为L×N=33×20=660,若pm=0.01,则每一代平均有6.6个基因发生改变。
        在遗传算法中应用变异算子过程如下:对种群中的每一个染色体的每一基因,产生一个随机数r,若r<pm,那么该基因进行变异,否则不进行变异。设变异概率pm=0.01,所产生的的660个随机数中有5个比0.01小,假设这5个个体如下表所示。

随机数序号 随机数 染色体号 染色体中基因的位置
112 0.000213 4 13
349 0.009945 11 19
418 0.008809 13 22
429 0.005425 13 33
602 0.002836 19 8

最后

最终的主程序文件为main.cpp
主函数文件:main.cpp


//main.cpp
#include <iostream>
#include <fstream>
#include<string>
#define _USE_MATH_DEFINES //在math.h前定义
#include <math.h>
#include<iomanip>
#include "Ranseti.h"
#include "X.h"
#include "Zajiaodui.h"
#include "Zhongqun.h"
using namespace std;


Zajiaodui Zajiao(Ranseti r1, Ranseti r2, int pos)//根据杂交点位置pos来对两个染色体基因右边的子串进行交换的函数
{
    
    
	Zajiaodui Rdui;
	for (int i = pos; i < L; i++)
	{
    
    
		int temp;
		temp = r1.shiti[i];
		r1.shiti[i] = r2.shiti[i];
		r2.shiti[i] = temp;
	}
	Rdui.r[0] = r1; Rdui.r[1] = r2;
	return Rdui;
}
Zhongqun Bianyi(int randomxuhao[5], Zhongqun zq)//染色体变异函数
{
    
    
	for (int i = 0; i < 5; i++)
	{
    
    
		int ransetihao = randomxuhao[i] / L, yushu = randomxuhao[i] % L;
		if (yushu == 0) zq.r[ransetihao - 1].shiti[L - 1] = 1 - zq.r[ransetihao - 1].shiti[L - 1];
		else if (yushu > 0)
		{
    
    
			ransetihao += 1;
			zq.r[ransetihao - 1].shiti[yushu - 1] = 1 - zq.r[ransetihao - 1].shiti[yushu - 1];
		}
	}
	return zq;
}
int main()
{
    
    
	std::cout << "Hello World!" << endl;
	//第一步,产生初始种群:N个染色体
	Ranseti r[N];//初始化N个染色体
	char buf[L + 1];
	for (int i = 0; i < L; i++)
		buf[i] = 'a';
	int j = 0;
	ifstream f("E:\\计算智能代码\\data.txt", ios::in);
	if (!f.fail())
	{
    
    
		while (!f.eof())
		{
    
    
			f >> buf;
			for (int k = 0; k < L; k++)
				r[j].shiti[k] = buf[k] - '0';
			j++;
		}
	}
	else
		cout << "文件不存在" << endl;
	f.close();
	std::cout << "输入的" << N << "条染色体如下" << endl;
	for (int i = 0; i < N; i++)
	{
    
    
		std::cout << i + 1 << " ";
		r[i].shuchu1();
	}
	//第二步,计算染色体个体的适应值
	std::cout << "计算适应值如下:" << endl;
	double evalvalue[N];//适应值数组
	for (int i = 0; i < N; i++)
	{
    
    
		X xgeti = r[i].f();
		cout << "eval(v" << (i + 1) << ")f(" << setiosflags(ios::fixed) << setprecision(6) << xgeti.x[0]
			 << "," << setiosflags(ios::fixed) << setprecision(6) << xgeti.x[1]
			 << ") = " << setiosflags(ios::fixed) << setprecision(6) << xgeti.eval() << endl;
		evalvalue[i] = xgeti.eval();
	}
	//第三步,进行轮盘赌选择来选择父体
	double F = 0; //忠犬所有染色体适应值之和
	double p[N], q[N];
	for (int i = 0; i < N; i++)
	{
    
    
		p[i] = 0; q[i] = 0;
		F += evalvalue[i];//计算染色体适应值之和
	}
	std::cout << "染色体适应值之和为: " << F << endl;
	for (int i = 0; i < N; i++)
	{
    
    
		p[i] = evalvalue[i] / F;
	}
	for (int i = 0; i < N; i++)
	{
    
    
		for (int j = 0; j <= i; j++)
			q[i] += p[j];
	}
	for (int i = 0; i < N; i++)
	{
    
    
		std::cout << "第" << i + 1 << "个染色体的" << "选择概率: " << p[i] << "累计概率: " << q[i] << endl;
	}
	int rl[N];//选择的N个染色体的上一代索引的数组
	double random[N] = {
    
     0.513870,0.175741,0.308652,0.534534,0.947628,
						0.171736,0.702231,0.226431,0.494773,0.424720,
						0.703899,0.389647,0.277226,0.368071,0.983437,
						0.005398,0.765682,0.646473,0.767139,0.780237
						};
	for (int i = 0; i < N; i++)
	{
    
    
		//轮盘赌选择第i个染色体
		j = 0;
		while (random[i] > q[j]) j++;//qj<r<+qj+1
		rl[i] = j;//选择的第i个染色体对应的上一代的索引
	}
	for (int i = 0; i < N; i++)
	{
    
    
		std::cout << "选择的第v'" << i + 1 << "个染色体";
		for (int j = 0; j < L; j++)
			std::cout << r[rl[i]].shiti[j];
		std::cout << "(v" << rl[i] + 1 << ")" << endl;
	}
	Ranseti r_new[N];//父体轮盘赌选择后的新一代染色体
	Zhongqun zq_new;
	for (int i = 0; i < N; i++)
	{
    
    
		r_new[i] = r[rl[i]];
	}
	//下面进行遗传,分为杂交和变异
	double random1[N] = {
    
     0.822951,0.151932,0.625477,0.314685,0.346901,
						0.917204,0.519760,0.401154,0.606758,0.785402,
						0.031523,0.869921,0.166525,0.674520,0.758400,
						0.581893,0.389248,0.200232,0.355635,0.826927
						};
	double Pc = 0.25;//,若random1[k]<Pc则选择第k个染色体
	int zajiao_ranseti_geshu = 0;
	for (int i = 0; i < N; i++)
	{
    
    
		if (random1[i] < Pc)zajiao_ranseti_geshu++;
	}//先统计满足条件的杂交染色体个数
	cout << "共选出" << zajiao_ranseti_geshu << "个杂交染色体!" << endl;
	for (int i = 0; i < N; i++)
	{
    
    
		if (random1[i] < Pc)
		{
    
    
			cout << "选择了" << i + 1<<"    ";
			r_new[i].shuchu1();
		}
	}//先统计满足条件的杂交染色体个数
	//下面两两配对进行杂交,这里选择pos1=9,211染色体杂交,pos2=201318染色体杂交
	int pos1 = 9, pos2 = 20;
	Zajiaodui z1, z2;
	z1 = Zajiao(r_new[1], r_new[10], pos1);
	z2 = Zajiao(r_new[12], r_new[17], pos2);
	std::cout << "杂交后的结果如下:" << endl;
	z1.shuchu1();
	z2.shuchu1();
	r_new[1] = z1.r[0]; r_new[10] = z1.r[1];
	r_new[12] = z2.r[0]; r_new[17] = z2.r[1];

	std::cout << "杂交后的种群染色体如下:" << endl;
	for (int i = 0; i < N; i++)
	{
    
    
		zq_new.r[i] = r_new[i];
	}
	zq_new.shuchu1();
	//下面进行变异
	int Gene_num = L*N;//基因总数
	double Pm = 0.01;//变异概率
	//这里假设所产生的的660个随机数中有5个比0.01小,进行变异
	int randomxuhao[5] = {
    
    112,349,418,429,602};
	Zhongqun zq_new1 = Bianyi(randomxuhao, zq_new);
	std::cout << "变异后的种群染色体如下:" << endl;
	zq_new1.shuchu1();

	//最后一步,计算新一代种群染色体个体的适应值
	std::cout << "计算适应值如下:" << endl;
	double evalvalue_new[N];//适应值数组
	for (int i = 0; i < N; i++)
	{
    
    
		X xgeti = zq_new1.r[i].f();
		cout << "eval(v" << (i + 1) << ")f(" << setiosflags(ios::fixed) << setprecision(6) << xgeti.x[0]
			<< "," << setiosflags(ios::fixed) << setprecision(6) << xgeti.x[1]
			<< ") = " << setiosflags(ios::fixed) << setprecision(6) << xgeti.eval() << endl;
		evalvalue_new[i] = xgeti.eval();
	}
	double F_new = 0;
	for (int i = 0; i < N; i++)
	{
    
    
		F_new += evalvalue_new[i];
	}
	std::cout << "上一代种群的染色体适应值之和为: " << F << endl;
	std::cout << "新一代种群的染色体适应值之和为: " << F_new << endl;
	std::cout << "至此完成了种群的一次演化" << endl;
	return 0;
}

运行结果:

猜你喜欢

转载自blog.csdn.net/jing_zhong/article/details/108760373
今日推荐