遗传算法

如何用遗传算法求解:MAX f(x)=x360x2+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)=x360x2+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

  • 3
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值