"""
Visualize Genetic Algorithm to find a maximum point in a function.
Visit my tutorial website for more: https://mofanpy.com/tutorials/
"""
import numpy as np
import matplotlib.pyplot as plt
DNA_SIZE = 10 # DNA length,dna长度
POP_SIZE = 100 # population size 初始种群大小
CROSS_RATE = 0.8 # mating probability (DNA crossover) 交叉的概率
MUTATION_RATE = 0.003 # mutation probability 变异概率
N_GENERATIONS = 200 #迭代次数
X_BOUND = [0, 5] # x upper and lower bounds 函数x坐标范围
def F(x): return np.sin(10*x)*x + np.cos(2*x)*x # to find the maximum of this function
# find non-zero fitness for selection
def get_fitness(pred): return pred + 1e-3 - np.min(pred)
#因为如果直接返回pred可能是负值,而我们在计算概率的时候不能为负值。
#要进行处理,np.min表示取最小,为最大的负数,可以使全部只变成正的;1e-3为了让float进行相除防止小数点后的数被省略
# convert binary DNA to decimal and normalize it to a range(0, 5)
def translateDNA(pop): return pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2**DNA_SIZE-1) * X_BOUND[1]
def select(pop, fitness): # nature selection wrt pop's fitness
#np.arrange(POP_SIZE)生成从[0,POP_SIZE)的步长为1的数组
idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
p=fitness/fitness.sum())
#np.arrange(POP_SIZE)和fitness是同长一维数组,按照fitness的概率有放回的选择np.arrange(POP_SIZE)中的数,
#其中的数字可能会重复,毕竟概率越大越容易选到
return pop[idx] #按索引去选择pop的数组,numpy的数组除了可被整数索引还可以被数组索引
def crossover(parent, pop): # mating process (genes crossover)
if np.random.rand() < CROSS_RATE: #0.8的概率父亲选择一个母亲交叉配对
i_ = np.random.randint(0, POP_SIZE, size=1) #选取随机母亲下标索引 # select another individual from pop
#随机选择一个dna长度的01数组
cross_points = np.random.randint(0, 2, size=DNA_SIZE).astype(np.bool) # choose crossover points
#父亲在cross_points下为1的地方换成pop的母亲的dna
parent[cross_points] = pop[i_, cross_points] # mating and produce one child
return parent
def mutate(child):
for point in range(DNA_SIZE):
if np.random.rand() < MUTATION_RATE:
child[point] = 1 if child[point] == 0 else 0 #变异,在某个概率下将某些01互换
return child
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE)) # initialize the pop DNA
# 返回一个0,1数组,大小pop_size,长度dna_size
plt.ion() # something about plotting 画动态图,开启交互模式
x = np.linspace(*X_BOUND, 200) #产生从0,5的等差数列,个数为200个
plt.plot(x, F(x)) #画出平滑的F(x)的曲线
for _ in range(N_GENERATIONS):
F_values = F(translateDNA(pop)) # compute function value by extracting DNA
# something about plotting
if 'sca' in globals(): sca.remove() #'sca'判断是不是一个全局变量,如果是,则将其移除
sca = plt.scatter(translateDNA(pop), F_values, s=200, lw=0, c='red', alpha=0.5);plt.pause(0.05)
# GA part (evolution)
fitness = get_fitness(F_values)
print("Most fitted DNA: ", pop[np.argmax(fitness), :])
pop = select(pop, fitness)
pop_copy = pop.copy()
for parent in pop:
child = crossover(parent, pop_copy)
child = mutate(child)
parent[:] = child # parent is replaced by its child
plt.ioff(); plt.show()