zoukankan      html  css  js  c++  java
  • “遗传算法”解决“背包问题”

    遗传算法基本思想:

    1)       一个种群有多个个体,每个个体有染色体和对应的基因

    为了繁殖进行:

    2)       选择:在残酷的世界中,适者生存,优胜略汰。

    3)       重组:染色体交叉,基因重组

    4)       突变:染色体上的基因小概率的突变 (一般给小数点后两位)

    背包问题:

    背包只能容得下一定重量b的物品,物品有m种,每种物品有自己的重量w(i)和价值v(i)(0<i<=m),从这些物品中选择装入背包,是背包不超过重量b,但价值又要最大。

    运用动态规划,分支限界都可以达到效果,但不佳。

    我用遗传算法解决:

    一般人有多条染色体,但对于背包问题,一个解我们将看成一个个体,所以,一个个体只有一个染色体,一个染色体对应多个基因。如:100101010100111 表示装入背包的可能解。(具体情况具体分析)

    遗传所做准备:

    1)       用0表示“不选择装入”,1表示“装入”,形成一条基因链;100101010100111则表示“15种物品”装入或不装入背包的可能解。                         ------- 此处用chrom[]存放基因,代表染色体

    2)       一个基因对应一个个体。                                                              ------- 此处用Population类或结构体声明其含有chrom[]等信息

    3)       可能的解有很多,构成一个种群。                                           ------- 用Population类定义一个数组代表个体构成的种群newPop[]:存放新生代,oldPop[]:存放上一代

    4)       适应度:适应度和目标函数是正相关的,所以需要物品价值和重量。

    ------- fitness,weight包含在Population类中

    最大适应度:maxFitness,

    最小适应度:minFitness,

    总适应度:sumFitness,(帮助求突变和交叉的染色体)

    平均适应度:avgFitness

    遗传算法的函数:

    基本:

    1)       InitPop()                      初始化个体,使每个个体都有基因组

    2)       Statistics(*pop)                   计算适应度(最大,最小,总的,平均的)

    3)       Selection(*pop)          通过选择种群中符合要求的父母去繁殖新代,返回这对父母的位置

    4)       crossover(*parent1,*parent2,pos)  传入要改的个体位置,随机产生交叉位置,用优良父母繁殖优良后代并替代传入个体位置

    5)       mutation(i)                  i为基因组基因的位置,逐个基因看是否要变异

    6)       generation()               对个体进行判断,若不符合要求,进行选择,重组,突变。

    背包的适应性函数:(个体是一个解)

    1)       calWeight(pop)          计算个体的重量

    2)       calFit( pop )                 计算个体的价值

      1 #include <AfxWin.h>
      2 #include <stdlib.h>
      3 #include <math.h>
      4 #include <time.h>
      5 #include <conio.h>
      6 #include <stdio.h>
      7 /**
      8  * @author:     ckj
      9  * @date:         2012/12/27 
     10  */
     11 /*============= 一些必要的常量 ===============*/
     12 #define POP_SIZE 200            // 种群的规模
     13 #define PRO_CROSS 0.618            // 交叉概率
     14 #define PRO_MUTATE 0.03            // 变异概率
     15 #define CHROM_SIZE 50            // 染色体长度
     16 #define GENERATION_NUM 1000        // 繁殖代数
     17 /*============================================*/
     18 
     19 // 个体类
     20 struct population{
     21     unsigned int chrom[CHROM_SIZE];        // 基因组
     22     double weight;                        // 背包重量
     23     double fitness;                        // 适应度
     24     unsigned int parent1,parent2,cross;    // 双亲,交叉节点
     25 };
     26 
     27 // 新生代和上一代的种群
     28 struct population oldPop[POP_SIZE], newPop[POP_SIZE];
     29 
     30 // 物体的重量,收益,背包的容量
     31 int weight[CHROM_SIZE], profit[CHROM_SIZE], contain;
     32 
     33 // 种群的总的适应度,最小,最大,平均适应度。
     34 double sumFitness, minFitness, maxFitness, avgFitness;
     35 
     36 // 计算适应度时,用的惩罚函数系数
     37 double alpha;
     38 
     39 // 一个种群的最大和最小适应度个体
     40 int minPop, maxPop;
     41 
     42 /**=============================================
     43  * 参数:chr为装入背包的一个可能解
     44  * 用途:计算装入背包的一个可能解得(个体)重量
     45  * 返回值:个体重量
     46  ============================================**/
     47 double calWeight( unsigned int *chr ){
     48     double popSumWeight = 0;
     49     for(int i = 0; i < CHROM_SIZE; i++){
     50         popSumWeight += (*chr) * weight[i];
     51         chr++;
     52     }
     53     return popSumWeight;
     54 }
     55 
     56 /**===============================
     57  * 参数:chr为装入背包的一个可能解
     58  * 用途:计算装入背包的总价值
     59  * 返回值:返回个体的价值
     60  ================================*/
     61 double calFit( unsigned int *chr ){
     62     double popProfit = 0;
     63     for(int i = 0; i < CHROM_SIZE; i++){
     64         popProfit += (*chr) * profit[i];
     65         chr++;
     66     }
     67     return popProfit;
     68 }
     69 
     70 /**=======================================
     71  * 参数:传入一个种群
     72  * 用途:计算种群的最大适应度和最小适应度
     73  =======================================*/
     74 void statistics( struct population *pop ){
     75     double tmpFitness;
     76 
     77     sumFitness = pop[0].fitness;
     78     minFitness = pop[0].fitness;
     79     minPop = 0;
     80     maxFitness = pop[0].fitness;
     81     maxPop = 0;
     82 
     83     for( int i =1; i < POP_SIZE; i++ ){
     84     // 计算种群的总适应度
     85         sumFitness += pop[i].fitness;
     86         tmpFitness = pop[i].fitness;
     87         // 选择最大适应度的个体的位置和适应度的值
     88         if( (tmpFitness > maxFitness) && ((int)(tmpFitness*10)%10 == 0) ){
     89             maxFitness = pop[i].fitness;
     90             maxPop = i;
     91         }
     92         // 选择最小适应度的个体的位置
     93         if( (tmpFitness < minFitness) ){
     94             minFitness = pop[i].fitness;
     95             minPop = i;
     96         }    
     97     avgFitness = sumFitness / (float)POP_SIZE;
     98     }    
     99 }
    100 
    101 /**====================
    102  * 用途:报告种群信息 *
    103  ====================**/
    104 void report( struct population *pop, int gen ){
    105     int popWeight = 0;
    106     printf("\nThe generation is %d.\n", gen);        // 输出种群的代数
    107     // 输出种群中最大适应度个体的染色体信息
    108     printf("The population chrom is: \n");
    109     for( int j = 0; j < CHROM_SIZE; j++ ){
    110         if( j % 5 == 0 ) {printf(" ");}
    111         // 每个基因单位输出
    112         printf("%1d", pop[maxPop].chrom[j]);
    113     }
    114     printf("\nThe population's max fitness is %d.", (int)pop[maxPop].fitness);
    115     printf("\nThe population's max weight is %d.\n", (int)pop[maxPop].weight);
    116 }
    117 
    118 /**====================
    119  * 用途:初始化种群   *
    120  ====================**/
    121 void initPop(){
    122     int i, j;
    123     double tmpWeight;
    124     bool isPop = false;
    125     // 生成一个祖宗种群
    126     for( i = 0; i < POP_SIZE; i++ ){
    127         while(!isPop){
    128             // 如果不满足条件,则继续随机产生直到有相同的个体
    129             for( j = 0; j < CHROM_SIZE; j++ ){
    130                 oldPop[i].chrom[j] = rand()%2;    // 产生0/1的任意一个数
    131                 oldPop[i].parent1 = 0;
    132                 oldPop[i].parent2 = 0;
    133                 oldPop[i].cross = 0;
    134             }
    135             // 选择重量小于背包容量的个体,满足条件的个体
    136             
    137             tmpWeight = calWeight( oldPop[i].chrom );
    138             if( tmpWeight <= contain ){
    139                 oldPop[i].fitness = calFit( oldPop[i].chrom );
    140                 oldPop[i].weight = tmpWeight;
    141                 oldPop[i].parent1 = 0;
    142                 oldPop[i].parent2 = 0;
    143                 oldPop[i].cross = 0;
    144                 isPop = true;
    145             }
    146         }
    147         isPop = false;
    148     }
    149 }
    150 /**=========================================
    151  * 参数:用于带入概率参数:"交叉"/"变异"       *
    152  * 用途:概率选择试验                       *
    153  =========================================**/
    154 int excise( double probability ){
    155     double pp;
    156     pp = (double)(rand()%20001/20000.0);
    157     if( pp <= probability ) 
    158         return 1;
    159     return 0;
    160 }
    161 
    162 /**====================
    163  * 用途:个体的选择   *
    164  ====================**/
    165 int    selection(int pop){
    166     double wheelPos, randNumber,partsum = 0;
    167     int i = 0;
    168     // 轮赌法
    169     randNumber=(rand()%2001)/2000.0;
    170     wheelPos = randNumber*sumFitness;
    171     do {
    172         partsum += oldPop[i].fitness;
    173         i++;
    174     }while( ( partsum < wheelPos) && (i < POP_SIZE) );
    175     return i-1;
    176 }
    177 
    178 /**========================================
    179  * 参数:父母染色体,即不被测试选到的染色体
    180  * 用途:染色体交叉
    181  * 返回值:1
    182  *========================================*/
    183 int crossOver(unsigned int *parent1, unsigned int *parent2, int i){
    184     int j;                    // 基因组的基因位置
    185     int crossPos;            // 交叉点位置
    186     if( excise(PRO_CROSS) ){
    187         crossPos = rand()%(CHROM_SIZE-1);    
    188     }else{
    189         crossPos = CHROM_SIZE - 1;
    190     }
    191     // 开始交叉
    192     for( j = 0; j <= crossPos; j++ ){
    193         newPop[i].chrom[j] = parent1[j];
    194     }
    195     for( j = crossPos+1; j < CHROM_SIZE; j++ ){
    196         newPop[i].chrom[j] = parent2[j];
    197     }
    198     newPop[i].cross = crossPos;
    199     return 1;
    200 }
    201 
    202 /**==========================================
    203  * 参数:染色体基因组的某一位置突变
    204  * 用途:染色体基因突变
    205  * 返回值:若突变,则突变结果,否则,原样返回
    206  *=========================================*/
    207 int mutation(unsigned int alleles){
    208     if( excise(PRO_MUTATE) ){
    209         // 满足突变概率,则此基因突变
    210         alleles == 0? alleles = 1: alleles = 0;
    211     }
    212     return alleles;
    213 }
    214 
    215 /**====================================================
    216  * mate1,mate2是从种群中挑出的能来诞生新一代的个体位置
    217  * 用途:种群群体更新
    218  *=====================================================*/
    219 void generation(){
    220     unsigned int i, j, mate1, mate2; 
    221     double tmpWeight = 0;
    222     bool notGen;
    223     for( i = 0; i < POP_SIZE; i++){
    224         notGen = false;
    225         // 需要繁殖
    226         while( !notGen ){
    227             // 选择:选择有几率产生优良后代的父母个体位置
    228             mate1 = selection(i);
    229             mate2 = selection(i+1);
    230             // 交叉:产生新一代替换掉不符合生存条件的i
    231             crossOver( oldPop[mate1].chrom, oldPop[mate2].chrom, i);
    232             for( j = 0; j < CHROM_SIZE; j++ ){
    233                 // 变异:新个体一个一个基因位置代入,看是否要突变
    234                 newPop[i].chrom[j] = mutation(newPop[i].chrom[j]);
    235             }
    236             // 选择重量小于背包重量的个体,满足条件,则把价值给记录起来
    237             tmpWeight = calWeight( newPop[i].chrom );
    238             if( tmpWeight <= contain ){
    239                 newPop[i].fitness = calFit( newPop[i].chrom );
    240                 newPop[i].weight = tmpWeight;
    241                 newPop[i].parent1 = mate1;
    242                 newPop[i].parent2 = mate2;
    243                 notGen = true;                // 不需要再产生子代
    244             }
    245         }
    246     }
    247 }
    248 
    249 void main(int argc, char* argv[]){
    250     int gen, oldMaxPop, k;
    251     double oldMax;
    252     // 输入每个物品的重量和价值,背包容量
    253     printf("Please input the products\' weights:\n");
    254     for(int i = 0; i < CHROM_SIZE; i++ )
    255         scanf("%d", &weight[i]);
    256     printf("\nPlease input the products\' porfits:\n");
    257     for(int j = 0; j < CHROM_SIZE; j++ )
    258         scanf("%d", &profit[j]);    
    259     contain = 1000;
    260     gen = 0;                        // 代表第一代(主要用在输出显示上)
    261 
    262     srand( (unsigned)time(NULL) );    // 置随机种子
    263     initPop();                        // 初始化种群
    264     memcpy(&newPop,&oldPop,POP_SIZE*sizeof(struct population));
    265     statistics( newPop );            // 产生新种群的信息(适应度等)
    266     report( newPop, gen );
    267     while( gen < GENERATION_NUM ){
    268         gen += 1;
    269         if( gen % 100 == 0 )
    270             srand( (unsigned)time(NULL) );    // 每一百代产生新的随机数
    271         oldMax = maxFitness;        // 将最大适应度给上一代
    272         oldMaxPop = maxPop;            // 拥有最大适应度的个体位置给上一代
    273         generation();
    274         statistics( newPop );
    275 
    276         // 如果新的最大适应度小于上一代最大适应度,则上一代活着
    277         if( maxFitness < oldMax ){
    278             for( k = 0; k < CHROM_SIZE; k++ ){
    279                 newPop[minPop].chrom[k] = oldPop[oldMaxPop].chrom[k];
    280             }
    281             newPop[minPop].fitness = oldPop[oldMaxPop].fitness;
    282             newPop[minPop].parent1 = oldPop[oldMaxPop].parent1;
    283             newPop[minPop].parent2 = oldPop[oldMaxPop].parent2;
    284             newPop[minPop].cross = newPop[minPop].cross;
    285             statistics(newPop);
    286         }else if( maxFitness > oldMax ){
    287             report(newPop, gen);
    288         }
    289          //保存新生代种群的信息到老一代种群信息空间
    290         memcpy(&oldPop,&newPop,POP_SIZE*sizeof(struct population));
    291     }
    292 }
  • 相关阅读:
    spring core源码解读之ASM4用户手册翻译之一asm简介
    nginx启动,重启,关闭命令
    linux LVM分区查看dm设备
    jdbc 对sqlite的基本操作
    linux配置多个ip
    细说Linux下的虚拟主机那些事儿
    打造字符界面的多媒体Linux系统
    linux计划crontab
    因修改/etc/ssh权限导致的ssh不能连接异常解决方法
    Linux修改主机名
  • 原文地址:https://www.cnblogs.com/Caikejia/p/2836425.html
Copyright © 2011-2022 走看看