自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go語言如何實(shí)現(xiàn)遺傳算法

開發(fā) 后端 大數(shù)據(jù) 算法
本文將重點(diǎn)介紹如何用Go語言實(shí)現(xiàn)遺傳算法。如果你還沒有參加過GoLang Tour,我還建議你快速看一下這門語言的介紹。話不多說,讓我們開始從代碼說起吧!

出于好玩的心態(tài),我決定學(xué)習(xí)一下Go語言。我認(rèn)為學(xué)習(xí)新語言***的方法就是深入學(xué)習(xí),并且盡可能多犯錯(cuò)誤。這樣做雖然可能會(huì)很慢,但是可以確保在后面的過程中再也不會(huì)出現(xiàn)編譯的錯(cuò)誤。

Go語言與我習(xí)慣的其他語言不同。Go更喜歡自己單獨(dú)實(shí)現(xiàn),而其他像Java這類語言更喜歡繼承。其實(shí)在Go語言里面根本沒有繼承這種概念,因?yàn)樗鼔焊蜎]有對(duì)象這一說法。比如說C語言,它有結(jié)構(gòu)體,但是沒有類。但是這樣它還是可以有像“構(gòu)造者”這樣的常見思想和設(shè)計(jì)模式(一種在這種情況下有序地產(chǎn)生結(jié)構(gòu)體的方式)。

Go語言如何實(shí)現(xiàn)遺傳算法

Go語言堅(jiān)決擁護(hù)組合(composition),同時(shí)也很反對(duì)繼承的做法,在網(wǎng)絡(luò)上引起了強(qiáng)烈的討論,同時(shí)也讓人們重新思考了語言該往哪個(gè)方向發(fā)展。所以,從這個(gè)角度來看,Go語言與其它語言的差別可能也沒有那么大。

本文將重點(diǎn)介紹如何用Go語言實(shí)現(xiàn)遺傳算法。如果你還沒有參加過GoLang Tour,我還建議你快速看一下這門語言的介紹。

話不多說,讓我們開始從代碼說起吧!***個(gè)例子與我以前做過的很類似:找到一個(gè)二次的最小值。

 

  1. type GeneticAlgorithmSettings struct { 
  2.   PopulationSize int 
  3.   MutationRate int 
  4.   CrossoverRate int 
  5.   NumGenerations int 
  6.   KeepBestAcrossPopulation bool 
  7.  
  8. type GeneticAlgorithmRunner interface { 
  9.   GenerateInitialPopulation(populationSize int) []interface{} 
  10.   PerformCrossover(individual1, individual2 interface{}, mutationRate int) interface{} 
  11.   PerformMutation(individual interface{}) interface{} 
  12.   Sort([]interface{}) 

我立馬定義了一組設(shè)置,以便在稍后啟動(dòng)的算法中用到。

第二部分的GeneticAlgorithmRunner這個(gè)看起來有點(diǎn)奇怪。GeneticAlgorithmRunner是一個(gè)接口,詢問如何生成初始種群,執(zhí)行corssovers和mutataions,并對(duì)答案進(jìn)行排序,以便在Population中保持***的個(gè)體,這樣下一代才會(huì)更加優(yōu)秀。我認(rèn)為這看起來很奇怪,因?yàn)?ldquo;接口”通常用于面向?qū)ο蟮恼Z言,通常會(huì)要求對(duì)象實(shí)現(xiàn)某些特性和方法。這里沒有什么差別。這一小段代碼實(shí)際上是在說,它正在請(qǐng)求一些東西來定義這些方法的細(xì)節(jié)。我是這樣做的:

 

  1. type QuadraticGA struct {} 
  2.  
  3. func (l QuadraticGA) GenerateInitialPopulation(populationSize int) []interface{}{ 
  4.   initialPopulation := make([]interface{}, 0, populationSize) 
  5.   for i:= 0; i < populationSize; i++ { 
  6.     initialPopulation = append(initialPopulation, makeNewEntry()) 
  7.   } 
  8.   return initialPopulation 
  9.  
  10. func (l QuadraticGA) PerformCrossover(result1, result2 interface{}, _ int) interface{}{ 
  11.   return (result1.(float64) + result2.(float64)) / 2 
  12.  
  13. func (l QuadraticGA) PerformMutation(_ interface{}, _ int) interface{}{ 
  14.   return makeNewEntry() 
  15.  
  16. func (l QuadraticGA) Sort(population []interface{}){ 
  17.   sort.Slice(population, func(i, j int) bool { 
  18.     return calculate(population[i].(float64)) > calculate(population[j].(float64)) 
  19.   }) 

更奇怪的是,我從來沒有提到過這些方法的接口。請(qǐng)記住,因?yàn)闆]有對(duì)象,也沒有繼承。QuadraticGA結(jié)構(gòu)體是一個(gè)空白對(duì)象,隱式地作為GeneticAlgorithmRunner。每個(gè)必需的方法都在括號(hào)中綁定到該結(jié)構(gòu)體,就像Java中的“@ override”?,F(xiàn)在,結(jié)構(gòu)體和設(shè)置需要傳遞給運(yùn)行該算法的模塊。

 

  1. settings := ga.GeneticAlgorithmSettings{ 
  2.    PopulationSize: 5, 
  3.    MutationRate: 10, 
  4.    CrossoverRate: 100, 
  5.    NumGenerations: 20, 
  6.    KeepBestAcrossPopulation: true
  7.  
  8. best, err := ga.Run(QuadraticGA{}, settings) 
  9.  
  10. if err != nil { 
  11.    println(err) 
  12. }else
  13.    fmt.Printf("Best: x: %f  y: %f\n", best, calculate(best.(float64))) 

很簡單,對(duì)吧?“QuadraticGA {}”只是簡單地創(chuàng)建了該結(jié)構(gòu)的一個(gè)新實(shí)例,其余的則由Run()方法完成。該方法返回搜索結(jié)果和發(fā)生的任何錯(cuò)誤,因?yàn)镚o不相信try / catch——另一場戰(zhàn)爭作者采取了嚴(yán)格的設(shè)計(jì)立場。

現(xiàn)在來計(jì)算每個(gè)項(xiàng)的性能,以求二次函數(shù)求出的二次函數(shù)來求出一個(gè)新的X值的方法:

 

  1. func makeNewEntry() float64 { 
  2.    return highRange * rand.Float64() 
  3.  
  4. func calculate(x float64) float64 { 
  5.    return  math.Pow(x, 2) - 6*x + 2 // minimum should be at x=3 

既然已經(jīng)為二次實(shí)現(xiàn)創(chuàng)建了接口,那么GA本身需要完成:

 

  1. func Run(geneticAlgoRunner GeneticAlgorithmRunner, settings GeneticAlgorithmSettings) (interface{}, error){ 
  2.  
  3.    population := geneticAlgoRunner.GenerateInitialPopulation(settings.PopulationSize) 
  4.  
  5.    geneticAlgoRunner.Sort(population) 
  6.  
  7.    bestSoFar := population[len(population) - 1] 
  8.  
  9.    for i:= 0; i < settings.NumGenerations; i++ { 
  10.  
  11.       newPopulation := make([]interface{}, 0, settings.PopulationSize) 
  12.  
  13.       if settings.KeepBestAcrossPopulation { 
  14.          newPopulation = append(newPopulation, bestSoFar) 
  15.       } 
  16.  
  17.       // perform crossovers with random selection 
  18.       probabilisticListOfPerformers := createStochasticProbableListOfIndividuals(population) 
  19.  
  20.       newPopIndex := 0 
  21.       if settings.KeepBestAcrossPopulation{ 
  22.          newPopIndex = 1 
  23.       } 
  24.       for ; newPopIndex < settings.PopulationSize; newPopIndex++ { 
  25.          indexSelection1 := rand.Int() % len(probabilisticListOfPerformers) 
  26.          indexSelection2 := rand.Int() % len(probabilisticListOfPerformers) 
  27.  
  28.          // crossover 
  29.          newIndividual := geneticAlgoRunner.PerformCrossover( 
  30.             probabilisticListOfPerformers[indexSelection1], 
  31.             probabilisticListOfPerformers[indexSelection2], settings.CrossoverRate) 
  32.  
  33.          // mutate 
  34.          if rand.Intn(101) < settings.MutationRate { 
  35.             newIndividual = geneticAlgoRunner.PerformMutation(newIndividual) 
  36.          } 
  37.  
  38.          newPopulation = append(newPopulation, newIndividual) 
  39.       } 
  40.  
  41.       population = newPopulation 
  42.  
  43.       // sort by performance 
  44.       geneticAlgoRunner.Sort(population) 
  45.  
  46.       // keep the best so far 
  47.       bestSoFar = population[len(population) - 1] 
  48.  
  49.    } 
  50.  
  51.    return bestSoFar, nil 
  52.  
  53. func createStochasticProbableListOfIndividuals(population []interface{}) []interface{} { 
  54.  
  55.    totalCount, populationLength:= 0, len(population) 
  56.    for j:= 0; j < populationLength; j++ { 
  57.       totalCount += j 
  58.    } 
  59.  
  60.    probableIndividuals := make([]interface{}, 0, totalCount) 
  61.    for index, individual := range population { 
  62.       for i:= 0; i < index; i++{ 
  63.          probableIndividuals = append(probableIndividuals, individual) 
  64.       } 
  65.    } 
  66.  
  67.    return probableIndividuals 

很像以前,一個(gè)新的人口被創(chuàng)造出來,人口的成員將會(huì)世代交配,而他們的后代可能攜帶突變。一個(gè)人的表現(xiàn)越好,就越有可能交配。隨著時(shí)間的推移,算法收斂到***的答案,或者至少是一個(gè)相當(dāng)不錯(cuò)的答案。

那么當(dāng)它運(yùn)行時(shí),它返回了什么呢?

  1. Best: x: 3.072833 y: -6.994695 

不壞!由于人口規(guī)模只有5、20代,而且輸入的范圍被限制在[0 100],這一搜索就釘在了頂點(diǎn)上。

現(xiàn)在,您可能想知道為什么我定義了所有的接口方法來返回“接口{}”。這就像Go和generics一樣。沒有對(duì)象,因此沒有對(duì)象類型返回,但是沒有描述的大小的數(shù)據(jù)仍然可以在堆棧上傳遞。這本質(zhì)上也是這個(gè)返回類型的含義:它傳遞一些已知的和類似的類型的對(duì)象。有了這個(gè)“泛型”,我就可以將GA移動(dòng)到它自己的包中,并將相同的代碼移到多個(gè)不同類型的數(shù)據(jù)上。

我們有兩個(gè)輸入的3D二次方程,而不是一個(gè)二維二次方程的單個(gè)輸入。接口方法只需要很小的改變:

 

  1. type Quad3D struct { 
  2.    x, y float64 
  3. func makeNewQuadEntry(newX, newY float64) Quad3D { 
  4.    return Quad3D{ 
  5.       x: newX, 
  6.       y: newY, 
  7.    } 
  8.  
  9. func calculate3D(entry Quad3D) float64 { 
  10.    return math.Pow(entry.x, 2)- 6 * entry.x + math.Pow(entry.y, 2)- 6 * entry.y + 2 
  11.  
  12. type Quadratic3dGA struct { 
  13.  
  14. func (l Quadratic3dGA) GenerateInitialPopulation(populationSize int)[]interface{}{ 
  15.  
  16.    initialPopulation := make([]interface{}, 0, populationSize) 
  17.    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)) 
  18.    }) 
  19.  
  20. func quadratic3dMain(){ 
  21.    settings := ga.GeneticAlgorithmSettings{ 
  22.       PopulationSize: 25, 
  23.       MutationRate: 10, 
  24.       CrossoverRate: 100, 
  25.       NumGenerations: 20, 
  26.       KeepBestAcrossPopulation: true
  27.    } 
  28.  
  29.    best, err := ga.Run(Quadratic3dGA{}, settings) 
  30.    entry := best.(Quad3D) 
  31.  
  32.    if err != nil { 
  33.       println(err) 
  34.    }else
  35.       fmt.Printf("Best: x: %f  y: %f  z: %f\n", entry.x, entry.y, calculate3D(entry)) 
  36.    } 

而不是到處都是float64s,任何地方都可以通過Quad3D的條目;每一個(gè)都有一個(gè)X和一個(gè)Y值。對(duì)于創(chuàng)建的每個(gè)條目,都使用contructor makeNewQuadEntry創(chuàng)建。Run()方法中的代碼都沒有更改。

當(dāng)它運(yùn)行時(shí),我們得到這個(gè)輸出:

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

很接近了!

哦,我忘了說走快了!在Java中執(zhí)行此操作時(shí),即使使用相同的設(shè)置,也會(huì)有明顯的等待時(shí)間。在一個(gè)相對(duì)較小的范圍內(nèi)求解二次方程并不是很復(fù)雜,但它對(duì)一個(gè)人來說是值得注意的。

Go是本地編譯的,比如C。當(dāng)二進(jìn)制執(zhí)行時(shí),它似乎馬上就吐出一個(gè)答案。這里有一個(gè)簡單的方法來度量每次運(yùn)行的執(zhí)行時(shí)間:

 

  1. func main() { 
  2.    beforeQuadTime := time.Now() 
  3.    quadraticMain() 
  4.    afterQuadTime := time.Since(beforeQuadTime) 
  5.    fmt.Printf("%d\n", afterQuadTime) 
  6.  
  7.    before3dQuadTime := time.Now() 
  8.    quadratic3dMain() 
  9.    after3dQuatTime := time.Since(before3dQuadTime) 
  10.    fmt.Printf("%d\n", after3dQuatTime) 

邊注:我能說我很高興我們是一個(gè)開發(fā)者社區(qū),讓他們從過去的錯(cuò)誤中走出來,并把綜合的時(shí)間模塊和包構(gòu)建成一種語言嗎?Java 8 +擁有它們,Python擁有它們,并擁有它們。這使我開心。

現(xiàn)在的輸出:

 

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

那“近乎瞬間”的感覺是我想要傳達(dá)的,現(xiàn)在我們有了很難的數(shù)字。136,876看起來很大,但要在納秒內(nèi)報(bào)告時(shí)間。

重申一遍:納秒。不是幾毫秒,我們都習(xí)慣了在互聯(lián)網(wǎng)時(shí)代或者其他像Python和Java這樣的通用語言;納秒。1/1,000,000毫秒。

這意味著我們?cè)诓坏揭缓撩氲臅r(shí)間里找到了一個(gè)使用遺傳算法來搜索答案的二次方程的答案。這句話,“該死的瞬間”似乎很合適,不是嗎?這包括打印到終端。

那么,要計(jì)算更密集的東西呢?在我展示一種尋找好的夢幻足球lineups的方法之前,我在Fanduel上使用。這包括從電子表格中讀取數(shù)據(jù),制作和過濾lineups,并進(jìn)行更復(fù)雜的交叉和突變。強(qiáng)制尋找***解決方案可能需要超過75,000年(至少使用我當(dāng)時(shí)使用的Python)。

我不需要再檢查所有的細(xì)節(jié),你可以自己去看代碼,但我會(huì)在這里顯示輸出:

 

  1. Best: 121.409960:, $58100 
  2. QB: Aaron Rodgers - 23.777778 
  3. RB: Latavius Murray - 15.228571 
  4. RB: DeMarco Murray - 19.980000 
  5. WR: Kelvin Benjamin - 11.800000 
  6. WR: Stefon Diggs - 14.312500 
  7. WR: Alshon Jeffery - 9.888889 
  8. TE: Connor Hamlett - 8.200000 
  9. D: Philadelphia Eagles - 10.777778 
  10. K: Phil Dawson - 7.444444 
  11. 16,010,182 

哦,是的!現(xiàn)在看來這是一個(gè)很好的陣容!它只需要16毫秒就能找到。

現(xiàn)在,這個(gè)遺傳算法可以改進(jìn)了。與C一樣,當(dāng)將對(duì)象傳遞給方法時(shí),將在堆棧上復(fù)制對(duì)象(讀取數(shù)據(jù))。隨著對(duì)象大小的增長,***不要反復(fù)復(fù)制它們,而是要在堆中創(chuàng)建它們,并在周圍傳遞指針。目前,我將把它作為未來的工作。

Go也被用coroutines和信道的原生支持編寫,利用多個(gè)內(nèi)核來解決一個(gè)問題,比過去簡單多了,相比于單核時(shí)代的其他語言來說,這是一個(gè)巨大的優(yōu)勢。我想要增強(qiáng)這個(gè)算法來使用這些工具,但這也必須留給以后的工作。

我很享受學(xué)習(xí)的過程。對(duì)于我來說,用組合而不是繼承來考慮工程解決方案是很困難的,因?yàn)槲乙呀?jīng)習(xí)慣了8年以上的時(shí)間,也是我學(xué)會(huì)編程的方式。但是每種語言和方式都有各自的優(yōu)點(diǎn)和缺點(diǎn);每一種語言在我的工具中都是不同的工具。對(duì)于任何擔(dān)心嘗試的人,不要。有一個(gè)駝峰(更像是一個(gè)減速帶),但你很快就會(huì)克服它,走上成功之路。

還有一些我喜歡的東西,我喜歡其他語言,主要是一組基本的函數(shù)方法來操作數(shù)據(jù)。我需要一個(gè)lambda函數(shù)和方法來映射、減少和篩選數(shù)據(jù)的數(shù)組或部分。設(shè)計(jì)人員反對(duì)功能實(shí)現(xiàn)的理由是,代碼應(yīng)該總是簡單、易于閱讀和編寫,并且這與for循環(huán)是可實(shí)現(xiàn)的。我認(rèn)為,映射、過濾和減少通常更容易讀和寫,但這是一場已經(jīng)在肆虐的戰(zhàn)爭中的爭論。

盡管我與一些開發(fā)人員的觀點(diǎn)存在分歧,以及我必須考慮解決問題的不同方式,但Go真的是一種很好的語言。我鼓勵(lì)大家在學(xué)習(xí)一兩門語言后再試一試。它很快就成為了***的語言之一,有很多原因可以解釋為什么。我期待著在未來更多地使用它。

責(zé)任編輯:未麗燕 來源: CSDN
相關(guān)推薦

2025-01-16 07:10:00

2024-07-03 08:00:00

2020-06-11 08:32:50

Python遺傳算法代碼

2021-03-16 11:30:33

2021-03-10 15:49:20

人工智能遺傳算法

2017-09-22 15:03:08

Python遺傳算法GAFT框架

2009-08-14 09:41:03

C#遺傳算法

2017-07-12 14:23:25

遺傳算法java自然選擇

2024-09-12 10:06:21

2017-08-21 10:00:23

遺傳算法Python生物學(xué)

2017-08-03 10:05:01

Python遺傳算法GAFT

2020-10-26 13:42:28

Python算法垃圾

2017-10-17 14:25:56

機(jī)器學(xué)習(xí)算法優(yōu)化

2014-11-28 16:08:33

射頻識(shí)別RFID

2022-11-01 18:29:25

Go語言排序算法

2023-05-08 07:55:05

快速排序Go 語言

2010-05-11 11:00:44

遺傳算法宋詞

2022-05-19 14:14:26

go語言限流算法

2020-03-17 10:24:12

Go語言停止寫障礙

2017-09-22 14:12:04

AI
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)