Python 遗传算法理解

Talk is cheap, show me the code. 代码整理自Youtube: Genetic Algorithm In Python Super Basic Example。作者几乎没有将任何理论上的知识点,但是一切又是那么的自然而然。

目标

对于下面函数foo,我们需要求解一组参数x,y,z,使得函数值尽可能接近于0

def foo(x,y,z):  
    return 6*x**3 + 9*y**2 + 90*z - 25

为此,我们还需要一个函数,来判断对于一组参数,函数foo 的返回值有多么接近0

def fitness(x,y,z):  
    ans = foo(x,y,z)  

    if ans == 0:  
        return 99999999   # 如果foo 等于0,则应返回无穷大
    else:  
        return abs(1/ans) # 否则则返回其倒数的绝对值,值越大,传入的参数越接近于最优解

生成解的集合

首先,随机生成1000 组解,也就是1009 个包含x,y,z 的数组:

import random  

solutions = []  
for s in range(1000):
    solutions.append( (
                        random.uniform(0,1000),  # x
                        random.uniform(0,1000),  # y
                        random.uniform(0,1000)   # z
                      ) )

# 可以打印结的前几行  
print(solutions[:5])

主体功能

主体功能包含:求解,筛选,组合,编译。往复循环直到满足条件或者找到最优解

# 假设主体功能将循环1000 次  
for i in range(10000):  

    # 1. 求解,并将所有的解按合适的程度排序  
    rankedsolutions = []  
    for s in solutions:  
        rankedsolutions.append( (fitness(s[0],s[1],s[2]), s) )
    rankedsolutions.sort()    # 排序
    rankedsolutions.reverse() # 逆序

    print(f"=== Gen {i} best solutions ===")  # 打印运行信息

    # 2. 筛选前100 结果,并将所有解放入一个容器,随机抽奖  
    bestsolutions = rankedsolutions[:100]  # 筛选  

    topS = bestsolutions[0]
    if topS[0] > 10000:   # 终止循环,已找到最优解
        print(topS)
        break  
    
    elementsX = []   # 基因容器  
    elementsY = []   # 原视频并没有细分x,y,z 反而是把他们混在一起了  
    elementsZ = []   # 窃以为那样做是不合理的  

    # 为基因容器填入内容  
    for bs in bestsolutions:  
        s = bs[1]  # 因为上面的代码中,我们的bs 是这种形式(fitness, (x,y,z))
        elementsX.append(s[0])
        elementsY.append(s[1])
        elementsZ.append(s[2])

    # 交叉组合+变异,生成新一代解  
    newGen = []
    for _ in range(1000): 
        # 随机获取基因的组合             乘以一个随机数相当于是小幅度的变异 
        x = random.choice(elementsX) * random.uniform(0.99, 1.01) 
        y = random.choice(elementsY) * random.uniform(0.99, 1.01)
        z = random.choice(elementsZ) * random.uniform(0.99, 1.01)
        newGen.append((x,y,z))
    solutions = newGen

在理解了上述的流程之后,就很容易在基础的代码上进行扩展,比如基因池的构建和编译的算法,甚至可以进行一些细节的优化来提高计算效率。相信这种代码对于初学者来说更容易理解一些。