算法学习之遗传算法-JAVA简单实例

遗传算法概念:

​ 基于达尔文的进化论,物竞天择,适者生存;认为生物总是向着更加贴合于环境的方向进化;通过各种基因的遗传、杂交、变异、复制等手段,慢慢使整个种群更加贴合于自然环境;遗传算法也是模拟生物的遗传、杂交、变异、复制手段逐渐进化为最优解!

名词概念解析:

基因和染色体:

染色体在数学建模上可以看作是可行解,例如 3x+4y+5z<100,它的可行解为[1,2,3]、[1,3,2]、[3,2,1],那么这个可行解就是它的染色体。每一个染色体有无数个基因控制,所以在这些可行解中,里面的每一元素称之为基因。

适应度函数:

在自然界中,生物的进化总是遵循优胜略汰,适者生存;一些适应不了环境的个体和染色体逐渐被淘汰,而那些基因强大,能够很好的适应环境的个体和染色体则被保留和遗传;同样遗传算法 中的适应度函数就扮演者这个优胜略汰,适者生存的角色,在运行过程中,会进行多次迭代,每一迭代都会产生多个染色体即可行解,适应度函数会为每一个染色体打分,适应度高的保留,适应度低的淘汰,这样会使整个解种群的染色体更加优良。更加贴近环境。

交叉:

遗传算法每一次迭代都会产生n条染色体,我们称之为进化,每一次进化所需要的染色体从哪来,这里就要引入交叉的概念,可以理解为交配;交叉过程需要选择两条染色体,一个充当母亲,一个充当父亲;将这两条染色体从某一结点切开,再组合在一起,组成一个新的染色体;这样新染色体既包含了父亲的其因也包含了母亲的基因。

但是如何从上一代选出父母基因呢!可以通过轮盘赌算法来实现适应度越高的染色体被选中的概率越高,公式如下:

染色体i被选择的概率 = 染色体i的适应度 / 所有染色体的适应度之和

变异:

交叉能保证每次进化留下优良的基因,但它仅仅是对原有的结果集进行选择,基因还是那么几个,只不过交换了他们的组合顺序。这只能保证经过N次进化后,计算结果更接近于局部最优解,而永远没办法达到全局最优解,为了解决这一个问题,我们需要引入变异。变异很好理解。当我们通过交叉生成了一条新的染色体后,需要在新染色体上随机选择若干个基因,然后随机修改基因的值,从而给现有的染色体引入了新的基因,突破了当前搜索的限制,更有利于算法寻找到全局最优解。

复制

为了保证上一代的优良染色体,会将上一代适应度最高的几条染色体原封不动的复制给下一代

算法思路
  1. 算法初期阶段会随机生成一组可行解,就是祖先代染色体
  2. 采用适应度函数计算每一条染色体的适应度,并根据适应度计算再下一次进化中被选中当作父体母体的概率。
  3. 通过交叉产生N-M条染色体
  4. 对这N-M条染色体进行变异操作。
  5. 通过复制生成M条染色体。

至此N条染色体生成完毕,紧接着再次计算适应度和选中概率重复2-5步骤,直至限定进化次数或者误差允许范围为止。

实例

问题
在这里插入图片描述
适应度:要求函数的最小值,那么就要使得到结果越小的个体,适应度越高。

精度:想要精确到小数点后6位,那么定义域的区间至少需要划分成6*10^6等份,再求出表示这个量级所需要的二进制位数,作为基因的位数。

代码详解

初始化,第一代染色体编码

package com.geneticalgorithm;

/**
 * 初始化群体,进行染色体编码
 * @author 皇甫
 */
public class Initialization {

    /**
     * 初始化一条染色体,随机产生二进制染色体基因
     * @param GENE 基因位数
     * @return 染色体
     */
    public String initSingle(int GENE){
        StringBuilder res = new StringBuilder();
        for(int i = 0;i<GENE;i++){
            if(Math.random()<0.5){
                res.append(0);
            }else{
                res.append(1);
            }
        }
        return res.toString();
    }

    /**
     * 初始化一组染色体,即为祖先染色体,第一代
     * @param GENE 基因个数
     * @param groupSize 染色体个数
     * @return 一组染色体
     */
    public String[] initGroup(int GENE,int groupSize){
        String[] isAll = new String[groupSize];
        for (int i =0;i<groupSize;i++){
            isAll[i] = initSingle(GENE);
        }
        return isAll;
    }
}

解码类,将二进制转换为十进制

package com.geneticalgorithm;

/**
 * 基因解码,将原始基因的二进制转换为十进制
 * @author 皇甫
 */
public class Decoding {
    public double[] decoding(String single,int GENE){
        //将字符串分割成两个基因 x y
        int a = Integer.parseInt(single.substring(0, GENE/2),2 );
        int b = Integer.parseInt(single.substring(GENE/2,GENE),2 );
        double[] x = new double[2];
        x[0] = a*(6.0-0)/(Math.pow(2, GENE/2)-1);
        x[1] = b*(6.0-0)/(Math.pow(2, GENE/2)-1);
        return x;
    }
}

计算个体适应度

package com.geneticalgorithm;

/**
 * 计算适应度函数
 * @author 皇甫
 */
public class Adaptability {
    /**
     * 计算个体的适应度,返回值越大,则3-返回值越小
     * @param single
     * @param GENE
     * @return
     */
    public double calculationFitness(String single,int GENE){
        //解码
        Decoding decoding = new Decoding();
        double[] xyValue = decoding.decoding(single, GENE);
        double yValue = Math.sin(2*xyValue[0])*Math.sin(2*xyValue[0])+Math.sin(2*xyValue[1])*Math.sin(2*xyValue[1]);
        return yValue;
    }

    /**
     * 批量计算适应度
     * @param singles 染色体
     * @param GENE  基因数目
     * @return  群体适应度
     */
    public double[] calculationFitnessAll(String[] singles,int GENE){
        double [] fit = new double[singles.length];
        for(int i = 0;i<singles.length;i++){
            fit[i] = calculationFitness(singles[i],GENE );
        }
        return fit;
    }

    /**
     * 计算出所有染色体中适应度最大的个体,输出下标,即结果最大值下标
     * @param fit 群体适应度
     * @return  极值下标
     */
    public int extremumSubscript(double[] fit){
        double max = fit[0];
        int n = 0;
        for(int i = 1 ;i<fit.length;i++){
            if(fit[i]>max){
                max = fit[0];
                n=i;
            }
        }
        return n;
    }

    /**
     * 返回适应度最大值
     * @param fit
     * @return
     */
    public double extremumValue(double[] fit){
        return fit[extremumSubscript(fit)];
    }
}

轮盘赌算法选择适应度高的群体

package com.geneticalgorithm;

/**
 * 选择父体母体  即适应度越大,被选择的几率越大,使用轮盘赌算法,为了保证群体总数,被淘汰的
 * 个体由随机新个体替代
 * @author 皇甫
 */
public class Select {
    private Initialization initialization = new Initialization();
    private Adaptability adaptability = new Adaptability();

    /**
     * 轮盘赌算法选择适应度高的群体
     * @param group 群体染色体
     * @param GENE  基因数
     * @return
     */
    public String[] newGroup(String[] group,int GENE){
        //初始化染色体概率数组
        double[] probability = new double[group.length];
        //初始化新群体数组
        String[] newGroup = new String[group.length];
        //适应度和详见计算适应度的公式  文档中有
        double f = 0;
        //得到适应度数组并求和
        double[] doubles = adaptability.calculationFitnessAll(group, GENE);
        for (double aDouble : doubles) {
            f+=aDouble;
        }
        //将选中概率填充到概率数组
        for (int i=0;i<doubles.length;i++) {
            probability[i] = doubles[i]/f;
        }
        //求出极值下标
        int maxNum = adaptability.extremumSubscript(doubles);
        //求出适应度最大个体
        String valueMax = group[maxNum];
        //开始转动轮盘
        for(int i =0;i<doubles.length;i++){
            double r = Math.random();
            //累计概率
            int q = 0;
            //适应度最大的个体直接继承
            if(i == maxNum){
                newGroup[i] = valueMax;
                //将本个体选中概率置为0
                probability[i] = 0;
            }

            //开始寻找轮盘指针再那个区域
            for(int j=0;j<doubles.length;j++){
                q+=probability[j];
                if(r<q){
                    //如果被选中,保留进下一代,将选中概率置为0
                    newGroup[i] = group[j];
                    probability[j] = 0;
                    break;
                }
                //没有被选中就重新生成新染色体
                newGroup[i] = initialization.initSingle(GENE);
            }
        }
        return newGroup;
    }
}

父体母体进行交叉

package com.geneticalgorithm;

/**
 * 父体母体进行交叉
 * 染色体依次两两配对,随机在一对染色体上选取一点分成两段,然后互换重组为新的两条染色体。
 * @author 皇甫
 */
public class Cross {
    private Adaptability adaptability = new Adaptability();

    /**
     * 交叉
     * @param group 种群
     * @param GENE 基因
     * @param crossRate
     * @return
     */
    public String[] cross(String[] group,int GENE,double crossRate){
        String temp1,temp2;
        int pos = 0;

        //获取适应度数组
        double[] fit = adaptability.calculationFitnessAll(group, GENE);
        //计算极值序号
        int index = adaptability.extremumSubscript(fit);
        //获取适应度最大的染色体
        String maxStr = group[index];
        for(int i = 0; i < group.length; i++){
            if(Math.random() < crossRate){
                //交叉点
                pos = (int)(Math.random()*GENE) + 1;
                //用来防止数组越界
                temp1 = group[i].substring(0, pos) + group[(i+1) % group.length].substring(pos);
                temp2 = group[(i+1) % group.length].substring(0, pos) + group[i].substring(pos);
                group[i] = temp1;
                group[(i+1) % group.length] = temp2;
            }
        }
        group[0] = maxStr;
        return group;
    }
}

变异,在染色体上随机选取一位,翻转其二进制位

package com.geneticalgorithm;

/**
 * 变异
 * 在染色体上随机选取一位,翻转其二进制位
 * @author 皇甫
 */
public class Variation {
    /**
     *
     * @param str 即将要改动的字符串
     * @param num 改动哪一位
     * @param pos 改成什么
     * @return
     */
    public String replacePos(String str,int num,String pos){
        String temp;
        if(num == 0){
            temp = pos + str.substring(1);
        }else if(num == str.length()-1){
            temp = str.substring(0, str.length() - 1) + pos;
        }else{
            String temp1 = str.substring(0, num);
            String temp2 = str.substring(num + 1);
            temp = temp1 + pos + temp2;
        }
        return temp;
    }

    public String[] mutation(String[] group,int GENE,double MP){
        Adaptability fitness = new Adaptability();
        double[] fit = fitness.calculationFitnessAll(group,GENE);
        //计算适应度最大的染色体序号
        int mFitNum = fitness.extremumSubscript(fit);
        String max = group[mFitNum];

        for(int i = 0; i < group.length * MP; i++){
            //从[0,GENE * group.length)区间取随机数
            int n = (int) (Math.random() * GENE * group.length );
            //取得的染色体数组下标
            int chrNum = (int) (n / GENE);
            //取得的基因下标
            int gNum = (int)(n % GENE);
            String temp = "";

            if(group[chrNum].charAt(gNum) == '0' ){
                temp = replacePos(group[chrNum], gNum, "1");
            }else{
                temp = replacePos(group[chrNum], gNum, "0");
            }
            group[chrNum] = temp;
        }
        group[0] = max;
        return group;
    }
}

测试

package com.geneticalgorithm;

public class GAmain {
    /**染色体数(群体中个体数)*/
    public static final int groupsize = 10;
    /**变异概率*/
    public static final double MP = 0.15;
    /**交叉概率*/
    public static final double CP = 0.6;
    /**迭代次数*/
    public static final int ITERA = 1000;
    /**精确度,选择精确到小数点后几位*/
    public static final int accuracy = 8;


    /**求出精度对应的所需基因数*/
    int temp = (int) ((int)Math.log(6)+ accuracy*Math.log(10) );
    //基因数
    int GENE = temp * 2;


    /**
     * 输出原群体和适应度,测试用
     * */
    public void outAll(String[] group){
        Adaptability fitness = new Adaptability();
        System.out.println("原群体");
        for(String str:group){
            System.out.println(str);
        }

        double fit[] = fitness.calculationFitnessAll(group,GENE);
        System.out.println("原群体适应度");
        for(double str:fit){
            System.out.println(str);
        }
    }

    /**输出适应度最大值,以及返回最优的个体,测试用*/
    public int outMax(String str,String[] group){
        Adaptability fitness = new Adaptability();
        double[] fit = fitness.calculationFitnessAll(group,GENE);
        double max = fitness.extremumValue(fit);
        System.out.println(str+"后适应度最大值为"+max);
        return fitness.extremumSubscript(fit);
    }

    public static void main(String[] args) {
        Initialization init = new Initialization();
        Adaptability fitness = new Adaptability();
        Select rws = new Select();
        Cross cross = new Cross();
        Variation mutation = new Variation();
        Decoding decode = new Decoding();
        GAmain ga = new GAmain();
        //初始化
        String group[] = init.initGroup(ga.GENE,groupsize);
        //输出原群体的适应度
        //ga.outAll(group);

        for(int i = 0; i < ITERA; i++){
            //选择
            group = rws.newGroup(group, ga.GENE);
            /**输出适应度最大值,以及返回最优的个体,测试用*/
            //ga.outMax("选择",group);
            //交叉
            group = cross.cross(group,ga.GENE,CP);
            /**输出适应度最大值,以及返回最优的个体,测试用*/
           // ga.outMax("交叉",group);
            //变异
            group = mutation.mutation(group, ga.GENE, MP);
            /**输出适应度最大值,以及返回最优的个体,测试用*/
            //ga.outMax("变异",group);

           // System.out.println("");
        }
        int max = ga.outMax("完成", group);
        double best[] = decode.decoding(group[max], ga.GENE);	//解码
        double result = 3-fitness.calculationFitness(group[max], ga.GENE);
        System.out.println("x1 = "+best[0]+"\n"+"x2 = "+best[1]);
        System.out.println("最小值为"+result);
    }
}

结果集
在这里插入图片描述

资料下载源码及pdf文件地址
https://download.csdn.net/download/qq_37561309/10953354

参考资料
https://blog.csdn.net/chamagudao11111/article/details/74346999
https://blog.csdn.net/u010425776/article/details/79155480

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值