1. 概述

早在几个周以前就有同学在群里面说使用启发式算法来解决来解决这个问题,当然也有同学使用这个方法得到很不错的效果。在本篇文章中将遗传算法作为一个例子,列出来与大家交流。
首先对遗传算法中的几个概念做一下简短的介绍。遗传算法是根据生物进化演变的规律总结来的,因而其中的一些概念也是跟生物息息相关的。
交叉:
有性生殖生物在繁殖下一代时,两个同源染色体之间通过交叉而重组,亦即在两个染色体的某一相同位置处DNA被切断,其前后两串分别交叉组合而形成新的染色体,这个新的染色体继承了两个父代染色体的基因。

变异:
虽然概率很小,但有可能产生某些基因复制差错,从而使DNA发生某种变异,产生出新的染色体,这些新的染色体表现出新的生物性状。
适应性:
根据达尔文的自然选择学说,地球上的生物具有很强的繁殖能力。在繁殖过程中,大多数生物通过遗传,使物种保持相似的后代;部分生物由于变异,后代具有明显差别,甚至形成新物种。正是由于生物的不断繁殖后代,生物数目大量增加,而自然界中生物赖以生存的资源却是有限的。因此,为了生存,生物就需要竞争。生物在生存竞争中,根据对环境的适应能力,适者生存,不适者消亡。自然界中的生物,就是根据这种优胜劣汰的原则,不断地进行进化。每一个个体对其生存环境都有不同的适应能力,这种适应能力称为个体的适应度。适应性强的个体有更高多机会生存并遗传下一代。
群体:
生物的进化是以集团的形式共同进行的,这样的一个团体称为群体或称为种群;组成群体的单个生物称为个体。
针对比赛中提出的问题,这里采用0-1规划与遗传算法结合,因而下面的概念可以解释为:
编码:染色体是生物个体的唯一表示,通常用编码来表示染色体;在比赛中使用0-1来表示该节点是否是服务器节点,也就是二进制编码。
群体:个体的集合;在比赛中种群的大小也意味着计算量的大小,因而在大图的时候可以适当减小种群的数量。
适应度:个体的适应性、生命力的度量;在比赛中使用最小费用最大流计算得到的代价来作为它的代价函数,也可以采用拉格朗日松弛来简化数学模型。
选择(复制):适应度高的个体,被大自然选择并生存的机率高;采用随机类似赌轮的方式进行。
交叉:个体间的基因交叉,生物遗传的主要行为;
变异:个体继承父代基因时的基因变异,生物进化的主要行为。

2. 编码

///*遗传算法 start*
# define POPSIZE 10     //种群内个体数量
# define MAXGENS 100        //最大的迭代次数
static int NVARS = 3;      //变量个数,即用以表示基因型的bit数
# define PXOVER 0.85        //交换率
# define PMUTATION 0.3  //突变率  struct genotype
{double gene[MAX_NODE];double fitness;double upper[MAX_NODE];double lower[MAX_NODE];double rfitness;double cfitness;
};struct genotype population[POPSIZE + 1];
struct genotype newpopulation[POPSIZE + 1];
///*遗传算法 end*/////*遗传算法 start*
//遗传算法搜索
void my_GAsearch()
{int seed = 772002;srand((unsigned)time(NULL));GA_initialize(seed);evaluate();keep_the_best();for (int generation = 0; generation < MAXGENS; generation++){selector(seed);crossover(seed);mutate(seed);evaluate();elitist();}
}//选择两个个体进行交叉变异
void crossover(int &seed)
{  const double a = 0.0;  const double b = 1.0;  int mem;  int one;  int first = 0;  double x;  for (mem = 0; mem < POPSIZE; ++mem )  {  //x = r8_uniform_ab ( a, b, seed );  x = Dou_uniform_ab( a, b);  if ( x < PXOVER )  {  ++first;  if ( first % 2 == 0 )  {  Xover ( one, mem, seed );  }  else  {  one = mem;  }  }  }  return;
}  //选出本轮中最优和最差的个体,将本轮中最好的个体赋值给最后一个个体,将上轮中最坏的个体赋值给当前最坏的个体
void elitist()
{  int i;  double best;  int best_mem;  double worst;  int worst_mem;  best = population[0].fitness;  worst = population[0].fitness;  for (i = 0; i < POPSIZE - 1; ++i )  {  if ( population[i+1].fitness < population[i].fitness )  {  if ( best <= population[i].fitness )  {  best = population[i].fitness;  best_mem = i;  }  if ( population[i+1].fitness <= worst )  {  worst = population[i+1].fitness;  worst_mem = i + 1;  }  }  else  {  if ( population[i].fitness <= worst )  {  worst = population[i].fitness;  worst_mem = i;  }  if ( best <= population[i+1].fitness )  {  best = population[i+1].fitness;  best_mem = i + 1;  }  }  }  //   //  If the best individual from the new population is better than   //  the best individual from the previous population, then   //  copy the best from the new population; else replace the   //  worst individual from the current population with the   //  best one from the previous generation                       //  if ( population[POPSIZE].fitness <= best )  {  for ( i = 0; i < NVARS; i++ )  {  population[POPSIZE].gene[i] = population[best_mem].gene[i];  }  population[POPSIZE].fitness = population[best_mem].fitness;  }  else  {  for ( i = 0; i < NVARS; i++ )  {  population[worst_mem].gene[i] = population[POPSIZE].gene[i];  }  population[worst_mem].fitness = population[POPSIZE].fitness;  }   return;
}  //对遗传算法的效果进行评价
void evaluate()
{  int member;  int i;  double x[800+1];  int temp_flow(0);    //流量标记std::vector<int> m_server;  //取到的时候对应的serverfor ( member = 0; member < POPSIZE; member++ )  {  int flow(0), cost(0);double sum=0;for ( i = 0; i < NVARS; i++ )  {  x[i+1] = population[member].gene[i]; }std::vector<int> temp = GA_get_server_pos(x);   population[member].fitness = get_fitness(temp, cost, flow); if (flow >= needed_flow){if (min_cost > cost){min_cost = cost;m_server = temp;}}   //满足了流量要求,则要求其最小费用else{if (temp_flow < flow){temp_flow = flow;m_server = temp;}}    //不满足流量需求,则满足最大流量需求}  xjbs_search(m_server); //局部搜索最优GA_update(m_server);return;
}//使用搜索出来的局部最优解去更新遗传的参数
void GA_update(std::vector<int> m_server)
{for (int i = 0; i < NVARS; i++){for (int j = 0; j < POPSIZE; j++){if (CheckIsServer(i, m_server)){population[j].gene[i] += Dou_uniform_ab(0.0, 0.2);;}//else//   population[j].gene[i] = 0.2;}}
}//计算自定义的度量函数
double get_fitness(std::vector<int>& server, int& cost, int& flow)
{if(server.size()<=0){cout << "get 0 server pos in GA, ERROR" << endl;return (-MAXINT);}Dij_init(server);flow = 0;cost = Dij_MinCostFlow(net_node_num, (net_node_num+1), flow);cost = cost+server.size()*server_cost;cout << "server_num:" << server.size() <<" ,loop: min cost: " << cost << " , max flow: " << flow << " / " << needed_flow << endl;//return flow;if(flow >= needed_flow){if(min_cost > cost){//cout << "find better: " << cost << endl;min_server = server;min_cost = cost;min_path = path;}return cost;}else{return (needed_flow-flow)*10*cost;}
} //根据遗传算法得到的参数的解,得到服务器的位置
std::vector<int> GA_get_server_pos(double* data)
{std::vector<int> result;if(nullptr == data)return result;for(int i=0; i<NVARS-1; i++){if(data[i] > 0.88)result.push_back(i);}return result;
}//my new design function of the distribution
int Int_uniform_ab(int a, int b)
{   int tmp;  tmp = (rand() % (b-a+1))+ a;  return tmp;
}  //遗传算法初始化
void GA_initialize(int& seed)
{  //  Initialize variables within the boundsstd::vector<int> server;// = min_server;    for (int i=0; i<client_node_num; i++){server.push_back(client_node[i].innode[0]);}NVARS = net_node_num;//+1;    //设置GA算法的参数变量的个数为网络中节点的个数for (int i = 0; i < NVARS; i++){for (int j = 0; j < POPSIZE; j++){population[j].fitness = 0;population[j].rfitness = 0;population[j].cfitness = 0;population[j].lower[i] = 0;  //代表不选中该点作为服务器population[j].upper[i] = 1;  //代表选中该点作为服务器if (CheckIsServer(i, must_server)){population[j].gene[i] = 1.0;}else if (CheckIsServer(i, forbid_server)){population[j].gene[i] = 0.0;}else population[j].gene[i] = r8_uniform_ab(0.2, 0.9, seed);//population[j].gene[i] = Dou_uniform_ab(0.0, 1.0);  }}
}  //保存最好的个体到最后一个中去
void keep_the_best( )
{  int cur_best;  int mem;  int i;  cur_best = 0;  for ( mem = 0; mem < POPSIZE; mem++ )  {  if ( population[POPSIZE].fitness < population[mem].fitness )  {  cur_best = mem;  population[POPSIZE].fitness = population[mem].fitness;  }  }  //   //  Once the best member in the population is found, copy the genes.  //  for ( i = 0; i < NVARS; i++ )  {  population[POPSIZE].gene[i] = population[cur_best].gene[i];  }  return;
}  //变异
void mutate(int &seed )
{  const double a = 0.0;  const double b = 1.0;  int i;  int j;  double lbound;  double ubound;  double x;  for ( i = 0; i < POPSIZE; i++ )  {  for ( j = 0; j < NVARS; j++ )  {  x = r8_uniform_ab ( a, b, seed );  //x = Dou_uniform_ab( a, b);  if ( x < PMUTATION )  {  lbound = population[i].lower[j];  ubound = population[i].upper[j];    population[i].gene[j] = r8_uniform_ab ( lbound, ubound, seed );  //population[i].gene[j] = Dou_uniform_ab( lbound, ubound );  }  }  }  return;
}  //通过随机种子选取a,b之间的随机数
double r8_uniform_ab( double a, double b, int &seed )
{  int i4_huge = 2147483647;  int k;  double value;  if ( seed == 0 )  {  std::cerr << "\n";  std::cerr << "R8_UNIFORM_AB - Fatal error!\n";  std::cerr << "  Input value of SEED = 0.\n";  exit ( 1 );  }  k = seed / 127773;  seed = 16807 * ( seed - k * 127773 ) - k * 2836;  if ( seed < 0 )  {  seed = seed + i4_huge;  }  value = ( double ) ( seed ) * 4.656612875E-10;  value = a + ( b - a ) * value;  return value;
}  //随机选择a,b之间的随机数
double Dou_uniform_ab(double a, double b)
{  double tmp;  //rand() / double(RAND_MAX)可以生成0~1之间的浮点数  tmp = a + static_cast<double>(rand())/RAND_MAX*(b-a);  return tmp;
}  //计算相对适应度和累积适应度,然后根据适应度进行选择和赋值
void selector(int &seed)
{  const double a = 0.0;  const double b = 1.0;  int i;  int j;  int mem;  double p;  double sum; double max_value(0);//  Find the total fitness of the population.  sum = 0.0;  for (mem = 0; mem < POPSIZE; mem++){max_value = max_value<population[mem].fitness?population[mem].fitness:max_value;}//for ( mem = 0; mem < POPSIZE; mem++ )  //{  //    sum += max_value - population[mem].fitness;  //}  //  Calculate the relative fitness of each member.  for ( mem = 0; mem < POPSIZE; mem++ )  {  population[mem].rfitness = (max_value-population[mem].fitness) / max_value;  }  //  Calculate the cumulative fitness.  population[0].cfitness = population[0].rfitness;  for ( mem = 1; mem < POPSIZE; mem++ )  {  population[mem].cfitness = population[mem-1].cfitness +         population[mem].rfitness;  }  //  Select survivors using cumulative fitness.   for ( i = 0; i < POPSIZE; i++ )  {   p = r8_uniform_ab ( a, b, seed );  //p = Dou_uniform_ab(a,b);  if ( p < population[0].cfitness )  {  newpopulation[i] = population[0];        }  else  {  for ( j = 0; j < POPSIZE; j++ )  {   if ( population[j].cfitness <= p && p < population[j+1].cfitness )  {  newpopulation[i] = population[j+1];  }  }  }  }  //  Overwrite the old population with the new one.  for ( i = 0; i < POPSIZE; i++ )  {  population[i] = newpopulation[i];   }  return;
}//基因的交叉操作
void Xover( int one, int two, int &seed )
{  int i;  int point;  double t;  //  Select the crossover point.  //point = i4_uniform_ab ( 0, NVARS - 1, seed );  point = Int_uniform_ab(0, NVARS-1);  //  Swap genes in positions 0 through POINT-1.  for ( i = 0; i < point; i++ )  {  t                       = population[one].gene[i];  population[one].gene[i] = population[two].gene[i];  population[two].gene[i] = t;  }  return;
}
///*遗传算法 end*//

3. 总结

3.1 遗传算法深化

遗传算法的全局搜索能力强,但局部搜索能力就不行了。解释:比如对于一条染色体,遗传算法并不会去看看这条染色体周围局部的染色体适应度怎么样,是否比这条染色体好。遗传算法会通过变异和交叉产生新的染色体,但新产生的染色体可能和旧染色差的很远。因此遗传算法的局部搜索能力差。

对于一些局部搜索算法如:梯度法、爬山法和贪心法等算法的局部搜索能力强,运算效率也高。若能够将问题相关的启发知识引入这些算法,这些算法的威力不可小觑。受此启发,人们提出了混合遗传算法,将遗传算法和这些算法结合起来。混合遗传算法的框架是遗传算法的,只是生成新一代种群之后,对每个个体使用局部搜索算法寻找个体周围的局部最优点。

混合遗传算法是很常见的策略。比如遗传算法应用于排序问题,生成新一代种群之后,将个体中相邻两个元素交换次序,如果新的个体适应度更高则保留。这种贪心的变种往往能大幅度提高遗传算法的收敛速率。
3.2 个人观点

无论是上一讲中的禁忌搜索还是这篇文章中将的遗传算法,都是启发式算法,都有计算速度慢的问题,但是对于解决大规模问题是一种很好的方法。对这些概念的理解也很简单,可以用一下的一段话来理解:
曾经有过一个「有志气的兔子」的故事,相信很多朋友都听过:为了找出地球上最高的山,一群有志气的兔子们开始想办法:
  兔子朝着比现在高的地方跳去。他们找到了不远处的最高山峰。但是这座山不一定是珠穆朗玛峰。这就是局部搜索,它不能保证局部最优值就是全局最优值。
  兔子喝醉了。他随机地跳了很长时间。这期间,它可能走向高处,也可能踏入平地。但是,他渐渐清醒了并朝最高方向跳去。这就是模拟退火。
  兔子们吃了失忆药片,并被发射到太空,然后随机落到了地球上的某些地方。他们不知道自己的使命是什么。但是,如果你过几年就杀死一部分海拔低的兔子,多产的兔子们自己就会找到珠穆朗玛峰。这就是遗传算法。
  兔子们知道一个兔的力量是渺小的。他们互相转告着,哪里的山已经找过,并且找过的每一座山他们都留下一只兔子做记号。他们制定了下一步去哪里寻找的策略。这就是禁忌搜索。

2017华为软挑——遗传算法相关推荐

  1. O准备如何苟进复赛圈?华为软挑开挂指南(附赛题预测)

    这篇文章纯粹是心血来潮,原因是去年上传到github的参赛代码,前几天又有两个人star和fork了. 记得star热潮还是去年4月复赛刚结束的那几天,厚着脸皮在八大赛区的群里发消息求关注,之后就陷入 ...

  2. 2022华为软挑比赛(初赛笔记)

    文章目录 2022华为软挑(初赛笔记) 1. 赛题要求 2. 解决方案 2.1 挑选适合的边缘节点 2.2 第一轮:最大分配 2.3 第二轮:均值分配 总结 本文仓库地址: Github-CodeCr ...

  3. 2020华为软挑热身赛代码开源-思路大起底(华为软件精英挑战赛编程闯关)

    本文首发于个人公众号[两猿社],后台回复[华为],获取完整开源代码链接. 昵称:lou_shang_shi_bian_tai 成绩:0.032 社长没有针对硬件做任何优化,热身赛成绩也一般.但有些比赛 ...

  4. 2021华为软挑部分答疑——哪些你有错却总是找不到的地方,我来带你找啦(含标准输入代码)

    前期工作: 2021华为软挑初探--代码实现 2021华为软挑再探--代码实现 1 关于打包 在windows系统下,先把你写的程序写在src里面的CodeCraft-2021里面 然后在这个页面,将 ...

  5. 华为软挑赛2023-复赛笔记

    前言 比赛介绍 参考初赛博客: 华为软挑赛2023-初赛笔记_没有梦想的大白兔的博客-CSDN博客 赛题变动 官网赛题: 华为云论坛_云计算论坛_开发者论坛_技术论坛-华为云 (huaweicloud ...

  6. 2020华为软挑热身赛-这些坑我帮你踩过了(华为软件精英挑战赛编程闯关)

    本文始发于个人公众号[两猿社]. 声明,为保证比赛公平,本文不会提供比赛源码,仅提供思路与踩坑经验. 他来了,他来了,他带着面试绿卡走来了. 他来了,他来了,他带着20w大奖走来了. 一年一度的华为软 ...

  7. 2019华为软挑代码总结-交通规则模拟

    文章目录 前言 三.数据读入后怎么跑呢? 总结 四.怎么看效果? 前言 以前一份代码的阅读,2019华为软挑 三.数据读入后怎么跑呢? 思路: 1.创建地图类,将地图中每个路口到其他路口最短路径保存. ...

  8. 华为软挑赛2023-初赛笔记

    前言 比赛介绍 官方链接: 2023华为软件精英挑战赛--普朗克计划 (huaweicloud.com) 赛题介绍 场景介绍 官方赛题介绍: 2023华为软件精英挑战赛初赛赛题及相关材料发布_2023 ...

  9. 2018华为软挑--模拟退火+FF解决装箱问题【C++代码】

    算法简介: 装箱问题是一个NP完全问题,求解全局最优解有很多种方法:遗传算法.禁忌搜索算法.蚁群算法.模拟退火算法等等,本次使用模拟退火,它的优点是在参数合适的情况下基本上可以100%得到全局最优解, ...

  10. 2022华为软挑编程问题报错总结

    for i in number_feature: TypeError: 'int' object is not iterable的错误 错误原因:是因为在python里,整型(int)数据是不能直接用 ...

最新文章

  1. ISP【三】———— raw读取、不同格式图片差异
  2. mysqlls_mysql基本命令
  3. 关于GitHub推送时发生Permission denied (publickey)的问题
  4. 想拥有最新的微软嵌入式技术 就赶快加入微软嵌入式专家社区吧!
  5. android 自定义图片上传,android自定义ImageView仿图片上传(示例代码)
  6. C++中transform的用法
  7. case when then else 详解
  8. @transactional注解失效情况
  9. Java并发编程之线程定时器ScheduledThreadPoolExecutor解析
  10. JVM 内存监控 (1.8)
  11. 国家邮政局:前10月邮政行业业务收入同比增长17.8%
  12. 喜欢 TypeScript 的人,一点都不比 Python 少
  13. 20 世纪 10 大算法
  14. 深度学习总概论(超神之路)
  15. power iso linux启动盘,怎么用poweriso制作u盘启动盘win10_poweriso制作u盘启动盘win10详细步骤...
  16. Socket 简介及面试题
  17. quot 云计算 quot 是计算机,云计算是什么意思?
  18. Vscode连接远程服务器出现‘Acquiring lock on/home/~’问题
  19. 苹果官方首曝iOS 15!这些经典iPhone都要被弃
  20. 虚拟主机mysql数据库大小,香港虚拟主机的网站数据库空间不够怎么办

热门文章

  1. 单片机c语言 课程设计报告,单片机课程设计心得体会精选
  2. 网络连接正常,IE不能打开网页的全面解决方法
  3. 如何使用FreeTextBox
  4. Windows Server 2003 安装教程——图文小白版(附下载地址)
  5. visio画图复制粘贴到word_visio复制粘贴到word中
  6. VNC viewer双击鼠标出发ctrl+c
  7. 令人失望的智器Smart Q5
  8. java被安全阻止解决及用友Uclient安装
  9. shell命令之 tr
  10. YDOOK:STM32: 最新版选型手册下载 2021