遗传算法求解函数优化问题-基本遗传算法SGA

博客内图片文字来源于书本《遗传算法及其应用》

1.算法介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
《遗传算法及其应用》是在阅读GA过程中较好的一本算法基础方法介绍的书,想要从零开始详细的进行学习的同学们,这是很好的参考工具。
关于遗传算法,《遗传算法及其应用》一书给出了最为详尽的描述,书中也针对不同问题给出了基础的方法,例如组合优化问题中的函数优化、背包问题、货郎担问题和图论等等。

2.实例介绍

在上述内容的基础上,使用C++对函数优化问题进行求解,便于了解GA算法。这里使用的函数优化问题是无约束问题。
函数案例为:

min Z
	Z=(1.5-x_1+x_1*x_2)^2+(2.25-x_1+x_1*(x_2)^2)^2+(2.625-x_1+x_1*(x_2)^3)^2
	x->[-4.5,4.5]
最优解:Z(3,0.5)=0

2.1 编码
在示例函数当中,变量x_1和x_2均属于区间[-4.5,4.5]。使用二进制无符号整数进行编码,将变量约束空间映射到遗传空间。
为了更详细的表示遗传空间,以下使用8位二进制对变量进行编码,则编码空间整体位16位,其中前8位表示x_1的编码,其余表示x_2。
当然,这里使用5位编码也是可以的,只要能够包含约束区域即可。
2.2 解码
在2.1当中对变量进行了编码,这种编码从0000 0000开始,到1111 1111结束。
一般情况下0000 0000表示数值0,0000 00001表示数值1,以此类推。显然,无法将编码与约束进行一一对应。那么约束空间的负数和小数空间该如何进行表示呢?
假设约束空间的上限为b,下限为a,编码长度l,二进制编码转化为十进制值为x_t,实际值为x,则有:
x=a+(x_t *(b-a))/(2^l -1)
使用该方法对编码进行解码。解码方法如下:

void Decode(int X[person_number][node],int pers) {
	int X_temp = 0;
	for (int i = 0; i < 8; i++) {
		X_temp += X[pers][i] * pow(2, 8 - 1 - i);
	}
	x_code[pers][0] = XMIN + XMIN + (X_temp*(XMAX - XMIN)) / (pow(2, 8) - 1);
	X_temp = 0;
	for (int i = 8; i < 16; i++) {
		X_temp += X[pers][i] * pow(2, 16 - 1 - i);
	}
	x_code[pers][1] = XMIN + (X_temp*(XMAX - XMIN)) / (pow(2, 8) - 1);
}

为什么编码后,还需要进行解码呢?原因是将二进制码串转化为约束空间内的十进制值,便于适应度函数进行计算。
2.3 产生初始群体
对于初始群体,只需要使用rand()函数生成对应的0或1即可。
初始群体为:

void initial() {
	srand((unsigned int)time(NULL));
	for (int i = 0; i < person_number;i++) {
		for (int j = 0; j < node;j++) {
			X_code[i][j] = (rand() % (1 + 1)); 
		}
	}
}

2.4 轮盘赌选择
假设群体的适应性由一个转轮代表。
每一个染色体指定转轮中一个区域。区域的大小与该染色体的适应性分数成正比,适应性分数越高,它在转轮中对应的区域面积也越大。
为了选取一个染色体,要做的就是旋转这个轮子,当转轮停止时,指针停止在哪一区域上,就选中与它对应的那个染色体。
在这里,假设个体适应值为f_i;则个体的选择概率是f_i/sum(f_i),概率越大,被选择的可能就越大,然后计算累积概率。
工作时,使用rand()函数生成0-1之间的小数,查看小数落在哪一累积概率区间,则选择该个体。
具体工作过程如下:

void Roulette() {
	double fit_sum = 0;//适应值之和
	for (int i = 0; i < person_number; i++) {
		fit_temp[i] = Fitness(x_code, i);
		fit_sum += fit_temp[i];
	}
	//注意:目标函数是取最小值,那么适应值越小,选择的概率越高
	double fit_temp1[person_number];
	double fit_sum1 = 0;//修改适应值之和
	for (int i = 0; i < person_number; i++) {
		fit_temp1[i]=fit_sum - fit_temp[i];//调整适应值为(和-适应值)
		fit_sum1 += fit_temp1[i];
	}
	/*cout << "适应值:" << endl;
	for (int i = 0; i < person_number; i++) {
		cout<< fit_temp[i]<<" ";
	}
	cout << endl << "适应值和:" << fit_sum1 << endl;*/

	for (int i = 0; i < person_number; i++) {
		
		pro_select[i] = (int((fit_temp1[i] / fit_sum1)*1000000))/1000000.0;
	}
	pro_cumul[0] = pro_select[0];
	for (int i = 1; i < person_number; i++) {
		pro_cumul[i] = pro_cumul[i-1]+pro_select[i];
	}
	pro_cumul[person_number-1] = 1.0;
}

2.5 选择
选择操作的本质为优胜略汰,选择原则是轮盘赌方法,选择的目的是将个体两两配对,放入交配池中,等待交配生成子代。
选择操作为:

void Selection() {
	Roulette();//确定选择概率

	for (int i = 0; i < person_number;i++) {
		for (int j = 0; j < dimension;j++) {
			per_cross[i][j] = -1;//初始化配对数组->满足交叉和变异的个体
		}
	}

	//除了复制个体以外,两两配对:产生随机数->确定标号

	//两两配对
	srand((unsigned int)time(NULL));
	for (int i = 0; i < person_number/2;i++) {
		for (int j = 0; j < 2;j++) {
			double temp = (rand() % (10000 + 1)) / 10000.0;
			//查询下标
			if (temp <= pro_cumul[0]) {
				per_cross[i][j] = 0;
			}			
			else {
				for (int k = 0; k < person_number - 1; k++) {
					if (temp > pro_cumul[k]) {
						if (temp <= pro_cumul[k + 1]) {
							per_cross[i][j] = k + 1;
							break;
						}
					}

				}
			}
			
		}
	}
	/*cout << "交配池:" << endl;
	for (int i = 0; i < person_number / 2; i++) {
		for (int j = 0; j < 2; j++) {
			cout << per_cross[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;*/
}

2.6 复制
复制的目的是保留最佳个体基因,将其保留到子代,有利于减少迭代次数。
复制操作为:

void Copy(double fit_temp[person_number]) {
	//确定复制个体
	double fit_temp1[person_number];//临时适应值数组
	for (int i = 0; i < person_number; i++) {
		fit_temp1[i] = fit_temp[i];
	}
	//适应值排序->由小到大
	int fit_index[person_number];//适应值编号
	for (int i = 0; i < person_number; i++) {
		fit_index[i] = i;
	}
	for (int i = 1; i < person_number; i++) {
		for (int j = 0; j < person_number - i; j++) {
			if (fit_temp1[j + 1] < fit_temp1[j]) {
				double temp1 = fit_temp1[j];
				int temp2 = fit_index[j];
				fit_temp1[j] = fit_temp1[j + 1];
				fit_index[j] = fit_index[j + 1];
				fit_temp1[j + 1] = temp1;
				fit_index[j + 1] = temp2;
			}
		}
	}
	//确定复制个体->与目标函数有关,这里复制最小的个体
	per_copy[0] = fit_index[0];
	fitness_temp = fit_temp1[0];
	//cout << endl << "适应值:"<< fit_temp1[0]<<" "<<"最佳个体基因:" << per_copy[0] << endl;
}

2.7 交叉
交叉是GA算法的重要操作。
在这里,使用二点交叉方法。二点交叉为:
在这里插入图片描述
若交配个体中含有最佳个体,则最佳个体直接复制到子代;交配对象的子代由最佳个体的交换部分加上自身未交换部分。
交叉操作为:

void Cross() {//二点交叉方法
	//若复制个体与其他进行交叉,则复制为下一代,同时交叉一次(两种交叉结果中保留复制个体为主的子代)
	//复制-》确定交叉点-》交叉
	Copy(fit_temp);//确定复制个体
	int X_code_tem[person_number][node];//临时二进制编码值
	for (int i = 0; i < person_number;i++) {
		for (int j = 0; j < node;j++) {
			X_code_tem[i][j] = X_code[i][j];
		}
	}
	srand((unsigned int)time(NULL));
	int count = 0;//计数
	for (int i = 0; i < person_number / 2;i++) {
		double r1 = (rand() % (100 + 1)) / 100.0;
		//cout <<endl<< "随机概率:" << r1 << endl;

		int temp[node];//中转
		for (int j = 0; j < node; j++) {
			temp[j] = 0;
		}
		int X_code_tem1[2][node];
		for (int m = 0; m < 2; m++) {
			for (int n = 0; n < node; n++) {
				X_code_tem1[m][n] = 0;
			}
		}
		//cout << endl << "配对基因:"<<per_cross[i][0]<<" "<< per_cross[i][1] <<endl;
		if (r1 < pc) {//可以进行交叉
			//确定交叉点
			int cross_section[2];//存放交叉点->小号在前
			cross_section[0] = rand() % (15 + 1);
			cross_section[1] = rand() % (15 + 1);
			if ((cross_section[0] == cross_section[1]) && (cross_section[0] == 0))
				cross_section[1] += 1;
			if ((cross_section[0] == cross_section[1]) && (cross_section[0] == 15))
				cross_section[1] -= 1;
			if (cross_section[0] > cross_section[1]) {
				swap(cross_section[0], cross_section[1]);
			}
			//cout << endl << "交叉点:" << cross_section[0]<<" "<< cross_section[1] << endl;

			//有最佳基因
			if ((per_cross[i][0]==per_copy[0])&&(per_cross[i][1]!=per_copy[0])) {//有其中一个
				for (int j = 0; j < node; j++) {
					X_code_tem1[0][j] = X_code_tem[per_cross[i][0]][j];
					X_code_tem1[1][j] = X_code_tem[per_cross[i][1]][j];
				}

				/*cout << endl;
				for (int k = 0; k < 2; k++) {
					for (int j = 0; j < node; j++) {
						cout << X_code_tem1[k][j] << " ";
					}
					cout << endl;
				}
				cout << endl;*/

				for (int j = cross_section[0]; j < cross_section[1] + 1; j++) {
					X_code_tem1[1][j] = X_code_tem1[0][j];
				}

				for (int j = 0; j < node; j++) {
					X_code[count][j] = X_code_tem1[0][j];
				}
				count++;
				for (int j = 0; j < node; j++) {
					X_code[count][j] = X_code_tem1[1][j];
				}
				count++;
			}
			else if ((per_cross[i][1] == per_copy[0]) && (per_cross[i][0] != per_copy[0])) {//有其中一个
				for (int j = 0; j < node; j++) {
					X_code_tem1[0][j] = X_code_tem[per_cross[i][0]][j];
					X_code_tem1[1][j] = X_code_tem[per_cross[i][1]][j];
				}

				/*cout << endl;
				for (int k = 0; k < 2; k++) {
					for (int j = 0; j < node; j++) {
						cout << X_code_tem1[k][j] << " ";
					}
					cout << endl;
				}
				cout << endl;*/

				for (int j = cross_section[0]; j < cross_section[1] + 1; j++) {
					X_code_tem1[0][j] = X_code_tem1[1][j];
				}

				for (int j = 0; j < node; j++) {
					X_code[count][j] = X_code_tem1[0][j];
				}
				count++;
				for (int j = 0; j < node; j++) {
					X_code[count][j] = X_code_tem1[1][j];
				}
				count++;
			}
			else {//没有最佳基因
				for (int j = 0; j < node; j++) {
					X_code_tem1[0][j] = X_code_tem[per_cross[i][0]][j];
					X_code_tem1[1][j] = X_code_tem[per_cross[i][1]][j];
				}

				/*cout << endl;
				for (int k = 0; k < 2; k++) {
					for (int j = 0; j < node; j++) {
						cout << X_code_tem1[k][j] << " ";
					}
					cout << endl;
				}
				cout << endl;*/

				for (int j = cross_section[0]; j < cross_section[1] + 1; j++) {
					temp[j] = X_code_tem1[0][j];
				}
				for (int j = cross_section[0]; j < cross_section[1] + 1; j++) {
					X_code_tem1[0][j] = X_code_tem1[1][j];
				}
				for (int j = cross_section[0]; j < cross_section[1] + 1; j++) {
					X_code_tem1[1][j] = temp[j];
				}

				for (int j = 0; j < node; j++) {
					X_code[count][j] = X_code_tem1[0][j];
				}
				count++;
				for (int j = 0; j < node; j++) {
					X_code[count][j] = X_code_tem1[1][j];
				}
				count++;
			}
		
		}
		else {//不交叉,直接复制
			for (int j = 0; j < node; j++) {
				X_code_tem1[0][j] = X_code_tem[per_cross[i][0]][j];
				X_code_tem1[1][j] = X_code_tem[per_cross[i][1]][j];
			}

			/*cout << endl;
			for (int k = 0; k < 2; k++) {
				for (int j = 0; j < node; j++) {
					cout << X_code_tem1[k][j] << " ";
				}
				cout << endl;
			}
			cout << endl;*/

			for (int j = 0; j < node; j++) {
				X_code[count][j] = X_code_tem1[0][j];
			}
			count++;
			for (int j = 0; j < node; j++) {
				X_code[count][j] = X_code_tem1[1][j];
			}
			count++;
		
		}
	}

	/*cout << "交叉个体:" << endl;
	for (int i = 0; i < person_number; i++) {
		for (int j = 0; j < node; j++) {
			cout << X_code[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;*/
}

3.约束问题优化

对于带约束的函数优化问题,可以参照PSO算法求解的内容,使用罚函数的方法进行约束处理,转变为无约束问题求解。

此外,遗传算法的高级实现方式以及并行算法,是对求解过程的改进,与SGA方法相比,改进的方法求解速度更快,准确度更高,也能避免局部最优的发生。

以上内容,如有引用不当,将立即修改。

欢迎各位同学进行交流!

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南音小榭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值