资讯专栏INFORMATION COLUMN

遗传算法解背包问题(javascript实现)

longshengwang / 2906人阅读

摘要:遗传算法物竞天择,适者生存,遗传算法就是借鉴生物体自然选择和自然遗传机制的随机搜索算法。随机生成的基因适应度的最大值随机生成,适应度函数计算种群中所有对象的适应度及总和,并对超出的基因进行惩罚。

此为《算法的乐趣》读书笔记,我用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
适应度函数

计算种群中所有对象的适应度及总和,并对超出C的基因进行“惩罚”。

function envaluateFitness(max){            //max参数只是用来记录进化代数
    let totalFitness = 0;
    for(let i=0; i 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= 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
变异算子函数

变异算子采用均匀变异的策略,选中个体基因变异的个数与位置都是随机选择的。

function mutation(){
    for(let i=0; i
算法主流程

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

initGenes();
var f = envaluateFitness(0)
for(let i=0; i " + maxGene.join(","));
总结

以前一直觉得遗传算法很神秘,因此仔细研究了一下,觉得算法的效果很大程度上取决于各种参数的设定。另外,也许进化过程中出现过最优值,但最后的种群中也不一定会有最优值存在。真是能用其它方法可以解决时最好不用它,呵呵!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79264.html

相关文章

  • 遗传算法GA(Genetic Algorithm)入门知识梳理

    摘要:编码需要将问题的解编码成字符串的形式才能使用遗传算法。遗传算子遗传算法有个最基本的操作选择,交叉,变异。实验发现,比较大的种群的规模并不能优化遗传算法的结果。灾变遗传算法的局部搜索能力较强,但是很容易陷入局部极值。 一、遗传算法进化论背景知识 作为遗传算法生物背景的介绍,下面内容了解即可: 种群(Population):生物的进化以群体的形式进行,这样的一个群体称为种群。 个体:组成...

    gxyz 评论0 收藏0
  • Python遗传算法框架DEAP-Creating Types

    摘要:是一个遗传算法框架,这里是它的简介。最小化问题使用负值的的最大化问题用正值。略种群种群横线个体。这个种群是直接使用和函数来初始化的。个体之间分布在网格中每个格子包含一个个体。调用将会返回一个种群,个体是使用两个索引可获得的。 DEAP是一个python遗传算法框架,这里是它的简介。DEAP documentation今天整理一下DEAP的概览,大体了解一下它的流程。初学,不严谨,仅作为...

    Channe 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<