遗传算法求函数最优解

前两天写了一个遗传算法求最优解的程序,今天拿出来简单整理一下。

程序要解决的问题:

最优解问题:四个变量取何值,该函数取最大值时?(取值范围-5到5)
这里写图片描述
显然,当四个未知数都为零的时候,函数取最大值,但如何利用遗传算法来实现,才是要研究的问题。

上代码:

1.染色体结构定义define.h
struct chromosome
{
    vector<double> s_chromosome;
    double s_value;
    double s_fit;
};
//基因
#define gene double
//种群
#define pool vector<struct chromosome>
2.遗传算法类定义,以及辅助函数GA.h
//参数:
//种群规模N
//交配概率Pc
//变异概率Pm

class GA
{
public:
    GA(int N,double pc,double pm);  //根据参数确定种群规模
    GA(int N);
    ~GA(void);
    //初始化
    bool initial_pool();
    //开始迭代
    int run(int Maxnum,double d);
    double get_best_value();
    chromosome get_best_chro();
private:
    int c_n;                        //种群规模
    double c_Pc;                    //交配概率Pc
    double c_Pm;                     //变异概率Pm
    gene c_gene_tmp;                //临时基因
    chromosome c_chromosome_temp;   //一条临时染色体
    pool c_pool;                    //种群
    //适应值评价,最优染色体
    bool eval_pool();
    //选择
    bool selection();
    //交配
    bool crossover();
    //变异
    bool mutation();
};

//设定取值范围
inline double get_5_5_rand()
{
    random_device rd;
    double a=double((rd()*6553)%100000)/100000;
    a=(a-.5)*10;
    return a;
}

//得到0--3之间的随机数
inline int get_0_3_rand()
{
    random_device rd;
    int a= rd()%4;
    return a;
}

//0--1
inline double get_0_1_rand()
{
    random_device rd;
    double a=double((rd()*6553)%100000)/100000;
    return a;
}

inline double get_chro_value(chromosome chro)
{
    double a=0,tmp=0;
    for(int i=0;i<chro.s_chromosome.size();i++)
    {
        tmp=tmp+chro.s_chromosome[i]*chro.s_chromosome[i];
    }
    a=1/(tmp+1);
    return a;
}
3.遗传算法成员函数实现GA.cpp
#include "GA.h"
inline bool sort_value(const chromosome &aim1,const chromosome &aim2);

GA::GA(int N,double pc,double pm)
{
    c_n=N;
    c_Pc=pc;
    c_Pm=pm;
}
GA::GA(int N)
{
    c_n=N;
    c_Pc=0.5;   //一般取值0.4--0.99
    c_Pm=0.1;   //一般取值0.001-0.1
}

GA::~GA(void)
{
    cout<<"清理GA类内存------------------"<<endl;
}

bool GA::initial_pool()
{
    c_pool.clear();
    //初始化染色体群
    for(int j=0; j < c_n;j++)
    {
        c_chromosome_temp.s_chromosome.clear();
        //得到一条染色体
        for(int i =0;i<4;i++)
        {
            c_chromosome_temp.s_chromosome.push_back(
                get_5_5_rand());
        }
        c_chromosome_temp.s_value=0;
        c_pool.push_back(c_chromosome_temp);
    }
    cout<<"初始值:"<<endl;
    cout<<"x1:"<<get_best_chro().s_chromosome[0]
        <<"  x2:"<<get_best_chro().s_chromosome[1]
        <<"  x3:"<<get_best_chro().s_chromosome[2]
        <<"  x4:"<<get_best_chro().s_chromosome[3]<<endl;
    return true;
}

//适应值评价
bool GA::eval_pool()
{
    int i=0;
    while (i!=c_pool.size())
    {
        double temp=0;
        for(int j=0;j<c_pool[i].s_chromosome.size();j++)
        {
            temp = temp+
                c_pool[i].s_chromosome[j]*c_pool[i].s_chromosome[j];
        }
        c_pool[i].s_value=1/(temp+1);
        i++;
    }
    sort(c_pool.begin(),c_pool.end(),sort_value);

    return true;
}

inline bool sort_value(const chromosome &aim1,const chromosome &aim2)
{
    return (aim1.s_value>aim2.s_value);
}


//根据对大自然的适应能力,进行自然选择,优胜劣汰
bool GA::selection()
{
    double all_value=0;
    int i=0;
    while(true)
    {
        all_value+=c_pool[i].s_value;
        i++;
        if(i>=c_pool.size())
        {
            i=0;
            break;
        }
    }
    //计算适应值fit
    while(true)
    {
        c_pool[i].s_fit=c_pool[i].s_value/all_value;
        i++;
        if(i>=c_pool.size())
        {
            i=0;
            break;
        }
    }
    //赌盘选择
    pool tmp_pool;
    for(int k=0;k<c_pool.size();k++)
    {
        double m=0,r=get_0_1_rand();
        for(int j=0;j<c_pool.size();j++)
        {
            m=m+c_pool[j].s_fit;
            if(r<=m)
            {
                tmp_pool.push_back(c_pool[j]);
                break;
            }
        }
    }
    c_pool.clear();
    c_pool=tmp_pool;
    return true;
}

//染色体交配
bool GA::crossover()
{
    vector<chromosome>::iterator it=c_pool.begin();
    pool cross_pool,no_cross_pool;
    //得到交叉池和非交叉池
    for(int i=0;i<c_pool.size();i++)
    {
        double a=get_0_1_rand();
        if (a<c_Pc)
            cross_pool.push_back(c_pool[i]);
        else 
            no_cross_pool.push_back(c_pool[i]);
    }
    //进行交叉
    //不需要交叉
    if(cross_pool.size()==0)
    {
        c_pool.clear();
        c_pool=no_cross_pool;
        return true;
    }
    else if (cross_pool.size()==1)
    {
        c_pool.clear();
        c_pool=no_cross_pool;
        c_pool.push_back(cross_pool[0]);
        return true;
    }
    else
    {
        chromosome cross_tmp1,cross_tmp2,cross_1,cross_2;
        for(int i=0;i<4;i++)
        {
            cross_tmp1.s_chromosome.push_back(0);
            cross_tmp2.s_chromosome.push_back(0);
        }
        //交叉数量位偶数
        if (cross_pool.size()%2==0)
        {
            for(int i=0;i<cross_pool.size();i+=2)
            {
                int lc=get_0_3_rand();
                //开始交叉
                for(int j=0;j<4;j++)
                {
                    cross_1=cross_pool[i];
                    cross_2=cross_pool[i+1];
                    if(lc<j)
                    {
                        cross_tmp1.s_chromosome[j]=cross_pool[i].s_chromosome[j];
                        cross_tmp2.s_chromosome[j]=cross_pool[i+1].s_chromosome[j];
                    }
                    else
                    {
                        cross_tmp2.s_chromosome[j]=cross_pool[i].s_chromosome[j];
                        cross_tmp1.s_chromosome[j]=cross_pool[i+1].s_chromosome[j];
                    }

                }
                //交叉结束,将新的染色体放入nocrosspool
                if(get_chro_value(cross_tmp1)>get_chro_value(cross_1))
                    no_cross_pool.push_back(cross_tmp1);
                else 
                    no_cross_pool.push_back(cross_1);
                if(get_chro_value(cross_tmp2)>get_chro_value(cross_2))
                    no_cross_pool.push_back(cross_tmp2);
                else 
                    no_cross_pool.push_back(cross_2);
            }
        }
        //交叉数量位奇数,放弃交叉第一个
        else
        {
            no_cross_pool.push_back(cross_pool[0]);
            for(int i=1;i<cross_pool.size();i+=2)
            {
                int lc=get_0_3_rand();
                //开始交叉
                for(int j=0;j<4;j++)
                {
                    cross_1=cross_pool[i];
                    cross_2=cross_pool[i+1];
                    if(lc<j)
                    {
                        cross_tmp1.s_chromosome[j]=cross_pool[i].s_chromosome[j];
                        cross_tmp2.s_chromosome[j]=cross_pool[i+1].s_chromosome[j];
                    }
                    else
                    {
                        cross_tmp2.s_chromosome[j]=cross_pool[i].s_chromosome[j];
                        cross_tmp1.s_chromosome[j]=cross_pool[i+1].s_chromosome[j];
                    }

                }
                //交叉结束,将新的染色体放入nocrosspool
                //如果交叉后代比上一代更好,就把父代替换子代
                if(get_chro_value(cross_tmp1)>get_chro_value(cross_1))
                    no_cross_pool.push_back(cross_tmp1);
                else 
                    no_cross_pool.push_back(cross_1);
                if(get_chro_value(cross_tmp2)>get_chro_value(cross_2))
                    no_cross_pool.push_back(cross_tmp2);
                else 
                    no_cross_pool.push_back(cross_2);
            }
        }
        c_pool.clear();
        c_pool=no_cross_pool;
        return true;
    }
}

//根据变异率,进行染色体变异
bool GA::mutation()
{
    int i=0;
    while(true)
    {
        double rm=get_0_1_rand();
        chromosome bianyi=c_pool[i];
        if(rm<c_Pm)
        {
            int lc=get_0_3_rand();//变异位置
            bianyi.s_chromosome[lc]=get_5_5_rand();
            if(get_chro_value(bianyi)>get_chro_value(c_pool[i]))
                c_pool[i]=bianyi;
        }
        i++;
        if(i>=c_pool.size())
        {
            i=0;
            break;
        }
    }
    return true;
}


int GA::run(int Maxnum,double d)
{
    int i=0;
    double last_value=0,current_value;
    while (true)
    {
        i++;
        eval_pool();
        current_value=c_pool[0].s_value;
        selection();
        crossover();
        mutation();
        if(i%40==0)
            cout<<"current_value:"<<current_value<<endl;
        if(abs(current_value-last_value)<d)
        {
            //cout<<"相邻迭代误差达到要求"<<endl;
            //break;
        }
        if(i>=Maxnum)
        {
            cout<<"超出迭代次数,算法结束!"<<endl;
            break;
        }
        last_value=current_value;
    }
    eval_pool();
    return i;
}

double GA::get_best_value()
{
    return c_pool[0].s_value;
}

chromosome GA::get_best_chro()
{
    return c_pool[0];
}
4.主函数main.cpp
#include "GA.h"
#include "test.h"

#define ERROR 0.00000001
#define MAXNUM 1000

int main()
{
    GA my_GA(10);
    my_GA.initial_pool();
    cout<<"迭代次数:"<<my_GA.run(MAXNUM,ERROR)<<endl;
    cout<<"最优解:"<<my_GA.get_best_value()<<endl;
    cout<<"x1:"<<my_GA.get_best_chro().s_chromosome[0]
        <<"  x2:"<<my_GA.get_best_chro().s_chromosome[1]
        <<"  x3:"<<my_GA.get_best_chro().s_chromosome[2]
        <<"  x4:"<<my_GA.get_best_chro().s_chromosome[3]<<endl;
    system("pause");
}

小结:

在程序编写过程中,遇到了几个小问题

  • 第一个是随机数生成,由于需要每次调用随机生成一个随机数,采用
    srand((unsigned)time(NULL));
    的方法发现每一次生成的随机数都是一样的规律,不能用于遗传算法,网上查了一下发现,c++自带了一种随机数生成器,可以产生较为“随机”的随机数,最起码每次的规律是不相同的。使用方法:
#include <random>
//得到0--3之间的随机数
inline int get_0_3_rand()
{
    random_device rd;
    int a= rd()%4;
    return a;
}
  • 第二个问题是种群交叉变异的过程中,如果没有添加“如果子代没有父代好,就放弃这次交叉或者变异”,导致程序收敛效果很不好,故而加了这一限制条件,收敛情况大有改善。
//交叉结束,将新的染色体放入nocrosspool
if(get_chro_value(cross_tmp1)>get_chro_value(cross_1))
    no_cross_pool.push_back(cross_tmp1);
else 
    no_cross_pool.push_back(cross_1);
if(get_chro_value(cross_tmp2)>get_chro_value(cross_2))
    no_cross_pool.push_back(cross_tmp2);
else 
    no_cross_pool.push_back(cross_2);
  • 第三个问题是种群规模的选择,在选择种群规模时要同时考虑算法计算速度和整体收敛速度的关系,最后设置了种群规模为10,迭代次数1000时已经能够达到很好的收敛效果,实际上如果加大种群规模,迭代次数也将大为减少,为了便于观察,选择了这一数字,发现程序收敛效果比较不错。
    这里写图片描述

参考:

  • 《计算智能》 张军 詹志辉等 清华大学出版社
  • 11
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值