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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章