参考,引用文章:
小白易懂的遗传算法(Python代码实现)_遗传算法python代码详解_tango棒棒的博客-CSDN博客
遗传算法及python实现_python遗传算法代码_某只旺仔的博客-CSDN博客
遗传算法详解 附python代码实现_遗传算法python-CSDN博客
GA算法步骤
1.编码:用二进制编码染色体
2.解码:为了下一步带到适应度函数
二进制转十进制
##二进制转实数
x = x_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (X_BOUND[1] - X_BOUND[0]) + X_BOUND[0]
这段代码是在进行一种数值转换,将一个二进制数转换为一个在指定范围内的实数。具体来说:
-
x_pop.dot(2 ** np.arange(DNA_SIZE)[::-1])
:这部分是将x_pop
中的每个元素与2的幂次方相乘,幂次方为从大到小的整数序列。 -
/ float(2 ** DNA_SIZE - 1)
:这部分是将上一步的结果除以2的DNA_SIZE次方减1,得到的结果是一个在0到1之间的实数。 -
* (X_BOUND[1] - X_BOUND[0])
:这部分是将上一步的结果乘以X轴的范围(即X_BOUND[1]减去X_BOUND[0])。 -
+ X_BOUND[0]
:这部分是将上一步的结果加上X轴的最小值(即X_BOUND[0])
3.适应度函数
求最大:适应度函数=目标函数
求最小:倒数/负数
适应度要为正,因为后面要求比例。目标函数为负怎么办:
#求解函数最大值
def get_fitness(pop):
x,y = translateDNA(pop) #解码过程
pred = F(x, y) #代入原函数求解适应度
return (pred - np.min(pred)) + 1e-3
#减去最小的适应度是为了防止适应度出现负数,通过这一步fitness的范围为[0, np.max(pred)-np.min(pred)],最后在加上一个很小的数防止出现为0的适应度
#求解函数最小值
def get_fitness(pop):
x,y = translateDNA(pop)
pred = F(x, y)
return -(pred - np.max(pred)) + 1e-3
理解:注意这里的pred是一个可能解代入函数得到的集合
每个减去最小的pred一定能保证是正值
比如1-(-5),-1-(-5),-3-(-5),-5-(-5)都大于0。
4.选择
适应度高的选择概率高,轮盘赌
使用p=(fitness)/(fitness.sum())
计算每个索引被选中的概率。这里,fitness
数组中的每个元素都被除以fitness
数组的总和,使得所有元素的和为1。这样,具有较高适应度值的个体被选中的概率更高。
def select(pop, fitness):
idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
p=(fitness)/(fitness.sum()) )
return pop[idx]
idx-索引值:idx的返回值是一个长度为POP_SIZE的一维数组,其中每个元素是从0到POP_SIZE-1的整数。这些整数是根据概率分布随机选择的,具体的概率分布由fitness数组决定。
choice的用法:
numpy.random.choice(a, size=None, replace=True, p=None)
a
:表示一维数组或整数。如果是一维数组,则从中随机抽取元素;如果是整数,则表示从range(a)
中随机抽取元素。size
:表示要抽取的元素个数。如果为None(默认值),则只抽取一个元素;如果为整数n,则抽取n个元素。replace
:表示是否允许重复抽取。如果为True(默认值),则允许重复抽取;如果为False,则不允许重复抽取。p
:表示每个元素被抽取的概率。如果为None(默认值),则所有元素被抽取的概率相等;如果为一维数组,则其长度必须与a
相同,且每个元素表示对应元素被抽取的概率。
返回值:
- 返回一个由从
a
中随机抽取的元素组成的一维数组。如果size
为None,则返回一个标量;如果replace
为False,则返回的数组不会包含重复的元素。
5.交叉,变异
对DNA进行剪接,找到两处随机点位置,对中间的片段进行交叉操作。
#交叉
def crossover_and_mutation(pop, CROSSOVER_RATE = 0.8):
new_pop = []
for father in pop:
child = father
if np.random.rand() < CROSSOVER_RATE: #np.random.rand()生成一个0-1间随机数,如果小于交叉概率则交叉
mother = pop[np.random.randint(POP_SIZE)] #再在种群中随机选择另一个个体,并将该个体作为母亲
cross_points = np.random.randint(low=0, high=DNA_SIZE) #随机产生交叉的点
child[cross_points:] = mother[cross_points:] #孩子得到位于交叉点后的母亲的基因
mutation(child) #每个后代有一定的机率发生变异
new_pop.append(child)
return new_pop
#变异
def mutation(child, MUTATION_RATE=0.003):
if np.random.rand() < MUTATION_RATE: #以MUTATION_RATE的概率进行变异
mutate_point = np.random.randint(0, DNA_SIZE) #随机产生一个实数,代表要变异基因的位置
child[mutate_point] = child[mutate_point]^1 #将变异点的二进制为反转
其中,变异的异或操作
child[mutate_point] ^ 1
的作用是将 child[mutate_point]
的值与1进行异或操作,然后将结果赋值给 child[mutate_point]
。
举个例子,假设 child
是一个包含二进制数的列表,例如 [0, 1, 0, 1, 1, 0, 1, 0, 0, 1]
,mutate_point
是3。那么执行 child[mutate_point] = child[mutate_point] ^ 1
后,child
变为 [0, 1, 0, 0, 1, 0, 1, 0, 0, 1]
,因为原来索引为3的元素是1,与1进行异或操作后变为了0。
扩展知识:
np.random.randint(low, high=None, size=None, dtype='l') 是numpy库中的一个函数,用于生成指定范围内的随机整数
np.random
是NumPy库中的一个模块,用于生成随机数。它提供了多种生成随机数的方法,如整数、浮点数、正态分布等。以下是一些常用的np.random
函数:
np.random.rand()
:生成一个0到1之间的随机浮点数。np.random.randn()
:生成一个标准正态分布(均值为0,标准差为1)的随机浮点数。np.random.randint(low, high=None, size=None, dtype='l')
:生成一个指定范围内的随机整数。参数low
表示范围的下限,high
表示范围的上限,size
表示生成随机数的形状,dtype
表示生成随机数的数据类型。np.random.choice(a, size=None, replace=True, p=None)
:从给定的一维数组a
中随机选择元素。参数size
表示生成随机数的数量,replace
表示是否允许重复选择,p
表示每个元素被选中的概率。np.random.shuffle(x)
:将一维数组x
的元素随机打乱。np.random.permutation(x)
:返回一个随机排列的一维数组x
。np.random.beta(a, b, size=None)
:生成服从Beta分布的随机数。参数a
和b
分别表示Beta分布的两个参数,size
表示生成随机数的形状。np.random.gamma(shape, scale=1.0, size=None)
:生成服从Gamma分布的随机数。参数shape
表示Gamma分布的形状参数,scale
表示Gamma分布的尺度参数,size
表示生成随机数的形状。np.random.dirichlet(alpha, size=None)
:生成服从Dirichlet分布的随机数。参数alpha
是一个一维数组,表示Dirichlet分布的参数,size
表示生成随机数的形状
6.主函数
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE)) #生成种群 matrix (POP_SIZE, DNA_SIZE)
for _ in range(N_GENERATIONS): #种群迭代进化N_GENERATIONS代
pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE)) #种群通过交叉变异产生后代
fitness = get_fitness(pop) #对种群中的每个个体进行评估
pop = select(pop, fitness) #选择生成新的种群
具体python代码
转载:遗传算法详解 附python代码实现_重学CS的博客-CSDN博客_python遗传算法
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
DNA_SIZE = 24
POP_SIZE = 200
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.005
N_GENERATIONS = 50
X_BOUND = [-3, 3]
Y_BOUND = [-3, 3]
def F(x, y):
return 3*(1-x)**2*np.exp(-(x**2)-(y+1)**2)- 10*(x/5 - x**3 - y**5)*np.exp(-x**2-y**2)- 1/3**np.exp(-(x+1)**2 - y**2)
def plot_3d(ax):
X = np.linspace(*X_BOUND, 100)
Y = np.linspace(*Y_BOUND, 100)
X,Y = np.meshgrid(X, Y)
Z = F(X, Y)
ax.plot_surface(X,Y,Z,rstride=1,cstride=1,cmap=cm.coolwarm)
ax.set_zlim(-10,10)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.pause(3)
plt.show()
def get_fitness(pop):
x,y = translateDNA(pop)
pred = F(x, y)
return (pred - np.min(pred)) + 1e-3 #减去最小的适应度是为了防止适应度出现负数,通过这一步fitness的范围为[0, np.max(pred)-np.min(pred)],最后在加上一个很小的数防止出现为0的适应度
def translateDNA(pop): #pop表示种群矩阵,一行表示一个二进制编码表示的DNA,矩阵的行数为种群数目
x_pop = pop[:,1::2]#奇数列表示X
y_pop = pop[:,::2] #偶数列表示y
#pop:(POP_SIZE,DNA_SIZE)*(DNA_SIZE,1) --> (POP_SIZE,1)
x = x_pop.dot(2**np.arange(DNA_SIZE)[::-1])/float(2**DNA_SIZE-1)*(X_BOUND[1]-X_BOUND[0])+X_BOUND[0]
y = y_pop.dot(2**np.arange(DNA_SIZE)[::-1])/float(2**DNA_SIZE-1)*(Y_BOUND[1]-Y_BOUND[0])+Y_BOUND[0]
return x,y
def crossover_and_mutation(pop, CROSSOVER_RATE = 0.8):
new_pop = []
for father in pop: #遍历种群中的每一个个体,将该个体作为父亲
child = father #孩子先得到父亲的全部基因(这里我把一串二进制串的那些0,1称为基因)
if np.random.rand() < CROSSOVER_RATE: #产生子代时不是必然发生交叉,而是以一定的概率发生交叉
mother = pop[np.random.randint(POP_SIZE)] #再种群中选择另一个个体,并将该个体作为母亲
cross_points = np.random.randint(low=0, high=DNA_SIZE*2) #随机产生交叉的点
child[cross_points:] = mother[cross_points:] #孩子得到位于交叉点后的母亲的基因
mutation(child) #每个后代有一定的机率发生变异
new_pop.append(child)
return new_pop
def mutation(child, MUTATION_RATE=0.003):
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 select(pop, fitness): # nature selection wrt pop's fitness
idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
p=(fitness)/(fitness.sum()) )
return pop[idx]
def print_info(pop):
fitness = get_fitness(pop)
max_fitness_index = np.argmax(fitness)
print("max_fitness:", fitness[max_fitness_index])
x,y = translateDNA(pop)
print("最优的基因型:", pop[max_fitness_index])
print("(x, y):", (x[max_fitness_index], y[max_fitness_index]))
if __name__ == "__main__":
fig = plt.figure()
ax = Axes3D(fig)
plt.ion()#将画图模式改为交互模式,程序遇到plt.show不会暂停,而是继续执行
plot_3d(ax)
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE*2)) #matrix (POP_SIZE, DNA_SIZE)
for _ in range(N_GENERATIONS):#迭代N代
x,y = translateDNA(pop)
if 'sca' in locals(): #检查当前作用域中是否存在名为sca的变量。如果存在,说明之前已经绘制过散点图,需要将其移除
sca.remove()
sca = ax.scatter(x, y, F(x,y), c='black', marker='o') #散点图
plt.show()
plt.pause(0.1)
pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE))
#F_values = F(translateDNA(pop)[0], translateDNA(pop)[1])#x, y --> Z matrix
fitness = get_fitness(pop)
pop = select(pop, fitness) #选择生成新的种群
print_info(pop)
plt.ioff()
plot_3d(ax)
指定的间隔内生成等差数列,也就是将一个区间均匀分割成若干份
numpy.linspace (start, stop, num=50, endpoint=True, retstep=False, dtype=None)
AI给我的简单遗传算法:
import numpy as np
def init_population(pop_size, x_range):
return np.random.uniform(x_range[0], x_range[1], (pop_size,))
def evaluate(population):
return aim(population)
def selection(population, fitness):
idx = np.random.choice(np.arange(len(population)), size=2, p=fitness/fitness.sum())
return population[idx]
def crossover(parents):
crossover_point = np.random.randint(1, len(parents[0]))
child1 = np.concatenate((parents[0][:crossover_point], parents[1][crossover_point:]))
child2 = np.concatenate((parents[1][:crossover_point], parents[0][crossover_point:]))
return child1, child2
def mutation(child, x_range, mutation_rate):
if np.random.rand() < mutation_rate:
mutation_point = np.random.randint(len(child))
child[mutation_point] = np.random.uniform(x_range[0], x_range[1])
return child
def genetic_algorithm(pop_size, x_range, max_iter, mutation_rate):
population = init_population(pop_size, x_range)
for i in range(max_iter):
fitness = fitnessget(evaluate(population))
new_population = []
for _ in range(pop_size // 2):
parents = selection(population, fitness)
child1, child2 = crossover(parents)
new_population.append(mutation(child1, x_range, mutation_rate))
new_population.append(mutation(child2, x_range, mutation_rate))
population = np.array(new_population)#转为数组形式
best_individual = population[np.argmax(fitness)]
return best_individual, aim(best_individual)
pop_size = 100
x_range = [0, 5]
max_iter = 1000
mutation_rate = 0.1
best_individual, best_fitness = genetic_algorithm(pop_size, x_range, max_iter, mutation_rate)
print("最优解:", best_individual)
print("最优解对应的目标函数值:", best_fitness)