python - 用遗传算法解决0-1背包问题,遗传算法是基于概率论的,因此不一定能一次命中最优解

# coding=utf-8
'''
贪心算法:局部最优解。
动态规划算法:多目标、多阶段优化。
穷举算法:万能,受问题规模限制。
遗传算法:只是比漫无目的的穷举搜索算法聪明一点点,通过较小的计算量获得较大的收益。
只要能用解析的方法直接得到的最优解问题,都不要试图用遗传算法。
适合-非线性问题。人工智能、自适应控制、机器学习等领域。
不依赖目标函数。
基于概率论,而不是一个确定的搜索过程,即每一次启发式搜索,可能会得出不同的最优解。
可能得到局部最优解或近似最优解。
迭代流程:
初始化种群
种群N个体评价
选择
交叉
变异
种群N+1
1-编码方式:二进制编码(汉明距离)、格雷编码、符号编码、属性序列编码。
2-适应度评估函数(关键点):
    固定的问题:运算初期早熟问题(未成熟收敛局部最优解),运算后期竞争区分度不高问题(等概率平均搜索)
    自适应的改进:尺度变换:线性、乘方、指数。
种群大小M32
交叉概率PC0.8
变异概率PM0.15
进化代数T500

用遗传算法解决0-1背包问题。
1-问题的解收敛了,但没有收敛到最优解 - 通过将最优解与实际结果比较,也可以循环100次,取价值最大的解。
2-self.total_fitness = 1 # 每次计算时,先将总数置0,在循环中,会产生全0的现象
     - envaluateFitness中加入防护,如果某个种群全零,则对其进行调整。
3-本例中,循环执行遗传算法24次,得到全局最优解,且结果收敛。
'''
import os
import random
from copy import deepcopy

class GAType(): # 种群32    def __init__(self, obj_count):
        self.gene = [0 for x in range(0, obj_count, 1)]   # 序列编码 0 / 1
        self.fitness = 0 # 适应度
        self.cho_feq = 0 # choose 选择概率
        self.cum_feq = 0 # cumulative 累积概率

class genetic():
    def __init__(self, value, weight, max_weight):
        self.value = value
        self.weight = weight
        self.max_weight = max_weight
        self.obj_count = len(weight)
        self._gatype = [GAType(self.obj_count) for x in range(0, population_size, 1)]  # 初始化32个种群
        self.total_fitness = 0

    def avoid_zero(self):
        '''防止遗传的下一代为全零,若为全零,则将随机的位数(1-7)置1'''
        flag = 0
        for i in range(0, population_size, 1):
            res = []
            for j in range(0, self.obj_count, 1):
                res.append(self._gatype[i].gene[j])
            if [0 for x in range(0, self.obj_count, 1)] == res: # 全零
                # print('找到了全零的状态!')
                flag = 1
                set_one = random.randint(1,self.obj_count)
                for k in range(0,set_one,1):
                    idx = random.randint(0,self.obj_count-1)
                    self._gatype[i].gene[idx] = 1
                # print(self._gatype[i].gene)
        return True if flag else False

    def initialize(self):
        '''初始化种群'''
        for i in range(0, population_size, 1):
            while (1): # 保证不全为零
                res = []
                for j in range(0, self.obj_count, 1):
                    self._gatype[i].gene[j] = random.randint(0,1)
                    res.append(self._gatype[i].gene[j])
                if [0 for x in range(0, self.obj_count, 1)] != res:
                    break

    def envaluateFitness(self):
        '''适应度评估 = 背包内装入物品的总价值,如果超出max_weight,则置1(惩罚性措施)'''
        self.total_fitness = 0 # 每次计算时,先将总数置0
        for i in range(0, population_size, 1):
            max_w = 0
            self._gatype[i].fitness = 0  # 0后再计算
            for j in range(0, self.obj_count, 1):
                if self._gatype[i].gene[j] == 1:
                    self._gatype[i].fitness += self.value[j] # 适应度
                    max_w += self.weight[j] # 最大重量限制
            if max_w > self.max_weight:
                self._gatype[i].fitness = 1 # 惩罚性措施
            if 0 == self._gatype[i].fitness: # 出现了全零的种群
                self.avoid_zero()
                i = i - 1  # 重新计算该种群的fitness
            else:
                self.total_fitness += self._gatype[i].fitness # 种群的所有适应度
        if 0 == self.total_fitness:
            print('total_fitness = 0 ')

    def select(self):
        '''采用选择概率和累积概率来做选择,得出下一代种群(个数不变)
        对环境适应度高的个体,后代多,反之后代少,最后只剩下强者'''
        last_cho_feq = 0
        for i in range(0, population_size, 1):
            try:
                self._gatype[i].cho_feq = self._gatype[i].fitness / float(self.total_fitness) # 选择概率
            except:
                # print('error', self.total_fitness)
                pass
            self._gatype[i].cum_feq = last_cho_feq + self._gatype[i].cho_feq # 累积概率
            last_cho_feq = self._gatype[i].cum_feq

        # _next = deepcopy(self._gatype)  # 下一代种群,参与到后续的交叉和变异
        _next = [GAType(self.obj_count) for x in range(0, population_size, 1)]
        for i in range(0, population_size, 1):
            choose_standard = random.randint(1, 100) / 100.0
            # print('choose_standard: %f' % choose_standard)
            if choose_standard < self._gatype[0].cum_feq: # 选出下一代种群
                _next[i] = self._gatype[0]
            else:
                for j in range(1, population_size, 1):
                    if self._gatype[j-1].cum_feq <= choose_standard < self._gatype[j].cum_feq:
                        _next[i] = self._gatype[j]
        self._gatype = deepcopy(_next)
        self.avoid_zero() # 全零是不可避免的??

    def crossover(self):
        '''采用交叉概率p_cross进行控制,从所有种群中,选择出两个种群,进行交叉互换'''
        first = -1
        for i in range(0, population_size, 1):
            choose_standard = random.randint(1, 100) / 100.0
            if choose_standard <= p_cross: # 选出两个需要交叉的种群
                if first < 0:
                    first = i
                else:
                    self.exchangeOver(first, i)
                    first = -1

    def exchangeOver(self,first,second):
        '''交叉互换'''
        exchange_num = random.randint(1, self.obj_count) # 需要交换的位置数量
        # print(exchange_num)
        for i in range(0, exchange_num, 1):
            idx = random.randint(0, self.obj_count - 1)
            self._gatype[first].gene[idx], self._gatype[second].gene[idx] = \
            self._gatype[second].gene[idx], self._gatype[first].gene[idx]

    def mutation(self):
        '''随机数小于变异概率时,触发变异'''
        for i in range(0, population_size, 1):
            choose_standard = random.randint(1, 100) / 100.0
            if choose_standard <= p_mutation: # 选出需要变异的种群
                self.reverseGene(i)

    def reverseGene(self, index):
        '''变异,将01,将10'''
        reverse_num = random.randint(1, self.obj_count)  # 需要变异的位置数量
        for i in range(0, reverse_num, 1):
            idx = random.randint(0, self.obj_count - 1)
            self._gatype[index].gene[idx] = 1 - self._gatype[index].gene[idx]

    def genetic_result(self):
        cnt = 0
        while (1):
            cnt = cnt + 1
            if cnt > 100:
                break
            self.initialize()
            self.envaluateFitness()
            for i in range(0, max_generations, 1):
                self.select()
                self.crossover()
                self.mutation()
                self.envaluateFitness()
            if True == self.is_optimal_solution(self._gatype, opt_result):
                print('循环的次数为:%d' % cnt)
                break
        self.show_res(self._gatype)

    def is_optimal_solution(self, gatype0, opt_result):
        '''判断是否达到了最优解'''
        for i in range(0, population_size, 1):
            res_list = []
            for j in range(0, self.obj_count, 1):
                res_list.append(gatype0[i].gene[j])
            if opt_result == res_list:
                return True
        return False

    def show_res(self, gatype0):
        '''显示所有种群的取值'''
        res = []
        res_list = []
        for i in range(0, population_size, 1):
            print('种群:%d --- ' % i),
            list0 = []
            for j in range(0, self.obj_count, 1):
                list0.append(gatype0[i].gene[j])
                print(gatype0[i].gene[j]),
            print(' --- 总价值:%d' % gatype0[i].fitness)
            res.append(gatype0[i].fitness)
            res_list.append(list0)
        #
        max_index = 0
        for i in range(0, len(res), 1):
            if res[max_index] < res[i]:
                max_index = i
        weight_all = 0
        for j in range(0, self.obj_count, 1):
            if gatype0[max_index].gene[j] == 1:
                weight_all += self.weight[j]
        print('当前算法的最优解(种群%d):' % max_index),
        print(res_list[max_index]),
        print('总重量(不超过%d):' % self.max_weight),
        print(weight_all),
        print('总价值:'),
        print(res[max_index])

if __name__ == '__main__':
    weight = [35,30,60,50,40,10,25]
    value  = [10,40,30,50,35,40,30]
    max_weight = 150
    opt_result = [1,1,0,1,0,1,1]  # 全局最优解:[1,2,4,6,7] - [35,30,50,10,25] = 150 [10,40,50,40,30] = 170

    population_size = 32  # 种群
    max_generations = 500  # 进化代数
    p_cross = 0.8  # 交叉概率
    p_mutation = 0.15  # 变异概率

    genetic(value, weight, max_weight).genetic_result()
'''
循环执行该遗传算法,收敛得到最优解(前提是将最优解与实际得到的结果进行比较)
循环的次数为:24
种群:0 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:1 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:2 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:3 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:4 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:5 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:6 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:7 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:8 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:9 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:10 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:11 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:12 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:13 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:14 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:15 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:16 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:17 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:18 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:19 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:20 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:21 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:22 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:23 ---  1 1 1 1 1 0 0  --- 总价值:1
种群:24 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:25 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:26 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:27 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:28 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:29 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:30 ---  1 1 0 1 0 1 1  --- 总价值:170
种群:31 ---  1 1 0 1 0 1 1  --- 总价值:170
当前算法的最优解(种群0): [1, 1, 0, 1, 0, 1, 1] 总重量(不超过150): 150 总价值: 170
'''
    原文作者:遗传算法
    原文地址: https://blog.csdn.net/bigdeng_2014/article/details/78043232
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞