考虑体积重量的01背包问题—基于遗传算法

该博客介绍了如何使用遗传算法解决考虑体积和重量的01背包问题。通过二进制编码、锦标赛选择、顺序交叉和基本位变异等算子设计,以及适应度函数来处理约束,实现物品的最优装箱策略。遗传算法在不同背包载重和容积限制下运行,最终找到最大化价值的货物组合。文中还展示了具体代码实现,并给出了不同参数设置对结果的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

考虑体积重量的01背包问题—基于遗传算法

1 背包问题简介

经典背包问题可以描述为:给定一组物品,每种物品都有自己的重量/体积和价值,要求把一定数量的物品放入背包,在满足背包载重/容积的约束下,最大化背包内的物品价值。

2 场景设计

已知货物的重量和体积,在满足背包载重和容积约束的情况下,最大化背包内的物品价值。

3 遗传算法设计

3.1 算子设计

采用二进制编码、锦标赛选择、顺序交叉、基本位变异、一对一生存者竞争。
在这里插入图片描述
二进制编码中的01串对应于货物序号,1表示包对应序号的货物放入背包中,0表示不放入,上面的例子中货物1、4、5、6需要放入背包中。

3.2 适应度设计

适应度用打包的物品的价值进行表征。但是01编码有一个比较大的特点:载重容积有溢出的可能,以3.1图中的编码为例子,取出的1、4、5、6货物可能放不进背包中,这就是不满足约束的解,对此引入惩罚:如果超出约束,取价值的相反数,这样表征对于超过约束的项,价值少的(即货物相对较少)反而是比较好的待优化解,后续操作更容易获得满足约束的解。

3.3 遗传算法设计

import random
import pandas as pd

def package_calFitness(cargo_df,pop,v,m):
    """
    #计算适应度:解满足约束取正值,不满足约束取负值
    输入:cargo_df-货物列表,pop-个体,v-背包容积,m-背包载重
    返回:适应值-fit
    """
    v_sum,m_sum,values_sum= 0,0,0
    for j in range(len(pop)):
        if pop[j]==1:
            v_sum += cargo_df.loc[j,'体积']
            m_sum += cargo_df.loc[j,'重量']
            values_sum += cargo_df.loc[j,'价值']
    #计算(不满足约束的为相反数,即惩罚)
    if (v_sum<=v) & (m_sum<=m):
        fit = values_sum
    else:
        fit = -values_sum
    return round(fit,3)


def tournament_select(population,popsize,fit,tournament_size):
    """
    锦标赛选择
    输入:population-种群,popsize-种群大小,fit-适应度,tournament_size-锦标赛小组个数
    返回:选择后种群-new_population
    """
    new_population = []
    while len(new_population)<len(population):
        tournament_list = random.sample(range(0,popsize),tournament_size)
        tournament_fit = [fit[i] for i in tournament_list]
        #转化为df方便索引
        tournament_df = pd.DataFrame([tournament_list,tournament_fit]).transpose().sort_values(by=1).reset_index(drop=True)
        
        new_pop = population[int(tournament_df.iloc[-1,0])]
        new_population.append(new_pop)
        
    return new_population


def package_crossover(popsize,parent1_pop,parent2_pop,pc):
    """
    顺序交叉
    输入:popsize-种群大小,parent1_pop-父代,parent2_pop-母代,pc-交叉概率
    返回:交叉后种群-child_population
    """
    child_population = []
    for i in range(popsize):
        parent1 = parent1_pop[i]
        parent2 = parent2_pop[i]
        child = [-1]*len(parent1)
        if random.random() >= pc:
            child = parent1#随机生成一个
            random.shuffle(child)
        else:
            #parent1
            start_pos = random.randint(0,len(parent1)-1)
            end_pos = random.randint(0,len(parent1)-1)
            if start_pos>end_pos:start_pos,end_pos = end_pos,start_pos
            child[start_pos:end_pos+1] = parent1[start_pos:end_pos+1].copy()
            child[0:start_pos] = parent2[0:start_pos].copy()
            child[end_pos+1:] = parent2[end_pos+1:].copy()
            
        child_population.append(child)
    return child_population


def package_mutate(populations,pm):
    """
    基本位变异
    输入:populations-种群,pm-变异概率
    返回:变异后种群-population_after_mutate
    """
    population_after_mutate = []
    for i in range(len(populations)):
        pop = populations[i].copy()
        for i in range(len(pop)):
            if random.random() < pm:
                if pop[i] == 0:pop[i] = 1
                else:pop[i] = 0
        population_after_mutate.append(pop)
        
    return population_after_mutate


def package_ga(cargo_df,v,m,pc,pm,tournament_size,popsize,generations):
    """
    遗传算法主流程
    """
    Chrom = [[random.randint(0,1) for i in range(len(cargo_df))] for j in range(popsize)]
    fit = [-1]*popsize
    for i in range(popsize):fit[i] = package_calFitness(cargo_df,Chrom[i],v,m)
    
    best_fit = max(fit)
    best_pop= Chrom[fit.index(max(fit))].copy()
    
    iter = 0
    while iter<generations:
        Chrom1 = tournament_select(Chrom,popsize,fit,tournament_size)
        Chrom2 = tournament_select(Chrom,popsize,fit,tournament_size)
        new_Chrom = package_crossover(popsize,Chrom1,Chrom2,pc)
        new_Chrom = package_mutate(new_Chrom,pm)
        iter +=1
        
        new_fit = [-1]*popsize
        for i in range(popsize):new_fit[i] = package_calFitness(cargo_df,new_Chrom[i],v,m)
        
        for i in range(popsize):
            if fit[i] < new_fit[i]:
                Chrom[i] = new_Chrom[i]
                fit[i] = new_fit[i]
        
        if best_fit < max(fit):
            best_fit = max(fit)
            best_pop= Chrom[fit.index(max(fit))].copy()
        print(iter,best_fit)
    
    
    v_sum,m_sum = 0,0
    package_list,unpackage_list = [],[]
    for j in range(len(best_pop)):
        if best_pop[j]==0:
            unpackage_list.append(cargo_df.loc[j,'货物序号'])
        else:
            package_list.append(cargo_df.loc[j,'货物序号'])
            v_sum += cargo_df.loc[j,'体积']
            m_sum += cargo_df.loc[j,'重量']
    return package_list,unpackage_list,v_sum,m_sum,best_fit


if __name__ == "__main__":
    #货物数据
    num = list(range(100))#货物编号
    volumns = [1,6,7,8,1,2,3,1,8,8,10,1,9,3,4,3,5,7,4,6,5,5,9,5,6,3,9,9,6,3,4,2,1,3,5,9,6,6,8,5,6,2,7,9,5,1,7,5,10,6,
               4,6,9,7,2,4,3,7,5,4,5,10,2,1,4,10,9,6,10,10,10,2,10,2,4,6,4,1,7,6,1,10,1,3,4,1,7,3,6,5,3,10,6,8,1,6,4,4,10,3]#体积
    weight = [3,5,3,8,10,4,7,2,10,1,9,2,1,9,7,1,7,1,4,2,5,9,1,6,1,4,2,1,2,1,5,5,6,8,3,6,7,4,9,7,7,4,8,3,9,4,1,1,9,5,8,
              4,10,3,5,1,7,8,8,2,8,7,1,10,3,3,8,2,4,6,8,3,5,8,10,5,7,5,7,1,9,1,5,9,9,2,10,2,9,3,7,10,5,1,2,1,9,8,6,9]#重量
    value = [5,5,7,7,9,2,2,6,8,3,9,4,5,3,7,10,5,6,1,2,9,8,8,9,5,2,1,8,10,6,3,2,10,10,9,8,7,6,2,8,5,1,7,5,6,2,2,7,6,10,
             5,4,8,9,1,7,8,8,4,7,5,5,6,5,1,6,8,5,2,6,9,9,6,9,7,5,4,6,3,10,7,4,8,8,4,10,9,2,10,5,9,10,6,10,4,3,7,5,7,9]#价值
    cargo_df = pd.DataFrame({'货物序号':num, "体积":volumns, "重量":weight,"价值":value})
    
    #参数
    M,V= 200,200 #箱子载重容积
    
    generations = 100
    popsize = 40
    tournament_size = 4
    pc = 0.9
    pm = 0.1
    
    package_list,unpackage_list,v_sum,m_sum,fit = package_ga(cargo_df,V,M,pc,pm,tournament_size,popsize,generations)
    print("需打包的商品序号:",package_list)
    print("背包容积载重利用情况价值分别是:",v_sum,m_sum,fit)
    

3.4 结果

需打包的商品序号: [0, 1, 3, 4, 7, 10, 13, 15, 17, 19, 20, 22, 24, 27, 28, 29, 33, 35, 39, 40, 45, 46, 49, 50, 55, 56, 59, 61, 62, 71, 73, 75, 77, 79, 80, 82, 84, 85, 89, 91, 98]
背包容积载重利用情况价值分别是: 197,191,281。

**【讨论】**这里设置M,V= 200,200,当M和V设置比较小时,需要适当提高迭代次数,因为货物较多时,随机生成的序列中,01串中的0和1数量是接近的,而MV值较小时,对于解的要求就是1少0多,那么需要一定的代数去迭代得到较优解。

背包问题系列
考虑体积重量的装箱问题(贪婪策略装箱)— 基于遗传算法
考虑体积重量的装箱问题(箱子装载平衡)— 基于遗传算法

记录学习过程,欢迎指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值