您的位置:首页 > 编程语言 > C语言/C++

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

2017-10-31 20:00 736 查看
(背包问题):背包只能容得下一定重量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<
4000
/span>, 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]); //给基因变异的概率
cbfa
}
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;
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息