遗传算法--背包问题

来源:互联网 发布:淘宝七天下架 编辑:程序博客网 时间:2024/06/06 11:02

关于遗传算法的入门介绍,这里有一篇非常好的博文:http://blog.csdn.net/b2b160/article/details/4680853/

我对遗传算法做了下简单整理:


下面,针对遗传算法在简单背包问题上的应用做一个详细的讲解。


背包问题

背包问题是一个非常经典的NP问题,现实中许多问题都可以抽象为背包问题模型,本文以简单的一维01背包问题为例,进行遗传算法的应用讲解。

背包问题描述

有n个物品,每个物品有2个属性:重量w和价值v。指定一个背包,该背包能承受的最大重量为W,对每个物品来说,只有放入背包和不放入背包2种选择。
求解:如何在不超过该背包最大承重W的前提下,使得放入物品的总价值V最大?

通过问题的描述可知,使用常规的算法很难在有限的时间内求出一个比较优的解,当然,通过贪心算法、动态规划等也是能求出可行解的,这里介绍遗传算法求解。

遗传算法求解

根据遗传算法的求解步骤,和背包问题的详细定义,对遗传算法应用在背包问题上的实现做了一个简单的设计,如下:


先上全部代码(C++实现):

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <windows.h>#define  PACK_MAX_CAPACITY1000// 背包最大承重#define  MAX_GENERATION100// 遗传的最大代数// 获取一个随机整数0-65535static unsigned int Rand() {unsigned int rand;HCRYPTPROV hcryp;CryptAcquireContext(&hcryp, NULL, NULL, PROV_RSA_FULL, 0);CryptGenRandom(hcryp, 2, (PBYTE)&rand);CryptReleaseContext(hcryp, 0);return rand;}// 获取一个0-1之间的随机浮点数static float Rand_Float(){return ( Rand() % 1000 / 1000.0 );}static std::string Int_2_String(int n){char buf[12] = { 0 };sprintf_s(buf, 12, "%d", n);return buf;}// 物品定义struct Item{Item(int w, int v) : weight(w), value(v) {}int weight;// 重量int value;// 价值};// 遗传 - 个体定义struct Entity{Entity(int goods_num) : fitness(0), sum_weight(0), generation_id(0) { InitGene(goods_num); }Entity() : fitness(0), sum_weight(0), generation_id(0) { gene.clear(); }Entity &operator=(const Entity &rv) {fitness = rv.fitness;sum_weight = rv.sum_weight;generation_id = rv.generation_id;gene = rv.gene;return *this;}void InitGene(int goods_num) { gene.assign(goods_num, 0); }intfitness;// 适应度(即总价值)intsum_weight;// 总重量intgeneration_id;// 当前遗传代数(最优个体记录此值)std::vector<int>gene;// 基因};// 遗传算法定义class GA{public:static bool EntitySort(const Entity &one, const Entity &two) { return ( one.fitness <= two.fitness ); }static float TotalFitness(const std::vector<Entity> &group){float sum = 0.0;for (size_t i = 0; i < group.size(); i++){sum += group[i].fitness;}return sum;}public:GA(int gn, float cr, float vr, int goods_n) : m_group_num(gn), m_cross_rate(cr), m_varia_rate(vr), m_goods_num(goods_n) { Init_Goods(); }void Run(){Init_Group();for(int i = 0; i < MAX_GENERATION; i++){CalcFitness();RecordOptimalEntity(i);Select();Cross();if (i % 5 == 0 && i != 0){Variation();}}}// 显示最优个体void PrintOptimal(){std::cout << "最优解在第" + Int_2_String(m_best_entity.generation_id) + "代" << std::endl;std::cout << "总重量:" << m_best_entity.sum_weight << ",总价值:" << m_best_entity.fitness << std::endl;std::cout << "放入物品排列为:";for (size_t i = 0; i < m_best_entity.gene.size(); i++){std::cout << m_best_entity.gene[i] << ",";}std::cout << std::endl;}private:// 初始化物品集合void Init_Goods(){std::cout << "物品集合如下:\n";for(int i = 0; i < m_goods_num; i++){m_goods.push_back( Item(Rand() % 150, Rand() % 150) );std::cout << "物品" << i << ",重量:" << m_goods[i].weight << ",价值:" << m_goods[i].value << "\n";}}// 初始化群体void Init_Group(){m_best_entity.InitGene(m_goods_num);int weight = 0, count = 0;for (int i = 0; i < m_group_num; i++){m_group.push_back( Entity(m_goods_num) );Entity &en = m_group.back();// 产生一个0.5-1.0倍的承重值float vir_capacity = ( (Rand() % 50 + 50) / 100.0 ) * PACK_MAX_CAPACITY;// 初始化该个体基因中的每一位weight = 0;count = 0;while(weight <= vir_capacity){int idx = Rand() % m_goods_num;// 连续3次都随机到基因中的同一个染色体,则该个体初始化结束if (count == 3) { break; }if(en.gene[idx]) { count++; continue; }en.gene[idx] = 1;weight += m_goods[idx].weight;}}}// 适应度计算void CalcFitness(){for(size_t i = 0; i < m_group.size(); i++){Entity &en = m_group[i];int wei = 0, val = 0;for(size_t j = 0; j < en.gene.size(); j++){if(en.gene[j]){wei += m_goods[j].weight;val += m_goods[j].value;}}if(wei > PACK_MAX_CAPACITY) { en.fitness = 0; continue; }en.fitness = val;en.sum_weight = wei;}}// 记录最优个体void RecordOptimalEntity(int gid){std::stable_sort(m_group.begin(), m_group.end(), GA::EntitySort);Entity &en = m_group.back();if(en.fitness > m_best_entity.fitness){m_best_entity = en;m_best_entity.generation_id = gid;}std::cout << "第" + Int_2_String(gid) + "代最优个体,总重量:" + Int_2_String(en.sum_weight)+ ",总价值:" + Int_2_String(en.fitness) + ", 放入物品排列:";for(size_t i = 0; i < en.gene.size(); i++){std::cout << en.gene[i] << ",";}std::cout << std::endl;}// 选择void Select(){std::vector<Entity>  new_group;std::stable_sort(m_group.begin(), m_group.end(), GA::EntitySort);int src_group_num = m_group.size();// 保留群体中最优的前10%int reserve_num = (int)(src_group_num * 0.1);for (int i = 0; i < reserve_num; i++){new_group.push_back( m_group.back() );m_group.pop_back();}// 计算剩余 个体的累积概率std::vector<float>  selected_rate;  float sum_fitness = GA::TotalFitness(m_group);selected_rate.push_back(m_group[0].fitness / sum_fitness);for(size_t i = 1; i < m_group.size(); i++){float cur_rate = selected_rate.back() + (m_group[i].fitness / sum_fitness);selected_rate.push_back(cur_rate);}// 利用轮赌法选择剩下的40%int left_num = (int)(src_group_num * 0.4);for(int i = 0; i < left_num; i++){float rand_rate = Rand_Float();for (size_t idx = 0; idx < selected_rate.size(); idx++){if(rand_rate <= selected_rate[idx]){new_group.push_back(m_group[idx]);break;}}}// 新群体赋值m_group.clear();m_group = new_group;}// 是否交叉bool IsCross() { return ( Rand_Float() <= m_cross_rate ); }// 交叉void Cross(){size_t src_group_num = m_group.size();for (size_t i = 0; i < src_group_num - 1; i += 2){Entity en1 = m_group[i];Entity en2 = m_group[i + 1];for(size_t j = 0; j < en1.gene.size(); j++){if(IsCross()){int tmp = en1.gene[j];en1.gene[j] = en2.gene[j];en2.gene[j] = tmp;}}m_group.push_back(en1);m_group.push_back(en2);}}// 是否变异bool IsVariation() { return ( Rand_Float() <= m_varia_rate ); }// 变异void Variation(){for(size_t i = 0; i < m_group.size(); i++){if(IsVariation()){Entity &en = m_group[i];for (size_t j = 0; j < en.gene.size(); j++){if(IsVariation()){en.gene[j] = (en.gene[j] ? 0 : 1);}}}}}private:intm_group_num;// 群体数量floatm_cross_rate;// 交叉概率floatm_varia_rate;// 变异概率intm_goods_num;// 物品总数Entitym_best_entity;// 最优个体std::vector<Entity>m_group;// 遗传 - 群体std::vector<Item>m_goods;// 总的物品集合};int _tmain(int argc, _TCHAR* argv[]){GA ga(20, 0.8, 0.5, 40);ga.Run();ga.PrintOptimal();return 0;}

代码中已经给出了比较详细的注释,首先定义了背包中的物品类Item,定义了种群中的个体Entity,在遗传算法中,通过容器vector来保存背包物品和种群。
在GA.Run()接口中,可以清楚的看到遗传算法的通用步骤:
初始化种群->开始迭代计算->计算种群中每个个体的适应度->记录最优个体->选择->交叉->变异。

遗传算法因为有交叉和变异2种改变个体的算子,所以拥有较好的局部搜索能力和全局搜索能力,得出的解不会很快的收敛。选择操作也是遗传算法中的一大设计要点,因为遗传算法要根据当代种群中个体的优劣性进行有选择的保留,适应度较高的个体具有较大的参考价值,但适应度小的个体也不能全部丢弃,否则算法会很快收敛,全局搜索能力下降,以一种怎样的方式来选出留下的个体,直接关系到求解的质量,比较通用的就是轮赌法。

最后的运行结果如下:




原创粉丝点击