先举一个简单的求解例子:
变量x,y
函数f(x,y) = (x-5)^2 + (y+3)^2 - 5
求最小值。
def test(x,y):return (x - 5)**2 + (y - 3)**2 - 5
显然,这个函数在x=5,y=3时取最小值-5。现在我们尝试用遗传算法解决之。
遗传算法主要是模拟生物进化的过程,将每一个值视作一个生物,有自己的DNA,会发生交叉变异,同时“适者生存”。
1.编码:
规定x,y的取值范围是[-10,10],并将其映射在一个长度为12的二进制列表中:
尽管严格来说,长度为12的二进制列表最大的值为2^12-1=4095。但为了化简逆映射的求解,我们视作4096,使之可被除尽。此时x,y的取值为[-10,10)。
区间长度是20,2^12 = 4096,20/4096 = 0.0048828125。可以知道二进制数每增长1,对应的10进制数大约增长0.0048828125。
写出其编码函数:
def encode(n,n_min,n_max,dna_length):n_range = n_max - n_minpies= n_range / 2**dna_lengthres = n - n_minn_bit = res / piesreturn get_bits_list(n_bit,dna_length)
2.解码函数:
def decode(dna,dna_length):result = 0for i in range(dna_length):result += dna[i] * (2**i)return result
将这些代码封装到一个类中:
class my_ga():def __init__(self,n_min,n_max,dna_length):self.n_min = n_minself.n_max = n_maxself.dna_length = dna_lengthself.n_range = self.n_max - self.n_minself.pies= self.n_range / 2**self.dna_lengthdef test(x,y):return (x - 5)**2 + (y - 3)**2 - 5def encode(self,n):res = n - self.n_minn_bit = res / self.piesprint(f"n_bit is {n_bit}")return self.get_bits_list(n_bit)def decode(self,dna):result = 0for i in range(self.dna_length):result += dna[i] * (2**i)print(f"result now is {result}")origin = result * self.pies + self.n_minreturn origin
3.种群:
交叉与变异是建立在种群的基础上的。我们将一个(x,y)的二进制条件下的数值对视为一个个体,它们组成一个种群。先将种群数设为1000,同时,定义一个计算适应性的函数。
def get_adapt(self):adapt = np.zeros(shape=(1000))for i in range(1000):adapt[i] = -self.test(self.pop_dna[i])adapt_max = np.max(adapt)adapt_min = np.min(adapt)adapt_normal = (adapt-adapt_min)/(adapt_max-adapt_min) + 0.001return adapt_normal
self.pop_dna = np.random.randint(2,size=(1000,2,dna_length))self.adapt_normal = self.get_adapt()
接着定义根据适应性选择个体的函数:
def select(self):idx = np.random.choice(np.arange(1000),size=(1000), replace=True, p=(self.adapt_normal)/(self.adapt_normal.sum()))return self.pop_dna[idx]
为了让算法更灵活,我们将1000换为pop_size。
4.交叉与变异:
交叉与变异并非是必然发生的,我们需要定义一个交叉率与变异率,同时定义交叉、变异函数:
def cross(self,child):if np.random.rand() < self.cross_rate:mother = self.pop_dna[np.random.randint(self.pop_size)]for j in range(2):cross_point = np.random.randint(low=0, high=self.pop_size)child[j][cross_point:] = mother[j][cross_point:]return childdef muta(self,child):if np.random.rand() < self.muta_rate:muta_dna = np.random.randint(0,2)muta_point = np.random.randint(0,self.pop_size)child[muta_dna][muta_point] ^= 1return childdef cross_muta(self):new_pop = self.pop_dna.copy()for i in range(self.pop_size):new_pop[i] = self.cross(new_pop[i])new_pop[i] = self.muta(new_pop[i])return new_pop
输出函数:
def get_best(self):the_min = 1e9x = 0y = 0for i in range(self.pop_size):if the_min > self.test(self.pop_dna[i]):the_min = self.test(self.pop_dna[i])x = self.decode(self.pop_dna[i][0])y = self.decode(self.pop_dna[i][1])print(f"best indiv is ({x},{y}), result is {the_min}")
测试:
ga = my_ga(n_min=-10,n_max=10,dna_length=12,pop_size=1000,cross_rate=0.8,muta_rate=0.08)print(ga.pop_dna)
genration = 10for _ in range(genration):ga.cross_muta()ga.select()ga.get_adapt()ga.get_best()
完整、且标准的含注释代码:
import numpy as npclass GeneticAlgorithm:# 初始化方法,设置遗传算法的参数def __init__(self, n_min, n_max, dna_length, pop_size, cross_rate, muta_rate):self.n_min = n_min # 最小范围self.n_max = n_max # 最大范围self.dna_length = dna_length # DNA长度self.pop_size = pop_size # 种群大小self.cross_rate = cross_rate # 交叉概率self.muta_rate = muta_rate # 变异概率self.n_range = n_max - n_min # 范围差值self.unit_interval = self.n_range / (2**dna_length) # 每个DNA单元对应的实际数值范围self.population = np.random.randint(2, size=(pop_size, 2, dna_length)) # 随机初始化种群self.normalized_fitness = self.calculate_fitness() # 初始化适应度# 目标函数,需要最小化的函数def objective_function(self, individual):x = self.decode(individual[0]) # 解码第一个DNA片段y = self.decode(individual[1]) # 解码第二个DNA片段return (x - 5)**2 + (y - 3)**2 - 5 # 计算适应度值# 将数值编码为二进制DNA片段def encode(self, n):res = n - self.n_min # 转换为范围内的数值n_binary = res / self.unit_interval # 转换为二进制数return self.to_binary_list(n_binary)# 将二进制DNA片段解码为数值def decode(self, dna):result = 0for i in range(self.dna_length):result += dna[i] * (2**i)decoded_value = result * self.unit_interval + self.n_minreturn decoded_value# 计算适应度值并进行归一化def calculate_fitness(self):fitness = np.zeros(shape=(self.pop_size)) # 初始化适应度数组for i in range(self.pop_size):fitness[i] = -self.objective_function(self.population[i]) # 计算适应度值max_fitness = np.max(fitness)min_fitness = np.min(fitness)normalized_fitness = (fitness - 0.001 - min_fitness) / (max_fitness - min_fitness) + 0.01self.normalized_fitness = normalized_fitnessreturn normalized_fitness# 选择适应度高的个体进行繁殖def select_population(self):indices = np.random.choice(np.arange(self.pop_size), size=(self.pop_size), replace=True, p=(self.normalized_fitness) / (self.normalized_fitness.sum()))self.population = self.population[indices]# 交叉操作,生成新个体def crossover(self, offspring):if np.random.rand() < self.cross_rate:parent = self.population[np.random.randint(self.pop_size)]for j in range(2):crossover_point = np.random.randint(low=0, high=self.dna_length)offspring[j][crossover_point:] = parent[j][crossover_point:]return offspring# 变异操作def mutate(self, offspring):if np.random.rand() < self.muta_rate:muta_dna = np.random.randint(0, 2)muta_point = np.random.randint(0, self.dna_length)offspring[muta_dna][muta_point] ^= 1return offspring# 对整个种群应用交叉和变异操作def apply_crossover_and_mutation(self):new_population = self.population.copy()for i in range(self.pop_size):new_population[i] = self.crossover(new_population[i])new_population[i] = self.mutate(new_population[i])return new_population# 获取当前种群中最优的个体def get_best_individual(self):best_fitness = float('inf')best_x, best_y = 0, 0for i in range(self.pop_size):current_fitness = self.objective_function(self.population[i])if best_fitness > current_fitness:best_fitness = current_fitnessbest_x = self.decode(self.population[i][0])best_y = self.decode(self.population[i][1])print(f"Best individual is ({best_x}, {best_y}), fitness is {best_fitness}")# 初始化遗传算法对象
ga = GeneticAlgorithm(n_min=-10, n_max=10, dna_length=16, pop_size=500, cross_rate=0.8, muta_rate=0.08)
generations = 10# 迭代执行遗传算法
for _ in range(generations):ga.apply_crossover_and_mutation()ga.select_population()ga.calculate_fitness()ga.get_best_individual()
输出如下:
自此入门成功......
下一篇:用遗传算法处理2024数学建模国赛C题(咕咕咕中......)
ps:由于这个函数过于简单,并没有体现进化的过程。但我懒得改了。
这是个更显著的代码:
import numpy as npclass GeneticAlgorithm:# 初始化方法,设置遗传算法的参数def __init__(self, n_min, n_max, dna_length, pop_size, cross_rate, muta_rate,dna_kind_size):self.n_min = n_min # 最小范围self.n_max = n_max # 最大范围self.dna_length = dna_length # DNA长度self.pop_size = pop_size # 种群大小self.cross_rate = cross_rate # 交叉概率self.muta_rate = muta_rate # 变异概率self.n_range = n_max - n_min # 范围差值self.dna_kind_size = dna_kind_sizeself.unit_interval = self.n_range / (2**dna_length) # 每个DNA单元对应的实际数值范围self.population = np.random.randint(0, dna_kind_size, size=(pop_size, dna_kind_size, dna_length)) # 随机初始化种群self.normalized_fitness = self.calculate_fitness() # 初始化适应度# 目标函数,需要最小化的函数def objective_function(self, individual):x = self.decode(individual[0]) # 解码第一个DNA片段y = self.decode(individual[1]) # 解码第二个DNA片段z = self.decode(individual[2]) return -((x - 5)**2 + (y - 3)**2 + (z + 2)**2) + 5 # 计算适应度值# 将二进制DNA片段解码为数值def decode(self, dna):result = 0for i in range(self.dna_length):result += dna[i] * (2**i)decoded_value = result * self.unit_interval + self.n_minreturn decoded_value# 计算适应度值并进行归一化def calculate_fitness(self):fitness = np.zeros(shape=(self.pop_size)) # 初始化适应度数组for i in range(self.pop_size):fitness[i] = self.objective_function(self.population[i]) # 计算适应度值max_fitness = np.max(fitness)min_fitness = np.min(fitness)normalized_fitness = (fitness - min_fitness) / (max_fitness - min_fitness + 0.00001) + 0.001self.normalized_fitness = normalized_fitnessreturn normalized_fitness# 选择适应度高的个体进行繁殖def select_population(self):print("normallized_fitness_sum is ",self.normalized_fitness.sum())indices = np.random.choice(np.arange(self.pop_size), size=(self.pop_size), replace=True, p=(self.normalized_fitness) / (self.normalized_fitness.sum()))self.population = self.population[indices]# 交叉操作,生成新个体def crossover(self, offspring):if np.random.rand() < self.cross_rate:parent = self.population[np.random.randint(self.pop_size)]for j in range(self.dna_kind_size):crossover_point = np.random.randint(low=0, high=self.dna_length)offspring[j][crossover_point:] = parent[j][crossover_point:]return offspring# 变异操作def mutate(self, offspring):if np.random.rand() < self.muta_rate:muta_dna = np.random.randint(0, self.dna_kind_size)muta_point = np.random.randint(0, self.dna_length)offspring[muta_dna][muta_point] ^= 1return offspring# 对整个种群应用交叉和变异操作def apply_crossover_and_mutation(self):new_population = self.population.copy()for i in range(self.pop_size):new_population[i] = self.crossover(new_population[i])new_population[i] = self.mutate(new_population[i])return new_population# 获取当前种群中最优的个体def get_best_individual(self):best_fitness = float('inf')best_x, best_y,best_z = 0, 0, 0for i in range(self.pop_size):current_fitness = self.objective_function(self.population[i])if best_fitness > current_fitness:best_fitness = current_fitnessbest_x = self.decode(self.population[i][0])best_y = self.decode(self.population[i][1])best_z = self.decode(self.population[i][2])print(f"Best individual is ({best_x}, {best_y}, {best_z}), fitness is {best_fitness}")# 初始化遗传算法对象
ga = GeneticAlgorithm(n_min=-10, n_max=10, dna_length=16, pop_size=10000, cross_rate=0.8, muta_rate=0.08,dna_kind_size = 3)
generations = 50# 迭代执行遗传算法
for _ in range(generations):ga.apply_crossover_and_mutation()ga.select_population()ga.calculate_fitness()ga.get_best_individual()