遗传算法

遗传算法

前言

  • 之前给小灯神做文本分析的时候,去图书馆借了两本书,一本是《数据之魅:基于开源工具的数据分析》,这本书适合学统计的相关人士学习,现在放在书架上吃灰中,另外一本是《集体智慧编程》,挺有意思的,没有讲很复杂的算法理论,用 Python 把机器学习相关的内容明明白白地、清清楚楚地写清楚了,其中在优化这一章就讲到了遗传算法,与其他章节一样,我也是把源码跑了一下就翻页了。

  • 最近有个同学找我帮忙,应该是课程设计作业之类的,题目如下:

    2017125-deng
  • 要求:

    1. 思考一种算法或规则,求解出灯布局的优化方案。指出算法过程步骤或规则,可以用示意性的代码或流程图表示;
    2. 基于你提出的算法,找到一种新的布局,使得照度均匀度达到最优,请参考上述MATLAB程序作修改并生成三维图形,判断均匀度效果,尝试画出灯的布局图。
  • 这个要求是房间内任何一个点的光照强度尽量平均,越平均越好,光照强度与光源距离成反比。

  • 灯管24个,相当于24个元素共同完成目标,每个元素是二维的(高度固定为3米),有横坐标和纵坐标。

  • 成本函数可以简单用光照强度的方差来评估,某个 solution 的方差越小,那么其成本函数也越小,即越优。

  • 看上去挺简单的,我认为遗传算法是个不错的选择,特别是算法中的变异和交叉操作,每次迭代都能向全局最优解靠近。

算法介绍

  • 想看完整的算法详解去其他博客看就好了,肯定比我写得好,我就偷懒一会,摘抄一些介绍。

百度百科

  • 遗传算法(Genetic Algorithm)是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。

  • 遗传算法是从代表问题可能潜在的解集的一个种群(population)开始的,而一个种群则由经过基因(gene)编码的一定数目的个体(individual)组成。每个个体实际上是染色体(chromosome)带有特征的实体。

  • 染色体作为遗传物质的主要载体,即多个基因的集合,其内部表现(即基因型)是某种基因组合,它决定了个体的形状的外部表现,如黑头发的特征是由染色体中控制这一特征的某种基因组合决定的。

  • 因此,在一开始需要实现从表现型到基因型的映射即编码工作。由于仿照基因编码的工作很复杂,我们往往进行简化,如二进制编码,初代种群产生之后,按照适者生存和优胜劣汰的原理,逐代(generation)演化产生出越来越好的近似解,在每一代,根据问题域中个体的适应度(fitness)大小选择(selection)个体,并借助于自然遗传学的遗传算子(genetic operators)进行组合交叉(crossover)和变异(mutation),产生出代表新的解集的种群。这个过程将导致种群像自然进化一样的后生代种群比前代更加适应于环境,末代种群中的最优个体经过解码(decoding),可以作为问题近似最优解。

《集体智慧编程》

  • 截图

    2017125-ga
    2017125-ga2
    2017125-ga3
  • 书上的源码:

      def geneticoptimize(domain, costf, popsize=50, step=1,
                          mutprob=0.2, elite=0.2, maxiter=100):
          # Mutation Operation
          def mutate(vec):
              i = random.randint (0, len (domain) - 1)
              if random.random () < 0.5 and vec[i] > domain[i][0]:
                  return vec[0:i] + [vec[i] - step] + vec[i + 1:]
              elif vec[i] < domain[i][1]:
                  return vec[0:i] + [vec[i] + step] + vec[i + 1:]
    
          # Crossover Operation
          def crossover(r1, r2):
              i = random.randint (1, len (domain) - 2)
              return r1[0:i] + r2[i:]
    
          # Build the initial population
          pop = []
          for i in range (popsize):
              vec = [random.randint (domain[i][0], domain[i][1])
                  for i in range (len (domain))]
              pop.append (vec)
    
          # How many winners from each generation?
          topelite = int (elite * popsize)
    
          # Main loop
          for i in range (maxiter):
              scores = [(costf (v), v) for v in pop]
              scores.sort ()
              ranked = [v for (s, v) in scores]
    
              # Start with the pure winners
              pop = ranked[0:topelite]
    
              # Add mutated and bred forms of the winners
              while len (pop) < popsize:
                  if random.random () < mutprob:
    
                      # Mutation
                      c = random.randint (0, topelite)
                      pop.append (mutate (ranked[c]))
                  else:
    
                      # Crossover
                      c1 = random.randint (0, topelite)
                      c2 = random.randint (0, topelite)
                      pop.append (crossover (ranked[c1], ranked[c2]))
    
              # Print current best score
              print scores[0][0]
    
          return scores[0][1]
    

实践

  • 我并没有直接用 Python 的源码解决问题,学弟的课程应该都是用 Matlab 仿真的,所以我就用忘光了的 Matlab 来练练手。

  • 代码移植的坑就不必说了,移植的过程中我才体会到 Python 是多么的平易近人!!!

  • Life is short, I use Python.

  • 除开代码移植花了挺久时间以外,还有一个很重要的点:源码只解决了一维的问题(一个列表/数组),而灯管问题是二维的,所以有几处地方要修改一下。

    • 首先在初始化种群时,Python 利用列表生成式可以很方便的操作。

          # Build the initial population
          pop = []
          for i in range (popsize):
              vec = [random.randint (domain[i][0], domain[i][1])
                  for i in range (len (domain))]
              pop.append (vec)
      
    • 在 Matlab 中就要麻烦一点,因为是二维的,所以 sol 是 2x24 的矩阵,每列代表一个灯管的坐标,第一行是灯管的y轴坐标(0到12),第二行是灯管的x轴坐标(0到17.5),共有24个灯管,为了代码的可移植性,我用变量 number 表示24。Python 列表的 append 的操作在 Matlab 中可以用 x = [x, y] 代替,但是 Matlab 本质上还是处理矩阵,所以这里的 x y 都是矩阵,以后访问某个序号时还得计算一番,略微麻烦,但没办法,我只想到这个法子。

          pop=[];
          cost=[];
          for i=1:popsize
              sol=unifrnd(0,1,2,number);
              sol(1,:) = sol(1,:).*12;
              sol(2,:) = sol(2,:).*17.5;
              cost = [cost, costf(sol,xx,yy,m,n,number)];
              pop = [pop, sol];
          end
      
    • Python 中关于种群排序,不得不说列表生成式是真的简单好用。

              scores = [(costf (v), v) for v in pop]
              scores.sort ()
              ranked = [v for (s, v) in scores]
      
    • Matlab 中关于种群排序,这里我就没有用到 scores 变量了,只有 ranked 才是后面需要的,可以看到,这里在 pop 中访问某个 sol 时挺麻烦的,比如访问第一个 sol,得访问第1列到第24列,所以别忘记把临时变量替换进去,我最开始写代码时就忘了写,导致 Matlab 报错:索引维度不一致。

              [ranked_cost,index_array] = sort(cost); 
              ranked = [];
              for r = 1:length(index_array)
                  ranked = [ranked, pop(1:2, (number*(index_array(r)-1)+1):number*index_array(r))];
              end
      
    • Python 中关于变异(mutate)的函数:

          # Mutation Operation
          def mutate(vec):
              i = random.randint (0, len (domain) - 1)
              if random.random () < 0.5 and vec[i] > domain[i][0]:
                  return vec[0:i] + [vec[i] - step] + vec[i + 1:]
              elif vec[i] < domain[i][1]:
                  return vec[0:i] + [vec[i] + step] + vec[i + 1:]
      
    • Matlab 中关于变异(mutate)的函数:

          % vec is a 2-d matrix, number is the length of the solution(number of lights)
          function vec=mutate(vec,number)
              i = floor(rand*(number-1)+0.5)+1;
              flag = rand;
              if flag<0.25 & vec(1,i)>1 & vec(2,i)>1
                  vec = [vec(1:2,1:i-1),[vec(1,i)-1,vec(2,i)-1]',vec(1:2,i+1:length(vec))];
              elseif flag<0.5 & vec(1,i)>1 & vec(2,i)<16.5
                  vec = [vec(1:2,1:i-1),[vec(1,i)-1,vec(2,i)+1]',vec(1:2,i+1:length(vec))];
              elseif flag<0.75 & vec(1,i)<11 & vec(2,i)>1
                  vec = [vec(1:2,1:i-1),[vec(1,i)+1,vec(2,i)-1]',vec(1:2,i+1:length(vec))];
              elseif vec(1,i)<11 & vec(2,i)<16.5
                  vec = [vec(1:2,1:i-1),[vec(1,i)+1,vec(2,i)+1]',vec(1:2,i+1:length(vec))];
              else
              end
          end
      

结果

  • 输出

    2017125-result
    2017125-last
    2017125-mesh
  • 在代码中可以自己设置 maxiter 最大迭代次数,本篇的遗传算法是比较简单的,如果初始化种群比较接近全局最优解的话,那么最后的结果也会非常好看。

Python 源码地址:https://github.com/edisonleolhl/Programming-Collective-Intelligence-Source-Code/blob/master/%E7%AC%AC5%E7%AB%A0%20%E4%BC%98%E5%8C%96/optimization.py

Matlab 源码地址:https://github.com/edisonleolhl/DataStructure-Algorithm/blob/master/Optimization/light_24_improve.m

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342