如何用遗传算法求解:MAX f(x)=x3−60x2+900x+100 f ( x ) = x 3 − 60 x 2 + 900 x + 100 ,x∈[0,30] & x∈Integer
函数图像:
1.介绍
遗传算法(Genetic Algorithm)遵循『适者生存』、『优胜劣汰』的原则,是一类借鉴生物界自然选择和自然遗传机制的随机化搜索算法。
遗传算法模拟一个人工种群的进化过程,通过选择(Selection)、交叉(Crossover)以及变异(Mutation)等机制,在每次迭代中都保留一组候选个体,重复此过程,种群经过若干代进化后,理想情况下其适应度达到近似最优的状态。
自从遗传算法被提出以来,其得到了广泛的应用,特别是在函数优化、生产调度、模式识别、神经网络、自适应控制等领域,遗传算法发挥了很大的作用,提高了一些问题求解的效率。
2.遗传算法组成
- 1.参数编码
- 2.初始群体
- 3.适应度函数
- 4.遗传操作
- 5.控制参数
2.1 编码与解码
实现遗传算法的第一步就是明确对求解问题的编码和解码方式。
对于函数优化问题,一般有两种编码方式,各具优缺点:
- 实数编码:直接用实数表示基因,容易理解且不需要解码过程,但容易过早收敛,从而陷入局部最优
- 二进制编码:稳定性高,种群多样性大,但需要的存储空间大,需要解码且难以理解
对于求解函数最大值问题,我选择的是二进制编码。
以我们的目标函数 f(x)=x3−60x2+900x+100 f ( x ) = x 3 − 60 x 2 + 900 x + 100 , x∈[0,30] & x∈Integer 为例。
由x的取值范围可以得取最小编码长度为5,表示范围[0,31]。
一开始,这些二进制串是随机生成的。
一个这样的二进制串代表一条染色体串chromosome,这里染色体串的长度为5。
2.2 个体与种群
『染色体』表达了某种特征,这种特征的载体,称为『个体』。
对于本次实验所要解决的一元函数最大值求解问题,个体可以用上面构造的染色体表示,一个个体里有一条染色体。
许多这样的个体组成了一个种群,其含义是一个一维点集(x轴上[0,30]的30个点)。
2.3 适应度函数
遗传算法中,一个个体(解)的好坏用适应度函数值来评价,在本问题中,f(x)就是适应度函数。
适应度函数值越大,解的质量越高。
适应度函数是遗传算法进化的驱动力,也是进行自然选择的唯一标准,它的设计应结合求解问题本身的要求而定。
2.4 遗传算子
我们希望有这样一个种群,它所包含的个体所对应的函数值都很接近于f(x)在[0,30]上的最大值,但是这个种群一开始可能不那么优秀,因为个体的染色体串是随机生成的。
如何让种群变得优秀呢?
不断的进化。
每一次进化都尽可能保留种群中的优秀个体,淘汰掉不理想的个体,并且在优秀个体之间进行染色体交叉,有些个体还可能出现变异。
种群的每一次进化,都会产生一个最优个体。种群所有世代的最优个体,可能就是函数f(x)最大值对应的定义域中的点。
如果种群无休止地进化,那总能找到最好的解。但实际上,我们的时间有限,通常在得到一个看上去不错的解时,便终止了进化。
对于给定的种群,如何赋予它进化的能力呢?
- 首先是选择(selection)
- 选择操作是从前代种群中选择多对较优个体,一对较优个体称之为一对父母,让父母们将它们的基因传递到下一代,直到下一代个体数量达到种群数量上限
- 在选择操作前,将种群中个体按照适应度从小到大进行排列
- 这里采用轮盘赌选择方法,各个个体被选中的概率与其适应度函数值大小成正比
- 轮盘赌选择方法具有随机性,在选择的过程中可能会丢掉较好的个体,所以可以使用精英机制,将前代最优个体直接选择
其次是交叉(crossover)
- 两个待交叉的不同的染色体(父母)根据交叉概率(cross_rate)按某种方式交换其部分基因
- 这里采用单点交叉法
最后是变异(mutation)
- 染色体按照变异概率(mutate_rate)进行染色体的变异
- 这里采用单点变异法
一般来说,交叉概率(cross_rate)
比较大,变异概率(mutate_rate)
极低。像求解函数最大值这类问题,我设置的交叉概率(cross_rate)
是0.6,变异概率(mutate_rate)
是0.01。
因为遗传算法相信2条优秀的父母染色体交叉更有可能产生优秀的后代,而变异的话产生优秀后代的可能性极低,不过也有存在可能一下就变异出非常优秀的后代。这也是符合自然界生物进化的特征的。
3.遗传算法基本流程
4.代码实现(java)
import java.util.Random;
/**
* 遗传算法求解:MAX f(x)=x*x*x-60*x*x+900*x+100,x∈[0,30] & x∈Integer
*
* 选择个体方法:轮盘赌选择
* 交叉方法:一点交叉
* 变异方法:位点变异
*
*/
public class GeneticAlgorithm {
/*
终止条件
*/
private final static int MAXPOPULATION = 500;//种群最大繁殖代数
private final static double MININUM = 0.0000000000000001;//适应度差异最小值
public static String[][] population = new String[20][5]; //种群:个数为20,编码长度为5
public static Double[][] populationData = new Double[20][3]; //种群的适应度populationDate[][0]、选择概率populationDate[][1]、累计概率populationDate[][2]
public static int[] individual =new int[10]; //用来进行繁殖下一代的个体标号
public static int generation = 1; //繁殖代数
//得到种群的适应度、选择概率、累计概率
public static void getPopulationData(){
Double fitnessSum =0.0;//适应度总和
for(int i = 0; i < 20; i++){//得到各个个体的适应度
int x = binaryToDecimal(population[i]);
populationData[i][0] = Double.valueOf(x*x*x-60*x*x+900*x+100);//适应度函数
fitnessSum += populationData[i][0];
}
for(int j = 0; j < 20; j++){//得到各个个体的选择概率
populationData[j][1] = populationData[j][0] / fitnessSum;
}
Double temp = 0.0;
for(int k = 0; k < 20; k ++){//得到各个个体的累计概率
temp += populationData[k][1];
populationData[k][2] = temp;
}
}
//得到用于繁殖下一代的个体
public static void getIndividual(){
for(int i = 0; i < 10; i ++){
Double r = Math.random();
for(int j = 0;j < 20; j ++){
if(populationData[j][2] >= r){
individual[i] = j;
break;
}
}
}
}
//得到初始种群
public static void initGeneration(){
for(int i = 0; i < 20; i ++){
for(int j =0;j<5;j++){
Random r = new Random();
population[i][j] = Integer.toString(r.nextInt(2));//随机生成0-2之间的证书
}
if(!isLegal(population[i])){
i--;
}
}
}
//判断个体是否合法,返回true则说明合法,返回false则说明非法
public static boolean isLegal(String[] population){
for(int i = 0; i < population.length; i++){
//因为x属于[0,30],当二进制位数全为1,即为31,越界。
if("0".equals(population[i])){
return true;
}
}
return false;
}
//把个体的二进制编码转换为十进制数
public static int binaryToDecimal(String[] binary){
int sample[] = {16,8,4,2,1};
int decimal =0 ;
for(int i = 0; i < binary.length; i++){
decimal += Integer.parseInt(binary[i]) * sample[i];
}
return decimal;
}
//得到种群中最优秀的个体的选择率
public static Double getMaxRate(){
Double max = 0.0;
for(int i = 0; i < 20; i++){
if(populationData[i][1] >= max){
max = populationData[i][1];
}
}
return max;
}
//产生下一代
public static void getNext(){
//交叉
for(int i = 0; i <= 8; i += 2){//从左至右相邻两个个体进行交配
String[] a = population[individual[i]];
String[] b = population[individual[i+1]];
Random r = new Random();
int position = r.nextInt(4);//交叉点的位置,从右往左依次为0,1,2,3
for(int j = position; j >= 0; j--){
String temp = a[4-j];
a[4-j] = b[4-j];
b[4-j] = temp;
}
}
//变异 发生两次变异,每次从新产生的后代中随机选取一位,并从这一位的5位编码中随机选取一位进行取反
for(int k = 0; k < 2; k ++){
Random r = new Random();
int fi = r.nextInt(10);
Random r1 = new Random();
int pi = r1.nextInt(5);
if("0".equals(population[individual[fi]][pi])){
population[individual[fi]][pi] ="1";
}else{
population[individual[fi]][pi] ="0";
}
}
generation ++;
}
//根据maxRate1得到对应的个体
public static void getResult(Double r){
for(int i=0;i<20;i++){
if(populationData[i][1]==r){
System.out.println("最优质的解:"+binaryToDecimal(population[i]));
break;
}
}
}
public static void main(String[] args) {
Double maxRate;
Double maxRate1;
initGeneration(); //得到初始种群
getPopulationData(); //得到初始种群的数据
getIndividual(); //得到用于繁殖的个体
maxRate = getMaxRate(); //得到初始种群中最优秀个体的选择率
getNext(); //得到下一代
getPopulationData(); //得到下一代的数据
maxRate1 =getMaxRate(); //得到下一下中最优秀个体的选择率
while(maxRate1 - maxRate > MININUM && generation < MAXPOPULATION){ //不满足终止条件,继续培育下一代
maxRate = getMaxRate();
getIndividual();
getNext();
getPopulationData();
maxRate1 = getMaxRate();
}
getResult(maxRate1); //得到结果
}
}
运行结果:在8~12之间
结合最上面的函数图像看,最优解为10
特别感谢 sjyan 关于遗传算法的讲解。
https://www.zhihu.com/question/23293449/answer/120220974