遗传算法解背包题目(javascript完成)

此为《算法的兴趣》读书笔记,我用javascript(ES6)从新完成算法。

遗传算法

“物竞天择,适者生存”,遗传算法就是自创生物体天然挑选和天然遗传机制的随机搜索算法。算法的症结点有:基因的挑选与编码、顺应度评价函数与三个遗传算子(挑选、交织和变异)的设想。

0-1背包题目

有一个背包,最多承重为C=150的物品,如今有7个物品,编号为1~7,分量分别是w=[35,30,60,50,40,10,25],代价分别是p=[10,40,30,50,35,40,30],如今从这7个物品中挑选一个或多个装入背包,请求在物品总分量不凌驾C的前提下,所装入的物品总代价最高。

代码及思绪

该算法采纳属性序列体式格局对基因编码,遗传算子则运用了比例挑选形式、多点交织和匀称变异三种体式格局,麻雀虽小,五脏具全。

变量定义

var C = 150                            //背包最大承重
var WEIGHT = [35,30,60,50,40,10,25]    //物品分量
var POWER = [10,40,30,50,35,40,30]     //物品代价
var LEN = 7                            //基因长度
var maxPower = 0                       //保留最大值计划
var maxGene = []
var maxi = 0;                          //最大值最初涌现的进化代数
const POPMAX = 32,                     //种群数目
    P_XOVER = 0.8,                     //遗传几率
    P_MUTATION = 0.15,                 //变异几率
    MAXGENERATIONS = 20                //总的进化代数
var pop = []                           //种群一切对象

基因编码

基起因7件物品状况构成,1示意装入,0示意不装入;每一个个别除了基因外,另有顺应度、挑选几率和积聚挑选几率。类定义以下:

class Gene{
    constructor(gene){
        this.gene = gene;            //基因,数组
        this.fitness = 0;
        this.rf = 0;
        this.cf = 0;
    }
}

种群初始化

每一个个别挑选随机的基因,运用0,1随机数直接添补gene数组。由于这个具体题目范围较小,在挑选时我抛弃了顺应度较高的计划,以此来更好的测试算法的结果。

function initGenes(){
    let count = 0, maxFit = 100;    //随机天生的基因顺应度的最大值
    while(count < POPMAX){
        let tmp = [],pall = 0;
        for(let j = 0; j<LEN; j++){
            let pow = Math.round(Math.random())    //随机天生0,1
            tmp.push(pow);
            if(pow == 1)
                pall += POWER[j]
        }
        if(pall < maxFit){
            let g = new Gene(tmp)
            pop.push(g)
            count++
        }
    }
}

顺应度函数

盘算种群中一切对象的顺应度及总和,并对超越C的基因举行“责罚”。

function envaluateFitness(max){            //max参数只是用来纪录进化代数
    let totalFitness = 0;
    for(let i=0; i<POPMAX; i++ ){
        let tw = 0;
        pop[i].fitness = 0;
        for(let j=0; j<LEN; j++){
            if(pop[i].gene[j]){
                tw += WEIGHT[j]
                pop[i].fitness += POWER[j]
            }
        }
        if(tw > C){                    //基因不符合请求,顺应降到1,让其天然镌汰
            pop[i].fitness = 1;
        }else{
            if(pop[i].fitness > maxPower){            //保留阶段最优值
                maxPower = pop[i].fitness;
                maxGene = __.cloneDeep(pop[i].gene);  //运用lodash库
                maxi = max;
            }
        }
        totalFitness += pop[i].fitness
    }
    return totalFitness;
}

挑选算子函数

采纳简朴的轮盘赌体式格局举行挑选,起首盘算种群中一切个别的挑选几率和积累几率,然后应用随机数举行“轮盘赌”,挑出幸运者作为新种群。这里有个坑,lodash的cloneDeep直接克隆pop会有题目,涌现奇异题目,岂非是我的对象条理太深,求解!

function selectBetter(totalFitness){
    let lastCf = 0;
    let newPop = []
    for(let i = 0; i<POPMAX; i++){        //盘算个别挑选几率和积累几率
        pop[i].rf = pop[i].fitness / totalFitness;
        pop[i].cf = lastCf + pop[i].rf;
        lastCf = pop[i].cf;
    }
    for(let i=0; i<POPMAX; i++){        //轮盘赌式挑选
        let p = Math.random();
        if(p < pop[0].cf){
            newPop[i] = pop[0];
        }else{
            for(var j = 0; j<POPMAX-1; j++){
                if(p >= pop[j].cf && p < pop[j+1].cf){
                    newPop[i] = pop[j+1];
                    break;
                }
            }
        }
    }
    pop = []         //种群替代,坑在这,直接 pop=__.cloneDeep(newPop)不对,高手给诠释下,谁研讨过lodash的源码?
    for(let i=0; i< newPop.length; i++){    
        pop.push(__.cloneDeep(newPop[i]))
    }
}

交织算子函数

交织算子采纳多点交织战略,对两个随机选中的个别基因举行交流,基因交流的位置和个数都是随机的,使得新个别的基因更具有随机性。

function crossover(){
    let first = -1;
    for(let i=0; i<POPMAX; i++){
        let p = Math.random();
        if(p < P_XOVER){
            if(first < 0){
                first = i;
            }else{    //挑选了两个随机个别,举行基因交流
                exChgOver(first,i)
                first = -1;
            }
        }
    }
}
function exChgOver(first,second){            //基因交流函数
    let ecc = Math.round(Math.random() * LEN)
    for(let i=0; i<ecc; i++){
        let idx = Math.floor(Math.random() * LEN)
        let tg = pop[first].gene[idx]
        pop[first].gene[idx] = pop[second].gene[idx]
        pop[second].gene[idx] = tg
    }
}

变异算子函数

变异算子采纳匀称变异的战略,选中个别基因变异的个数与位置都是随机挑选的。

function mutation(){
    for(let i=0; i<POPMAX; i++){
        let p = Math.random();
        if(p < P_MUTATION){        //只有当随机数小于变异几率才举行变异操纵
            reverseGene(i)
        }
    }
}
function reverseGene(index){        //变异操纵函数
    let mcc = Math.round(Math.random() * LEN)
    for(let i = 0; i < mcc; i++){
        let gi = Math.floor(Math.random() * LEN) 
        pop[index].gene[gi] = 1 - pop[index].gene[gi]
    }
}

算法主流程

主流程很简朴,几乎是线性的。

initGenes();
var f = envaluateFitness(0)
for(let i=0; i<MAXGENERATIONS; i++){
    selectBetter(f)
    crossover()
    mutation()
    f= envaluateFitness(i)
}
console.log(maxi + '--' + maxPower + ' <=> ' + maxGene.join(','));

总结

之前一向以为遗传算法很神奇,因而细致研讨了一下,以为算法的结果很大程度上取决于种种参数的设定。别的,或许进化过程当中涌现过最优值,但末了的种群中也不一定会有最优值存在。真是能用别的要领能够处理时最好不用它,呵呵!

    原文作者:zhoutk
    原文地址: https://segmentfault.com/a/1190000004989612
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞