Go语言如何实现遗传算法

遗传算法的简单实现(Go语言) 这是我自己写的一篇有关遗传算法的文章。 摘 要 很多计算机科学算法往往都受自然界和生物的启发,遗传算法就是受到了生物的自然选择过程而产生的一个启发式算法,启发式算法会尝试通过一些假设来更快地解决问题,因此启发式算法并不是最优的。即便如此,在很多领域都有遗传算法的应用。因此,本文就使用Go语言来实现简单的遗传算法。 关键词  遗传算法  Go语言 Abstract Selection by bi... 阅读详情

原文:Go with Genetic Algorithms
作者:5280incode
翻译:Diwei

出于好玩的心态,我决定学习一下Go语言。我认为学习新语言最好的方法就是深入学习,并且尽可能多犯错误。这样做虽然可能会很慢,但是可以确保在后面的过程中再也不会出现编译的错误。

Go语言与我习惯的其他语言不同。Go更喜欢自己单独实现,而其他像Java这类语言更喜欢继承。其实在Go语言里面根本没有继承这种概念,因为它压根就没有对象这一说法。比如说C语言,它有结构体,但是没有类。但是这样它还是可以有像“构造者”这样的常见思想和设计模式(一种在这种情况下有序地产生结构体的方式)。

Go语言坚决拥护组合(composition),同时也很反对继承的做法,在网络上引起了强烈的讨论,同时也让人们重新思考了语言该往哪个方向发展。所以,从这个角度来看,Go语言与其它语言的差别可能也没有那么大。

本文将重点介绍如何用Go语言实现遗传算法。如果你还没有参加过GoLang Tour,我还建议你快速看一下这门语言的介绍。

话不多说,让我们开始从代码说起吧!第一个例子与我以前做过的很类似:找到一个二次的最小值。

type GeneticAlgorithmSettings struct {
  PopulationSize int
  MutationRate int
  CrossoverRate int
  NumGenerations int
  KeepBestAcrossPopulation bool
}

type GeneticAlgorithmRunner interface {
  GenerateInitialPopulation(populationSize int) []interface{}
  PerformCrossover(individual1, individual2 interface{}, mutationRate int) interface{}
  PerformMutation(individual interface{}) interface{}
  Sort([]interface{})
}

我立马定义了一组设置,以便在稍后启动的算法中用到。

第二部分的GeneticAlgorithmRunner这个看起来有点奇怪。GeneticAlgorithmRunner是一个接口,询问如何生成初始种群,执行corssovers和mutataions,并对答案进行排序,以便在Population中保持最好的个体,这样下一代才会更加优秀。我认为这看起来很奇怪,因为“接口”通常用于面向对象的语言,通常会要求对象实现某些特性和方法。这里没有什么差别。这一小段代码实际上是在说,它正在请求一些东西来定义这些方法的细节。我是这样做的:

type QuadraticGA struct {}

func (l QuadraticGA) GenerateInitialPopulation(populationSize int) []interface{}{
  initialPopulation := make([]interface{}, 0, populationSize)
  for i:= 0; i < populationSize; i++ {
    initialPopulation = append(initialPopulation, makeNewEntry())
  }
  return initialPopulation
}

func (l QuadraticGA) PerformCrossover(result1, result2 interface{}, _ int) interface{}{
  return (result1.(float64) + result2.(float64)) / 2
}

func (l QuadraticGA) PerformMutation(_ interface{}, _ int) interface{}{
  return makeNewEntry()
}

func (l QuadraticGA) Sort(population []interface{}){
  sort.Slice(population, func(i, j int) bool {
    return calculate(population[i].(float64)) > calculate(population[j].(float64))
  })
}

更奇怪的是,我从来没有提到过这些方法的接口。请记住,因为没有对象,也没有继承。QuadraticGA结构体是一个空白对象,隐式地作为GeneticAlgorithmRunner。每个必需的方法都在括号中绑定到该结构体,就像Java中的“@ override”。现在,结构体和设置需要传递给运行该算法的模块。

settings := ga.GeneticAlgorithmSettings{
   PopulationSize: 5,
   MutationRate: 10,
   CrossoverRate: 100,
   NumGenerations: 20,
   KeepBestAcrossPopulation: true,
}

best, err := ga.Run(QuadraticGA{}, settings)

if err != nil {
   println(err)
}else{
   fmt.Printf("Best: x: %f  y: %f\n", best, calculate(best.(float64)))
}

很简单,对吧?“QuadraticGA {}”只是简单地创建了该结构的一个新实例,其余的则由Run()方法完成。该方法返回搜索结果和发生的任何错误,因为Go不相信try / catch——另一场战争作者采取了严格的设计立场。

现在来计算每个项的性能,以求二次函数求出的二次函数来求出一个新的X值的方法:

func makeNewEntry() float64 {
   return highRange * rand.Float64()
}

func calculate(x float64) float64 {
   return  math.Pow(x, 2) - 6*x + 2 // minimum should be at x=3
}

既然已经为二次实现创建了接口,那么GA本身需要完成:

func Run(geneticAlgoRunner GeneticAlgorithmRunner, settings GeneticAlgorithmSettings) (interface{}, error){

   population := geneticAlgoRunner.GenerateInitialPopulation(settings.PopulationSize)

   geneticAlgoRunner.Sort(population)

   bestSoFar := population[len(population) - 1]

   for i:= 0; i < settings.NumGenerations; i++ {

      newPopulation := make([]interface{}, 0, settings.PopulationSize)

      if settings.KeepBestAcrossPopulation {
         newPopulation = append(newPopulation, bestSoFar)
      }

      // perform crossovers with random selection
      probabilisticListOfPerformers := createStochasticProbableListOfIndividuals(population)

      newPopIndex := 0
      if settings.KeepBestAcrossPopulation{
         newPopIndex = 1
      }
      for ; newPopIndex < settings.PopulationSize; newPopIndex++ {
         indexSelection1 := rand.Int() % len(probabilisticListOfPerformers)
         indexSelection2 := rand.Int() % len(probabilisticListOfPerformers)

         // crossover
         newIndividual := geneticAlgoRunner.PerformCrossover(
            probabilisticListOfPerformers[indexSelection1],
            probabilisticListOfPerformers[indexSelection2], settings.CrossoverRate)

         // mutate
         if rand.Intn(101) < settings.MutationRate {
            newIndividual = geneticAlgoRunner.PerformMutation(newIndividual)
         }

         newPopulation = append(newPopulation, newIndividual)
      }

      population = newPopulation

      // sort by performance
      geneticAlgoRunner.Sort(population)

      // keep the best so far
      bestSoFar = population[len(population) - 1]

   }

   return bestSoFar, nil
}

func createStochasticProbableListOfIndividuals(population []interface{}) []interface{} {

   totalCount, populationLength:= 0, len(population)
   for j:= 0; j < populationLength; j++ {
      totalCount += j
   }

   probableIndividuals := make([]interface{}, 0, totalCount)
   for index, individual := range population {
      for i:= 0; i < index; i++{
         probableIndividuals = append(probableIndividuals, individual)
      }
   }

   return probableIndividuals
}

很像以前,一个新的人口被创造出来,人口的成员将会世代交配,而他们的后代可能携带突变。一个人的表现越好,就越有可能交配。随着时间的推移,算法收敛到最好的答案,或者至少是一个相当不错的答案。

那么当它运行时,它返回了什么呢?

Best: x: 3.072833 y: -6.994695

不坏!由于人口规模只有5、20代,而且输入的范围被限制在[0 100],这一搜索就钉在了顶点上。

现在,您可能想知道为什么我定义了所有的接口方法来返回“接口{}”。这就像Go和generics一样。没有对象,因此没有对象类型返回,但是没有描述的大小的数据仍然可以在堆栈上传递。这本质上也是这个返回类型的含义:它传递一些已知的和类似的类型的对象。有了这个“泛型”,我就可以将GA移动到它自己的包中,并将相同的代码移到多个不同类型的数据上。

我们有两个输入的3D二次方程,而不是一个二维二次方程的单个输入。接口方法只需要很小的改变:

type Quad3D struct {
   x, y float64
}
func makeNewQuadEntry(newX, newY float64) Quad3D {
   return Quad3D{
      x: newX,
      y: newY,
   }
}

func calculate3D(entry Quad3D) float64 {
   return math.Pow(entry.x, 2)- 6 * entry.x + math.Pow(entry.y, 2)- 6 * entry.y + 2
}

type Quadratic3dGA struct {
}

func (l Quadratic3dGA) GenerateInitialPopulation(populationSize int)[]interface{}{

   initialPopulation := make([]interface{}, 0, populationSize)
   for i:= 0; i < populationSize; i++ { initialPopulation = append(initialPopulation, makeNewQuadEntry(makeNewEntry(), makeNewEntry())) } return initialPopulation } func (l Quadratic3dGA) PerformCrossover(result1, result2 interface{}, mutationRate int) interface{}{ r1Entry, r2Entry := result1.(Quad3D), result2.(Quad3D) return makeNewQuadEntry((r1Entry.x + r2Entry.x) / 2, (r1Entry.y + r2Entry.y) / 2,) } func (l Quadratic3dGA) PerformMutation(_ interface{}) interface{}{ return makeNewQuadEntry(makeNewEntry(), makeNewEntry()) } func (l Quadratic3dGA) Sort(population []interface{}){ sort.Slice(population, func(i, j int) bool { return calculate3D(population[i].(Quad3D)) > calculate3D(population[j].(Quad3D))
   })
}

func quadratic3dMain(){
   settings := ga.GeneticAlgorithmSettings{
      PopulationSize: 25,
      MutationRate: 10,
      CrossoverRate: 100,
      NumGenerations: 20,
      KeepBestAcrossPopulation: true,
   }

   best, err := ga.Run(Quadratic3dGA{}, settings)
   entry := best.(Quad3D)

   if err != nil {
      println(err)
   }else{
      fmt.Printf("Best: x: %f  y: %f  z: %f\n", entry.x, entry.y, calculate3D(entry))
   }
}

而不是到处都是float64s,任何地方都可以通过Quad3D的条目;每一个都有一个X和一个Y值。对于创建的每个条目,都使用contructor makeNewQuadEntry创建。Run()方法中的代码都没有更改。

当它运行时,我们得到这个输出:

Best: x: 3.891671 y: 4.554884 z: -12.787259

很接近了!

哦,我忘了说走快了!在Java中执行此操作时,即使使用相同的设置,也会有明显的等待时间。在一个相对较小的范围内求解二次方程并不是很复杂,但它对一个人来说是值得注意的。

Go是本地编译的,比如C。当二进制执行时,它似乎马上就吐出一个答案。这里有一个简单的方法来度量每次运行的执行时间:

func main() {
   beforeQuadTime := time.Now()
   quadraticMain()
   afterQuadTime := time.Since(beforeQuadTime)
   fmt.Printf("%d\n", afterQuadTime)

   before3dQuadTime := time.Now()
   quadratic3dMain()
   after3dQuatTime := time.Since(before3dQuadTime)
   fmt.Printf("%d\n", after3dQuatTime)
}

边注:我能说我很高兴我们是一个开发者社区,让他们从过去的错误中走出来,并把综合的时间模块和包构建成一种语言吗?Java 8 +拥有它们,Python拥有它们,并拥有它们。这使我开心。

现在的输出:

Best: x: 3.072833 y: -6.994695
136,876
Best: x: 3.891671 y: 4.554884 z: -12.787259
4,142,778

那“近乎瞬间”的感觉是我想要传达的,现在我们有了很难的数字。136,876看起来很大,但要在纳秒内报告时间。

重申一遍:纳秒。不是几毫秒,我们都习惯了在互联网时代或者其他像Python和Java这样的通用语言;纳秒。1/1,000,000毫秒。

这意味着我们在不到一毫秒的时间里找到了一个使用遗传算法来搜索答案的二次方程的答案。这句话,“该死的瞬间”似乎很合适,不是吗?这包括打印到终端。

那么,要计算更密集的东西呢?在我展示一种寻找好的梦幻足球lineups的方法之前,我在Fanduel上使用。这包括从电子表格中读取数据,制作和过滤lineups,并进行更复杂的交叉和突变。强制寻找最佳解决方案可能需要超过75,000年(至少使用我当时使用的Python)。

我不需要再检查所有的细节,你可以自己去看代码,但我会在这里显示输出:

Best: 121.409960:, $58100
QB: Aaron Rodgers - 23.777778
RB: Latavius Murray - 15.228571
RB: DeMarco Murray - 19.980000
WR: Kelvin Benjamin - 11.800000
WR: Stefon Diggs - 14.312500
WR: Alshon Jeffery - 9.888889
TE: Connor Hamlett - 8.200000
D: Philadelphia Eagles - 10.777778
K: Phil Dawson - 7.444444
16,010,182

哦,是的!现在看来这是一个很好的阵容!它只需要16毫秒就能找到。

现在,这个遗传算法可以改进了。与C一样,当将对象传递给方法时,将在堆栈上复制对象(读取数据)。随着对象大小的增长,最好不要反复复制它们,而是要在堆中创建它们,并在周围传递指针。目前,我将把它作为未来的工作。

Go也被用coroutines和信道的原生支持编写,利用多个内核来解决一个问题,比过去简单多了,相比于单核时代的其他语言来说,这是一个巨大的优势。我想要增强这个算法来使用这些工具,但这也必须留给以后的工作。

我很享受学习的过程。对于我来说,用组合而不是继承来考虑工程解决方案是很困难的,因为我已经习惯了8年以上的时间,也是我学会编程的方式。但是每种语言和方式都有各自的优点和缺点;每一种语言在我的工具中都是不同的工具。对于任何担心尝试的人,不要。有一个驼峰(更像是一个减速带),但你很快就会克服它,走上成功之路。

还有一些我喜欢的东西,我喜欢其他语言,主要是一组基本的函数方法来操作数据。我需要一个lambda函数和方法来映射、减少和筛选数据的数组或部分。设计人员反对功能实现的理由是,代码应该总是简单、易于阅读和编写,并且这与for循环是可实现的。我认为,映射、过滤和减少通常更容易读和写,但这是一场已经在肆虐的战争中的争论。

尽管我与一些开发人员的观点存在分歧,以及我必须考虑解决问题的不同方式,但Go真的是一种很好的语言。我鼓励大家在学习一两门语言后再试一试。它很快就成为了最流行的语言之一,有很多原因可以解释为什么。我期待着在未来更多地使用它。

go语言:实现遗传算法(附完整源码) 阅读详情

相关推荐

go:实现遗传(Genetic Algorithm)算法(附完整源码)

go:实现遗传(Genetic Algorithm)算法(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 250

遗传算法(GA)求解TSP问题(golang实现)

本人水平有效,文中可能存在一些错误个人觉得标准的遗传算法求解TSP/VRP问题的效率一般,而且编码过程非常痛苦,尤其是交叉的部分,因为TSP要求基因点是不能重复的,即不能重复访问同一个点,因此交叉的过程需要考虑消除重复的节点。自适应大邻域算法(ALNS)求解TSP问题,golang实现效率还不错。

huyiqiuuuu的博客 1695

go:实现遗传算法(附完整源码)

go:实现遗传算法(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 160

Golang 遗传算法库 Goga 常见问题解决方案

Golang 遗传算法库 Goga 常见问题解决方案 1. 项目基础介绍和主要编程语言 Goga 是一个使用 Golang 编写的遗传算法库。遗传算法是一种模拟自然选择过程的搜索算法,用于解决优化和搜索问题。Goga 通过注入不同的行为到主遗传算法对象中来实现其功能,主要包括模拟器(Simulator)、选择器(Selector)和配对器(Mater)三个核心组件。用户可以通过自定义这些组件来适应...

gitblog_00986的博客 382

GeneticSudoku:用 GoLang 编写的基本遗传算法来解决数独难题!

遗传数独 遗传算法是一种试图模仿自然选择过程以解决问题的算法。 就像自然界一样,每个解决方案都由几个基因组成。 这些基因相互交配,并在持续的进化过程中发生突变,直到找到解决方案。 该算法首先填充给定板的随机解决方案的“总体”。 从那里开始,这些解决方案中的每一个都根据一组标准进行评分,例如放置的瓷砖数量以及每个解决方案中完成的行、列和框的数量。 计算出等级后,算法使用这些等级将两个解决方案“配对”在一起。 交配过程的结果将具有来自每个亲本的一些基因。 具有较高等级的基因更有可能被选择进行交配。 最后,新种群受到随机突变的影响,这将很少改变每个解决方案中的随机基因。 为了解决数独谜题,我选择将棋盘中的每一行表示为 {1, 2, 3, 4, 5, 6, 7, 8, 9} 的排列,注意保留每一行中的给定值排。 交叉将导致两块板相互交换一排。 突变将导致棋盘内的随机行被设置为另一个排列。 结果

用Go / golang编写的遗传算法库-Golang开发

描述用于Go / Golang的遗传算法Install $ go install git://github.com/thoj/go-galib.git编译示例:$ git clone git://github.com/thoj/go-galib.git $ cd go -galib $ cd示例$ go建立浮动描述遗传算法,用于Go / Golang安装$ go install git://github.com/thoj/go-galib.git编译示例:$ git clone git://github.com/thoj /go-galib.git $ cd go-galib $ cd示例$ go build float.go $ ./floating使用请参见example / *

遗传算法:go语言实现

tips:写这个遗传算法的另一个目的是为了练习go语言,如有缺陷,请不吝赐教。下面算法用来求解函数y=x^7+x^6-100x^5+200x^4-300x^3-13579x^2-2468x+123456789函数再(-8,8)上的最小值对于该问题解空间是非常明确的。对于结果偏差小于0.001则视为未改变。为了方便采用二进制编码我们将解空间换算为(8-(-8))*1000=1600因此每条染色体的基因

godwei_ding的博客 1123

Go-goga-Golang的遗传算法库

goga - Golang的遗传算法库

改进遗传算法求解数独谜题模型详解(Go语言)

使用Go语言实现遗传算法求解数独谜题详解。代码已开源

qq_22328011的博客 1245

用 Go / golang 编写的遗传算法库_go语言_代码_下载

描述 Go/Golang 的遗传算法

Go语言实现遗传算法的实例代码

Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文将重点介绍如何用Go语言实现遗传算法。如果你还没有参加过GoLang Tour,我还建议你快速看一下这门语言的介绍

测试2

出于好玩的心态,我决定学习一下Go语言。我认为学习新语言最好的方法就是深入学习,并且尽可能多犯错误。这样做虽然可能会很慢,但是可以确保在后面的过程中再也不会出现编译的错误。 Go语言与我习惯的其他语言不同。Go更喜欢自己单独实现,而其他像Java这类语言更喜欢继承。其实在Go语言里面根本没有继承这种概念,因为它压根就没有对象这一说法。比如说C语言,它有...

weixin_34390996的博客 74

ns3-gym入门(二):linear-mesh例子详解

学习ns3-gym中关于IEEE 802.11网状网络中控制随机接入的例子linear-mesh

zoe2222226666的博客 1845

Go和Async/Await | Gopher Daily (2021.12.30) ʕ◔ϖ◔ʔ

每日一谚:Struct embedding of interfaces to satisfy them fast.Go技术生态用于IDAPro的Go二进制解析器 - https://sec...

TonyBai 348

动力锂电池-二阶RC模型等效电路-RLS参数辨识-Simulink

1.锂电池二阶RC等效电路模型

QQ1978009424的博客 1万+

CANoe/CANalyzer新手必看:从零配置到报文分析的完整流程(附B站官方教程实操)

本文为CANoe/CANalyzer新手提供从零配置到报文分析的完整流程指南,涵盖硬件连接、工程配置、报文捕获及信号级分析等关键步骤。通过详细的实操案例和B站官方教程,帮助工程师快速掌握汽车电子总线分析工具的核心应用,提升诊断效率与通信系统理解。

y7z8a的博客 776

探索未来游乐园的创意引擎:基于遗传算法的过山车设计工具

探索未来游乐园的创意引擎:基于遗传算法的过山车设计工具 在追求极致游乐体验的数字世界中,一项令人兴奋的开源项目横空出世——这便是致力于通过遗传算法生成惊艳视觉的过山车设计工具。想象一下,让计算机学习并进化,创造出比肩甚至超越人类设计师的作品,这便是该项目的核心所在。 技术深潜:揭秘核心组件 此项目立足于Go语言,构建了一套完整的生态系统,涵盖了游戏内轨道元数据的解析、轨道生成与序列化、向量数学处...

gitblog_00752的博客 721

图形学基础知识:着色(三)Rendering Pipeline与Shader

管线(Pipeline) 什么是管线? 管线我们可以理解为流水线,流水线大家都懂,就是工厂里面生产东西的一整套流程。例如饮料厂要生产饮料,需要洗瓶子,倒饮料,加盖,包装等等,这一整套过程就是流水线。那么我们要把一个三维场景渲染成一幅二维的图像,同样需要一个先干嘛再干嘛然后干嘛最后干嘛的过程,这个过程或流水线,我们就称之为管线。 实时渲染管线(Real-time Rendering Pipeline) 通过前面几篇的学习,我们知道了要把一个三维场景渲染成一幅二维的图像大致分为以下几个步骤: M.

王王王渣渣的博客 2533

python算法设计思想_提到算法就头大,12个算法设计项目给你练练脑

1. C 语言程序设计实验地址: http s:// www.shiyanlou.com/courses/1649学习人数: 308关注人数: 12本课程讲解 C 语言的开发环境以及对 C 语言的剖析,引入大量的 C 语言程序案例,把算法和语法结合起来,通过引导大家由浅入深地编写 C 程序,让大家掌握 C 语言。我们将从中学会 C 语言语法、数组、模块化程序设计指针、文件的输入与输出等。2. Go...

weixin_35829704的博客 292

关于 SAP UI5 应用 url 中的特殊符号 # 的作用介绍

符号在 SAP UI5 的 URL 中不仅仅是一个分隔符,更是动态路由、状态管理和深链接的关键所在。通过合理使用,开发者可以实现高效、流畅的用户体验,同时保持系统的可维护性和扩展性。通过 SAP UI5 提供的灵活路由机制,企业应用可以轻松实现复杂导航需求,同时为用户提供直观的交互体验。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 415

《S32系列芯片——Boot & HSE详解》持续更新中...

专栏总目录,将详细介绍S32系列芯片的Boot功能,最终给出HSE基于IVT的Secure Boot方案!!!

车载系统攻城狮 4389
上一篇: 如何用C++实现自己的Tensorflow
下一篇: 如何保障微服务安全
csdn研发技术
博客等级 码龄9年 1381粉丝 90原创
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值