(揹包問題):揹包只能容得下一定重量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;
}
}
}
}