遗传算法-GA-小白笔记

本文详细介绍了遗传算法的基本原理,包括编码、解码过程,如何通过二进制转换为实数,以及适应度函数的选择、轮盘赌选择策略、交叉和变异操作。还提供了Python代码实例,展示了如何在特定问题中应用遗传算法进行优化。
摘要由CSDN通过智能技术生成

 参考,引用文章:

小白易懂的遗传算法(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]

这段代码是在进行一种数值转换,将一个二进制数转换为一个在指定范围内的实数。具体来说:

  1. x_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]):这部分是将x_pop中的每个元素与2的幂次方相乘,幂次方为从大到小的整数序列。

  2. / float(2 ** DNA_SIZE - 1):这部分是将上一步的结果除以2的DNA_SIZE次方减1,得到的结果是一个在0到1之间的实数。

  3. * (X_BOUND[1] - X_BOUND[0]):这部分是将上一步的结果乘以X轴的范围(即X_BOUND[1]减去X_BOUND[0])。

  4. + 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函数:

  1. np.random.rand():生成一个0到1之间的随机浮点数。
  2. np.random.randn():生成一个标准正态分布(均值为0,标准差为1)的随机浮点数。
  3. np.random.randint(low, high=None, size=None, dtype='l'):生成一个指定范围内的随机整数。参数low表示范围的下限,high表示范围的上限,size表示生成随机数的形状,dtype表示生成随机数的数据类型。
  4. np.random.choice(a, size=None, replace=True, p=None):从给定的一维数组a中随机选择元素。参数size表示生成随机数的数量,replace表示是否允许重复选择,p表示每个元素被选中的概率。
  5. np.random.shuffle(x):将一维数组x的元素随机打乱。
  6. np.random.permutation(x):返回一个随机排列的一维数组x
  7. np.random.beta(a, b, size=None):生成服从Beta分布的随机数。参数ab分别表示Beta分布的两个参数,size表示生成随机数的形状。
  8. np.random.gamma(shape, scale=1.0, size=None):生成服从Gamma分布的随机数。参数shape表示Gamma分布的形状参数,scale表示Gamma分布的尺度参数,size表示生成随机数的形状。
  9. 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)

  • 16
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值