C++实现多目标遗传算法(0/1揹包问题)

(揹包问题):揹包只能容得下一定重量b的物品,物品有m种,每种物品有自己的重量w(i)和价值v(i),从这些物品中选择装入揹包,是揹包不超过重量b,但价值又要最大。
上面为单目标的0/1规划问题,也就是说只考虑物体的重量不考虑物体的体积,形状等问题,一般而言,利用动态规划可以很好地解决揹包问题,但是如果物体过多,使用动态规划将浪费很大的资源.
遗传算法作经典的人工智能算法,可以很好的解决当物体较多的0/1规划问题。
(遗传算法概述):
遗传算法使用的就是生物学中适者生存的法则。但是和生物中一些专业人术语有些区别:
种群(Population):生物的进化以群体的形式进行,这样的一个群体称为种群。
  个体:组成种群的单个生物。

  基因 ( Gene ) :一个遗传因子。

  染色体 ( Chromosome ) :包含一组的基因。

  生存竞争,适者生存:对环境适应度高的、牛B的个体参与繁殖的机会比较多,后代就会越来越多。适应度低的个体参与繁殖的机会比较少,后代就会越来越少。

  遗传与变异:新个体会遗传父母双方各一部分的基因,同时有一定的概率发生基因变异。
具体流程如下:
这里写图片描述
(注:图片来之百度)
(具体揹包问题概述):
32件物体,属性重量,体积,价值(普通的0/1规划只考虑重量或者体积)。揹包最大体积:75,最大重量:80,把物体装入揹包,保证价值的最大化。

//主函数入口
#include"gene.h"
#include <string.h>
#include <iostream>
using namespace std;


int main(int argc, char*argv[])
{
    Gene *gene = new Gene;     //实例化类,返回指针

    int gen = 0;
    int oldMaxPop, k;
    double oldMax;

    srand((unsigned)time(NULL));
    gene->initPop();
    memcpy(&gene->newPop, &gene->oldPop, POP_SIZE * sizeof(struct Gene::population));
    gene->statistics(gene->newPop);    //计算种群的最大适应度和最小适应度以及适应度的下表号。
    gene->report(gene->newPop, gen);

    while (gen < CENERAION_NUM)
    {
        gen += 1;
        if (gen % 100 == 0) {
            srand((unsigned)time(NULL));
        }
        oldMax = gene->maxFitness;   //oldmax为种群中最大适应度
        oldMaxPop = gene->maxPop;    //oldMaxPop指种群中最大适应度的个体
        gene->generation();
        gene->statistics(gene->newPop);

        if (gene->maxFitness < oldMax) {
            for (k = 0; k < CHROM_SIZE; k++) {
                gene->newPop[gene->minPop].chrom[k] = gene->oldPop[oldMaxPop].chrom[k];
            }
            gene->newPop[gene->minPop].fitness = gene->oldPop[oldMaxPop].fitness;
            gene->newPop[gene->minPop].weight = gene->oldPop[oldMaxPop].weight;
            gene->newPop[gene->minPop].volume = gene->oldPop[oldMaxPop].volume;
            gene->newPop[gene->minPop].parent1 = gene->oldPop[oldMaxPop].parent1;
            gene->newPop[gene->minPop].parent2 = gene->oldPop[oldMaxPop].parent2;
            gene->newPop[gene->minPop].cross = gene->oldPop[oldMaxPop].cross;
            gene->statistics(gene->newPop);
        }
        else if(gene->maxFitness > oldMax){
            gene->report(gene->newPop, gen);
        }
        memcpy(&gene->oldPop, &gene->newPop, POP_SIZE * sizeof(struct Gene::population));
    }

    delete[] gene;    //销毁对象占用空间
    system("pause");
    return 0;
}
/********
头文件的定义
********/

#pragma once
#ifndef GENE_H
#define GENE_H

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>

#define POP_SIZE 200 //定义种群规模
#define RRO_CROSS 0.618 //交叉概率
#define PRO_MUTATE 0.03 //变异概率
#define CHROM_SIZE 32  //给定染色体长度
#define CENERAION_NUM 1000 //定义繁殖代数
typedef unsigned int UINT;



class Gene
{
public:
    Gene();
    ~Gene();
public:
    struct population {      //定义私有的个体类
        UINT chrom[CHROM_SIZE];    //定义个体的基因组
        double weight;             //揹包的重量
        double volume;             //揹包的体积
        double fitness;            //个体的适应度
        UINT parent1, parent2, cross;   //双亲以及交叉的节点
    };
    population oldPop[POP_SIZE], newPop[POP_SIZE];

    int weight[CHROM_SIZE] = { 22, 15, 4, 5, 10, 19, 21, 20, 8, 13, 2, 3, 3, 17, 12, 5, 12, 4, 1, 21, 14, 23, 17, 15, 20, 22, 25, 0, 22, 15, 25, 13 };
    int volume[CHROM_SIZE] = { 11, 22, 12, 21, 21, 13, 1, 10, 13, 8, 6, 25, 13, 27, 12, 23, 12, 24, 23, 11, 6, 24, 28, 10, 20, 13, 25, 23, 5, 26, 30, 15 };
    int profit[CHROM_SIZE] = { 8, 9, 15, 6, 16, 9, 1, 4, 14, 9, 3, 7, 12, 4, 15, 5, 18, 5, 15, 4, 6, 2, 12, 14, 11, 9, 13, 13, 14, 13, 19, 4 };
    int containW = 80, containV = 75;

    double sumFitness;   //种群总适应度
    double minFitness;   //最小适应度
    double maxFitness;   //最大适应度
    double avgFitness;   //平均适应度

    double alpha; //计算适应度时的惩罚系数

    int minPop;  //种群内最大和最小的适应个体
    int maxPop;

    void initPop();  //总群初始化函数
    //int calWeight(UINT *chr);  //计算个体体积,重量,以及收益的函数
    //int Gene::calVolume(UINT *chr);
    int calSum(UINT *ch, int *pt);
    double calFit(UINT *ch);
    void statistics(struct population *pop);  //计算种群最大适应度和最小适应度的函数
    void report(struct population *pop, int gen); //为输出的函数
    int selection(int pop);   //通过选择总群中符合要求的父母进行繁殖 函数返回父母的位置
    int crossOver(UINT *parent1, UINT *parent2, int i);   //传入要更改的个体位置,随机产生交叉位置
    int excise(double probability);// 传入概率参数,进行交叉或者变异
    int mutation(UINT i);  //传入参数为基因组基因的位置,逐个基因判断变异概率
    void generation();  //种群群体更新的函数
};


#endif // !GENE_H
//类中代码的实现
#include "gene.h"
#include<bitset>
#include<iostream>
using namespace std;

Gene::Gene()
{
    cout << "begin" << endl;

}

Gene::~Gene()
{
}

int Gene::calSum(UINT *ch, int *pt)   //ch为装入揹包中的一个可能的解  pt为重量或者体积的指针
{
    int popSum = 0;
    for (int i = 0; i < CHROM_SIZE; i++) {
        popSum += (*ch) * pt[i];
        ch++;
    }
    return popSum;
}

void Gene::initPop()
{
    int tmpWeight = 0;
    int tmpVolume = 0;
    int m = 0;
    bool isPop = false;
    //最初代的种群的初始化
    for (int i = 0; i < POP_SIZE; i++) { //这里的POP_SIZE是种群规模
        while (!isPop){
            for (int j = 0; j < CHROM_SIZE; j++) {
                m = rand() % 1001;   //rand为初始化函数,这里设置生成0的概率要大一些
                if (m <= 499) oldPop[i].chrom[j] = 0;
                else oldPop[i].chrom[j] = 1;
                oldPop[i].parent1 = 0;
                oldPop[i].parent2 = 0;
                oldPop[i].cross = 0;
            }
            //剔除重量和体积大于揹包容量的体积的个体
            tmpWeight = calSum(oldPop[i].chrom, weight);
            tmpVolume = calSum(oldPop[i].chrom, volume);

            if ((tmpWeight <= containW) && (tmpVolume <= containV)) {
                oldPop[i].fitness = calSum(oldPop[i].chrom, profit);
                oldPop[i].weight = tmpWeight;
                oldPop[i].volume = tmpVolume;
                oldPop[i].parent1 = 0;
                oldPop[i].parent2 = 0;
                oldPop[i].cross = 0;
                isPop = true;
            }
        }
        isPop = false;
    }

}

void Gene::statistics(struct population *pop) 
{
    double tmpFitness;
    minPop = 0;
    maxPop = 0;

    sumFitness = pop[0].fitness;
    minFitness = pop[0].fitness;
    maxFitness = pop[0].fitness;

    for (int i = 1; i < POP_SIZE; i++) { 
        sumFitness += pop[i].fitness;
        tmpFitness = pop[i].fitness;

        //挑选出最大的适应度个体
        if ((tmpFitness > maxFitness) && ((int)(tmpFitness * 10) % 10 == 0)){
            maxFitness = pop[i].fitness;
            maxPop = i;
        }
        //挑选出最小的适应度个体
        if (tmpFitness < minFitness) {
            minFitness = pop[i].fitness;
            minPop = i;
        }
        //计算出平均的适应度
        avgFitness = sumFitness / (float)POP_SIZE;
    }
}

void Gene::report(struct population *pop, int gen)
{
    int popWeight = 0;
    cout << "The generation is " << gen << endl;  //显示种群的代数
    cout << "The population chrom is: " << endl;
    for (int j = 0; j < CHROM_SIZE; j++) {
        if (j % 4 == 0) cout << " ";
        cout << pop[maxPop].chrom[j];
    }
    cout << endl;
    cout << "The population's max fitness is: " << (int)pop[maxPop].fitness << endl;
    cout << "The population's max weight is: " << (int)pop[minPop].weight << endl;
    cout << "The population's max volume is: " << (int)pop[minPop].weight << endl;
}

int Gene::selection(int pop)   //使用轮赌法进行选择
{
    double wheelPos, randNumber, partsum = 0;
    int i = 0;
    randNumber = (rand() % 2001) / 2000.0;
    wheelPos = randNumber*sumFitness;
    do
    {
        partsum += oldPop[i].fitness;
        i++;
    } while ((partsum < wheelPos) && (i < POP_SIZE));
    return i - 1;
}

int Gene::crossOver(UINT *parent1, UINT *parent2, int i) 
{
    int j;         //基因组的基因位置
    int crossPos;  //交叉点的位置
    if (excise(RRO_CROSS)) { crossPos = rand() % (CHROM_SIZE - 1); }
    else { crossPos = CHROM_SIZE - 1; }
    for (j = 0; j <= crossPos; j++) { newPop[i].chrom[j] = parent1[j]; }
    for (j = crossPos + 1; j < CHROM_SIZE; j++) { newPop[i].chrom[j] = parent2[j]; }
    newPop[i].cross = crossPos;
    return 1;

}

int Gene::excise(double probability)   //传入概率参数,概率选择实验
{
    double pp;
    pp = (double)(rand() % 20001 / 20000.0);
    if (pp <= probability) { return 1; }
    else { return 0; }
}

int Gene::mutation(UINT alleles)
{
    if (excise(PRO_MUTATE)) {
        alleles == 0 ? alleles = 1 : alleles = 0;
    }
    return alleles;
}


void Gene::generation()
{
    UINT mate1, mate2;
    UINT i, j;
    int tmpWeight = 0;
    int tmpVolume = 0;
    bool notGen;
    for (i = 0; i < POP_SIZE; i++) {
        notGen = false;
        while (!notGen){
            mate1 = selection(i);  //选择有机率产生优良后代的双亲的位置
            mate2 = selection(i + 1);
            crossOver(oldPop[mate1].chrom, oldPop[mate2].chrom, i);
            for (j = 0; j < CHROM_SIZE; j++) {
                newPop[i].chrom[j] = mutation(newPop[i].chrom[j]); //给基因变异的概率
            }
            tmpWeight = calSum(newPop[i].chrom, weight);
            tmpVolume = calSum(newPop[i].chrom, volume);
            if ((tmpWeight <= containW) && (tmpVolume <= containV)) {
                newPop[i].fitness = calSum(newPop[i].chrom, profit);
                newPop[i].weight = tmpWeight;
                newPop[i].volume = tmpVolume;
                newPop[i].parent1 = mate1;
                newPop[i].parent2 = mate2;
                notGen = true;
            }
        }
    }
}
发布了50 篇原创文章 · 获赞 36 · 访问量 12万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章