In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)05/31 Report--
This article introduces the relevant knowledge of "how to implement a genetic algorithm in Go language". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
Needless to say, let's start with the code! * examples are similar to what I've done before: find a quadratic minimum.
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 {})}
I immediately defined a set of settings to use in the algorithm I started later.
The GeneticAlgorithmRunner in the second part looks a little strange. GeneticAlgorithmRunner is an interface that asks how to generate the initial population, execute corssovers and mutataions, and sort the answers in order to keep the individuals in the Population so that the next generation will be better. I think this seems strange because "interfaces" are usually used in object-oriented languages and usually require objects to implement certain features and methods. There's no difference here. This little piece of code is actually saying that it is asking for something to define the details of these methods. Here's what I did:
Type QuadraticGA struct {} func (l QuadraticGA) GenerateInitialPopulation (populationSize int) [] interface {} {initialPopulation: = make ([] interface {}, 0, populationSize) for iGenerateInitialPopulation = 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))})}
What's even weirder is that I've never mentioned the interfaces of these methods. Remember, because there are no objects and no inheritance. The QuadraticGA structure is a blank object that is implicitly used as a GeneticAlgorithmRunner. Each required method is bound to the structure in parentheses, like "@ override" in Java. Now, the structure and settings need to be passed to the module running the algorithm.
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)}
It's simple, right? "QuadraticGA {}" simply creates a new instance of the structure, and the rest is done by the Run () method. This method returns search results and any errors that occur because Go does not believe that the authors of another try / catch-- war have taken a strict design position.
Now let's calculate the performance of each item in order to find a new X value by finding the quadratic function of the quadratic function:
Func makeNewEntry () float64 {return highRange * rand.Float64 ()} func calculate (x float64) float64 {return math.Pow (x, 2)-6roomx + 2 / / minimum should be at xtriple 3}
Now that the interface has been created for the secondary implementation, the GA itself needs to complete:
Func Run (geneticAlgoRunner GeneticAlgorithmRunner, settings GeneticAlgorithmSettings) (interface {}, error) {population: = geneticAlgoRunner.GenerateInitialPopulation (settings.PopulationSize) geneticAlgoRunner.Sort (population) bestSoFar: = population [len (population)-1] for isettings GeneticAlgorithmSettings = 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))}}
Instead of having float64s everywhere, you can pass Quad3D entries anywhere; each has an X and a Y value. For each entry created, it is created using contructor makeNewQuadEntry. None of the code in the Run () method has changed.
When it runs, we get this output:
Best: X: 3.891671 y: 4.554884 z:-12.787259
Very close!
Oh, I forgot to say go fast! When you do this in Java, there is a significant wait time even if you use the same settings. Solving the quadratic equation in a relatively small range is not very complicated, but it is noteworthy for a person.
Go is compiled locally, such as C. When binary is executed, it seems to spit out an answer immediately. Here is a simple way to measure the execution time of each run:
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)}
Side note: I can say that I am very happy that we are a community of developers to get rid of past mistakes and build comprehensive time modules and packages into a language? Java 8 + owns them, Python owns them, and owns them. It makes me happy.
The output now is:
Best: X: 3.072833 y:-6.994695 136876 Best: X: 3.891671 y: 4.554884 z:-12.787259 4142778
That "almost instant" feeling is what I want to convey, and now we have a very difficult number. 136876 looks big, but report the time in nanoseconds.
I repeat: nanosecond. Not a few milliseconds, we are all used to the Internet age or other lingua franca like Python and Java; nanoseconds. 1pm 1000000 milliseconds.
This means that we have found the answer to a quadratic equation that uses genetic algorithms to search for answers in less than a millisecond. This sentence, "damn moment" seems appropriate, doesn't it? This includes printing to the terminal.
So, what about calculating more intensive things? Before I showed a way to find a good fantasy football lineups, I used it on Fanduel. This includes reading data from spreadsheets, making and filtering lineups, and making more complex crossovers and mutations. It may take more than 75, 000 years to force the search for a * solution (at least using the Python I was using at the time).
I don't need to check all the details, you can look at the code yourself, but I'll show the output here:
Best: 121.409960 RB, $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.7778K: Phil Dawson-7.444416010182 this is the content of "how to implement a genetic algorithm in Go language". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.