(→通过算法的乐趣第16章学习)
0
遗传算法是干嘛的?
貌似很高大上。
求最优解啊。
废话。
好吧就是比穷举快的随机化算法。
1
达尔文提出进化论时,肯定没想到它还会有今天这样的应用。“物竞天择,适者生存”已经成为一条真理。遗传算法就是根据这条真理而来的。
可以这么说,遗传算法就是一群(同种)小动物(解)在撕逼,弱的(基因不好)挂掉(一定概率),强的(基因好)活下来(也是一定概率( •̀ ω •́ )y),还有各种杂交,各种变异,最后撕出最牛逼的就是最优解或近似解(毕竟有随机因素)。
不是很清楚啊,那么明确下面这几个概念。
- 基因:参与计算的遗传特征
- 种群,个体:生物进化的形式为种群,而种群中的每个生物体就是一个个体
- 适者生存:强者后代多,弱者后代少
- 遗传,变异:下一代遗传上一代的部分基因,但有一定的概率基因突变
- 算法基因交叉:就是生物学的繁殖,将两个个体基因编码交换即可得到下一代个体
- 算法基因突变:直接替换个体基因中的某个或几个编码
- 算法选择:由个体的适应度,按照一定的规则从上一代种群中选择一些优良的个体遗传到下一代
就是这样
2
OK,接下来,how to do?
(流程图丑爆了)
看起来原理相当简单,实际上…
也的确很简单
3
好吧实际上好有些问题没有解决,比如说基因到底是什么鬼?
好吧,给个定义
- 遗传算法中的基因:以某种编码的形式表示的实际问题的解 then,how to code? 事实上,方法针对实际问题千变万化,举个栗子
我们有这样一些物品要放进背包,比如说5个,这事就用二进制表示某个物品是否被放进去(1为是,0为否),像酱紫 [1,1,0,1,0]
十进制就是26 这样子 还有格雷编码,符号编码,属性序列编码等等乌七八糟的 然后就不具体分析了… - 然后是适应度评估函数
适应度评估函数:首先对种群中个体的基因解码,然后根据问题空间得到其对应的结果,最后根据问题的类型和最优解的形式,按规则进行评估转换,得到个体适应度 这一串其实就是看个体的基因对问题的要求是否尽量符合,看它是否适应”环境“的程度
你可以在算法的不同阶段使用不同的函数(自适应或否),也可以简单的使用固定的函数来处理,在此不再展开 - 接着是重点——遗传算子的设计
什么是算子?广义的讲,对任何函数进行某一项操作都可以认为是一个算子。遗传算法中有三个遗传算子:选择算子、交叉算子和变异算子 下面具体分析
4
选择算子:作用是从群体中选择比较适应环境的个体复制到下一代。
介绍比例选择的策略
每个个体进入下一代的概率等于它的适应度值与整个种群中的个体适应的值总和的比例。
我们通过随机概率来选择个体,而选择概率和积累概率须事先计算
交叉算子:作用是将两个个体的基因的一部分片段互相交换,以产生新个体
介绍多点交叉的策略
对两个随机选中的个体的基因进行交换,基因交换的位置和个数都是随机选择,而交叉概率用于判断是否进行交叉运算(可以生成一随机数,判断其是否小于交叉概率),而交叉概率的大小——想想我们多少人会有后代就知道了。
变异算子:作用是直接替换基因片段,产生更好或更坏的新个体
介绍均匀变异的策略
对基因编码的每一位噫平均分布的概率进行选择,这需要变异概率,而这个概率应较低。
5
好吧,大致就是这样。遗传算法千变万化,这样的算法只是基础。
遗传算法可以用于旅行商问题,背包问题和装箱问题中,事实上基础算法的效率也不错,下面给出01背包的代码
#include <cstdlib>
#include <cstdio>
#include <ctime>
using namespace std;
#define MAX_N 40
int OBJ_COUNT;
int CAPACITY;
const int PUNISH=0.1;//惩罚,视情况修改
const int POPULATION_SIZE=32;//个体数量
const int MAX_GENERATIONS=500;//进化代数
const double P_XOVER=0.8;//交叉概率
const double P_MUTATION=0.15;//变异概率
int Weight[MAX_N];
int Value[MAX_N];
typedef struct GAType {
int gene[MAX_N];//基因
int fitness;//适应度
double rf;//选择概率
double cf;//累积概率
}GATYPE;
//生成任意解
void GetRandomGene(int *gene,int count) {
for(int i=0;i<count;i++)
gene[i]=rand()%2;
}
//初始化
void Initialize(GATYPE *pop) {
for(int i=0;i<POPULATION_SIZE;i++) {
GetRandomGene(pop[i].gene,OBJ_COUNT);
pop[i].fitness=0;
pop[i].rf=0.0;
pop[i].cf=0.0;
}
}
//适应度评估函数
int EnvaluateFitness(GATYPE *pop) {
int totalFitness=0;
for(int i=0;i<POPULATION_SIZE;i++) {
int tw=0;
pop[i].fitness=0;
for(int j=0;j<OBJ_COUNT;j++)
if(pop[i].gene[j]==1) {
tw+=Weight[j];
pop[i].fitness+=Value[j];
}
if(tw>CAPACITY)
pop[i].fitness=PUNISH;
totalFitness+=pop[i].fitness;
}
return totalFitness;
}
//最好情况
int GetBestPopulation(GATYPE *pop,GATYPE *bestGene) {
int best=0;
for(int i=0;i<POPULATION_SIZE;i++)
if(pop[i].fitness>pop[best].fitness)
best=i;
*bestGene=pop[best];
return best;
}
//最坏情况
int GetWorstPopulation(GATYPE *pop) {
int worst=0;
for(int i=0;i<POPULATION_SIZE;i++) {
if(pop[i].fitness<pop[worst].fitness) {
worst=i;
}
}
return worst;
}
void Select(int totalFitness,GATYPE *pop) {
int i;
GATYPE newPop[POPULATION_SIZE] = { 0 };
GATYPE best;
double lastCf=0.0;
for(i=0;i<POPULATION_SIZE;i++) {
pop[i].rf=(double)pop[i].fitness/totalFitness;
pop[i].cf=lastCf+pop[i].rf;
lastCf=pop[i].cf;
}
GetBestPopulation(pop,&best);
for(i=0;i<POPULATION_SIZE;i++) {
double p=(double)rand()/(RAND_MAX+1);
if(p<pop[0].cf)
newPop[i]=best;//;//pop[0];//
else {
for(int j=0;j<POPULATION_SIZE;j++) {
if((p>=pop[j].cf)&&(p<pop[j+1].cf)) {
newPop[i]=pop[j+1];
}
}
}
}
for(i=0;i<POPULATION_SIZE;i++)
pop[i]=(newPop[i].fitness==1)?best:newPop[i];
}
//基因交换
void ExchangeOver(GATYPE *pop, int first, int second) {
int ecc=rand()%OBJ_COUNT+1;
for(int i=0;i<ecc;i++) {
int idx=rand()%OBJ_COUNT;
int tg=pop[first].gene[idx];
pop[first].gene[idx]=pop[second].gene[idx];
pop[second].gene[idx]=tg;
}
}
//交叉算子设计
void Crossover(GATYPE *pop) {
int first=-1;
for(int i=0;i<POPULATION_SIZE;i++) {
double p=(double)rand()/(RAND_MAX + 1);
if(p<P_XOVER)
if(first<0) first=i;
else {
ExchangeOver(pop,first,i);
first=-1;
}
}
}
//基因变异
void ReverseGene(GATYPE *pop,int index) {
int mcc=rand()%OBJ_COUNT+1;
for(int i=0;i<mcc;i++) {
int gi=rand()%OBJ_COUNT;
pop[index].gene[gi]=1-pop[index].gene[gi];
}
}
//变异算子设计
void Mutation(GATYPE *pop) {
for(int i=0;i<POPULATION_SIZE;i++) {
double p=(double)rand()/(RAND_MAX+1);
if(p<P_MUTATION)
ReverseGene(pop,i);
}
}
const int TEST_ROUND=5;//是不是太大了
int main() {
srand((unsigned)time(NULL));
scanf("%d",&CAPACITY);
scanf("%d",&OBJ_COUNT);
for(int i=0;i<OBJ_COUNT;i++)
scanf("%d%d",&Weight[i],&Value[i]);
//clock_t start,finish;
//double duration;
//start=clock();
int success=0;
GATYPE population[POPULATION_SIZE] = { 0 };
int ans=-1;
for(int k=0;k<TEST_ROUND;k++) {
Initialize(population);
int totalFitness=EnvaluateFitness(population);
for(int i=0;i<MAX_GENERATIONS;i++) {
Select(totalFitness, population);
Crossover(population);
Mutation(population);
totalFitness = EnvaluateFitness(population);
}
GATYPE best;
GetBestPopulation(population,&best);
if(best.fitness>ans)
ans=best.fitness;
}
printf("%d\n",ans);
//finish = clock();
//duration = (double)(finish-start)/CLOCKS_PER_SEC;
//printf("%f seconds\n",duration);
return 0;
}
测试MAX_WEIGHT=300,N=10,测试次数TEST_ROUND=500时的数据时,若进化代数为500,则正确次数为470-490上下,运行时间为2s;若进化代数为100,则正确次数为400-420上下,运行时间为0.4s。
貌似不太对劲啊。。。
没错我是逗你的,上面是只有32个个体的情况
换成64呢?
测试MAX_WEIGHT=300,N=10,测试次数TEST_ROUND=500时的数据时,若进化代数为500,则正确次数为495以上,运行时间为6.75s左右;若进化代数为100,则正确次数为450-470上下,运行时间为1.5s左右。
END
ps:一个看不懂的blog
pps:另一个看不懂的blog
ppps:当然前一个是为工程服务的……
pppps:某个讲原理的ppt