我立马定义了一组设置,以便在稍后启动的算法中用到。
第二部分的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: %fn", 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
}










