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

本文介绍了如何运用遗传算法解决0-1背包问题,通过JavaScript(ES6)实现。详细阐述了遗传算法的基本原理,包括基因编码、适应度函数、选择、交叉和变异算子。在0-1背包问题中,选取物品装入背包以最大化总价值,同时不超过背包的承重限制。文章展示了完整的代码实现,包括种群初始化、适应度计算和各种算子的细节。最后指出,遗传算法的效果受参数设置影响,且可能存在最优解未出现在最终种群中的情况。
摘要由CSDN通过智能技术生成

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

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

let tw = 0;

pop[i].fitness = 0;

for(let j=0; 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

pop[i].rf = pop[i].fitness / totalFitness;

pop[i].cf = lastCf + pop[i].rf;

lastCf = pop[i].cf;

}

for(let i=0; i

let p = Math.random();

if(p < pop[0].cf){

newPop[i] = pop[0];

}else{

for(var j = 0; 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

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

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

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

selectBetter(f)

crossover()

mutation()

f= envaluateFitness(i)

}

console.log(maxi + '--' + maxPower + ' <=> ' + maxGene.join(','));

总结

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值