简单思路就是:
DNA = np.random.randint(2, size=(POP_SIZE, DNA_SIZE * NUM))
for i in range(N_GENERATIONS):
将DNA分为NUM个矩阵,矩阵的每一行翻译为一个数字
DNA变异交叉
计算适应度
物竞天择
代码:
"""
--------------------------------------------
DNA_SIZE: DNA长度,长度越长精度越高
POP_SIZE: 初始种群数
CROSSOVER_RATE: 交叉概率
MUTATION_RATE: 变异概率
N_GENERATIONS: 迭代次数
X1_BOUND: x1自变量范围
X2_BOUND: x2自变量范围
NUM: 自变量数量
--------------------------------------------
"""
import numpy as np
# 初始化群体----------------------------------------------------------------------------------------
DNA_SIZE = 5 # DNA长度,x1,x2各5个,长度越长精度越高
POP_SIZE = 50 # 初始种群数
CROSSOVER_RATE = 0.95 # 交叉概率
MUTATION_RATE = 0.01 # 变异概率
N_GENERATIONS = 30 # 迭代次数
X1_BOUND = [-1, 2] # x1自变量范围
X2_BOUND = [-1, 2] # x2自变量范围
# 定义函数----------------------------------------------------------------------------------------
def function(x1, x2):
# y = f(x1,x2)
return y
class GA():
# 解码过程 ----------------------------------------------------------------------------------------
def translateDNA(self, DNA):
# DNA表示种群矩阵,一行表示一个二进制编码表示的DNA,矩阵的行数为种群数目
x1_DNA = DNA[:, 1::2] # 前DNA_SIZE位表示x1 (1::2:从下标1开始,步长2)
x2_DNA = DNA[:, ::2] # 后DNA_SIZE位表示x2
# DNA:(POP_SIZE,DNA_SIZE)*(DNA_SIZE,1) --> (POP_SIZE,1)
# # 转二进制 --> 除2的SIZE次方 --> 摆开到bound间
x1 = x1_DNA.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (X1_BOUND[1] - X1_BOUND[0]) + \
X1_BOUND[0]
x2 = x2_DNA.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (X2_BOUND[1] - X2_BOUND[0]) + \
X2_BOUND[0]
return x1, x2
# 交叉过程 ----------------------------------------------------------------------------------------
def crossover_and_mutation(self, DNA, CROSSOVER_RATE=CROSSOVER_RATE):
new_DNA = []
for father in DNA: # 遍历种群中的每一个个体,将该个体作为父亲
child = father # 孩子先得到父亲的全部基因(这里我把一串二进制串的那些0,1称为基因)
if np.random.rand() < CROSSOVER_RATE: # 产生子代时不是必然发生交叉,而是以一定的概率发生交叉
mother = DNA[np.random.randint(POP_SIZE)] # 再种群中选择另一个个体,并将该个体作为母亲
cross_points = np.random.randint(low=0, high=DNA_SIZE * 2) # 随机产生交叉的点
child[cross_points:] = mother[cross_points:] # 孩子得到位于交叉点后的母亲的基因
self.mutation(child) # 每个后代有一定的机率发生变异
new_DNA.append(child)
return new_DNA
# 变异过程 ----------------------------------------------------------------------------------------
def mutation(self, child, MUTATION_RATE=MUTATION_RATE):
if np.random.rand() < MUTATION_RATE: # 以MUTATION_RATE的概率进行变异
mutate_point = np.random.randint(0, DNA_SIZE * 2) # 随机产生一个实数,代表要变异基因的位置
child[mutate_point] = child[mutate_point] ^ 1 # 将变异点的二进制为反转
# 计算适应值 ----------------------------------------------------------------------------------------
def get_fitness(self, DNA):
x1, x2 = self.translateDNA(DNA)
pred = function(x1, x2)
return (pred - np.min(pred)) + 1e-3
'''
减去最小的适应度是为了防止适应度出现负数,
通过这一步fitness的范围为[0, np.max(pred)-np.min(pred)],
最后在加上一个很小的数防止出现为0的适应度
'''
# 选择适应值高的个体 ----------------------------------------------------------------------------------------
def select(self, DNA, fitness): # nature selection wrt DNA's fitness
idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True, p=fitness / fitness.sum())
return DNA[idx]
if __name__ == "__main__":
DNA = np.random.randint(2, size=(POP_SIZE, DNA_SIZE * 2))
ga = GA()
for i in range(N_GENERATIONS):
x1, x2 = ga.translateDNA(DNA)
DNA = np.array(ga.crossover_and_mutation(DNA, CROSSOVER_RATE))
fitness = ga.get_fitness(DNA)
DNA = ga.select(DNA, fitness)