由于课程即研究方向需要,简单实现一些关于智能优化的算法。
算法过程实现方式比较简单,没有用比较复杂的语句并且对代码进行了注释,比较适合小白。
遗传算法(Genetic Algorithm,GA)是一种基于生物进化思想的优化算法,它通过模拟生物进化过程中的遗传、交叉、变异和选择等基本操作来不断优化种群,从而实现对问题的求解。广泛应用于函数优化、组合优化、路径规划、机器学习等领域。遗传算法的基本思想是:将问题的解表示为染色体形式,通过基因重组、突变等操作来产生新的染色体,并以适应度函数来衡量每个染色体的适应性,然后根据适应度大小进行选择、交叉和变异等操作,最终得到更优的染色体,进而得到更优的解。遗传算法的基本流程如下:
- 初始化:随机生成初始种群,每个个体表示一个可能的解。
- 适应度函数:根据问题的特点,定义适应度函数,用于衡量每个个体的适应性。
- 选择:根据适应度函数的值,选择适应性较好的个体,作为下一代种群的基础。
- 交叉:对选出的个体进行交叉操作,产生新的个体。
- 变异:对新产生的个体进行变异操作,引入新的基因,增加种群多样性。
- 终止条件:当达到预定的终止条件时,停止算法,并返回最优解。遗传算法具有较好的全局搜索能力和高效的并行性,能够自适应地学习和演化规则,适用于复杂问题的求解。但也存在一些问题,如可能陷入局部最优解、需要大量的计算资源和时间等。因此,在使用遗传算法时,需要根据具体问题来选择适当的参数和操作,以获得更好的性能和效果。
由于只是简单实现,所以适应度函数f(x)=x^2.
选择是采用轮盘赌的方式
import math
import random
# 遗传算法的参数
M = 100 # 种群数量
T = 50 # 迭代次数
# 变异概率是个范围,因此用两个值表示
Pm1 = 0.001
Pm2 = 0.01
# 交叉概率
Pc1 = 0.4
Pc2 = 0.9
NUM_VARIABLES = 20 # 变量数量
MIN_VALUE = 0 # 变量最小值
MAX_VALUE = 1023 # 变量最大值
NUMBER_BINARY = math.ceil(math.log2(MAX_VALUE)) # 二进制位数,需要通过变量最大值来对2取对数然后通过ceil方法向上取整
SELECT_NUMBERS = 30 # 选择次数
# 生成随机个体
#这里生成个体采用三维数组的方式,总共有100个个体 分为了5组 每组20个个体,每个个体的第一个位置放的是他的值,后面10位放的是该值的二进制数
#因为这样在后面的交叉与变异后有助于更新他的值
def generate_individual():
array = []
for k in range(M // NUM_VARIABLES):
col = []
for i in range(NUM_VARIABLES):
row = []
row.append(random.randint(MIN_VALUE, MAX_VALUE))
# 采用bin方法将得到的整数转化为二进制数,[2:]表示去掉前缀0b
binary = bin(row[0])[2:]
"""
将二进制数转化为10位固定长度的二进制数,
其中,字符串格式化函数"{:0>8}"表示将字符串填充到8位长度,不足部分用0补齐,
">"表示右对齐,"0"表示用0填充。
"""
fixed_binary = "{:0>10}".format(binary)
for j in range(NUMBER_BINARY): # 其实总共有11位 但前面已经将整数值添加到一个元素位置了
row.append(int(fixed_binary[j]))
col.append(row)
array.append(col)
print(array)
return array
# 计算个体的适应度
def fitness(individual):
return individual ** 2;
"""
交叉操作
还是需要从几组中选择一组,然后对该组中的个体进行操作,
操作完成之后需要更新种群,最终返回的是一个种群
"""
def crossover(population1):
# 首先从种群中选择一组
num = random.randint(0, M // NUM_VARIABLES - 1)
print("选择种群组数为: ")
print(num)
population = population1[num]
print(population)
# 随机选择其中的两个个体进行交叉操作
num1 = random.randint(0, NUM_VARIABLES - 1)
num2 = random.randint(0, NUM_VARIABLES - 1)
while num1 == num2:
num2 = random.randint(0, NUM_VARIABLES - 1)
parent1 = population[num1]
parent2 = population[num2]
print("选择个体1: ", parent1)
print("选择个体2: ", parent2)
# 随机生成一个在交叉概率范围内的数 然后在这个位置进行交叉
cross = random.randint(int(Pc1 * NUMBER_BINARY), int(Pc2 * NUMBER_BINARY))
print("交叉位置: ", cross)
# 将两个个体的前cross个染色体进行交换
for i in range(1, cross + 1): # 因为个体存染色体是从第一个位置开始的,第0个位置是他的值
temp = parent1[i]
parent1[i] = parent2[i]
parent2[i] = temp
binary1 = ''
binary2 = ''
# 交换完成后要更新个体的值
for j in range(1, len(parent1)):
binary1 += str(parent1[j])
binary2 += str(parent2[j])
# 将二进制转化为十进制数并放入个体中的第0个位置
parent1[0] = int(binary1, 2)
parent2[0] = int(binary2, 2)
print("交叉后的个体1: ", parent1)
print("交叉后的个体2: ", parent2)
# 最后更新种群
population[num1] = parent1
population[num2] = parent2
population1[num] = population
# 返回交叉后的种群
return population1
# 变异操作
# 变异概率决定了要变异几次(即染色体变异的个数)
def mutate(population1):
# 首先根据变异概率确定变异次数 因为总共有100个个体 每个个体有10染色体
num = random.randint(M * NUMBER_BINARY * Pm1, M * NUMBER_BINARY * Pm2)
while num > 0:
# 随机选择种群中的一组中的一个个体的一个染色体
num1 = random.randint(0, M // NUM_VARIABLES - 1)
num2 = random.randint(0, NUM_VARIABLES - 1)
individual = population1[num1][num2]
num3 = random.randint(1, NUMBER_BINARY)
print("被选中的个体为: ", individual)
print("染色体变异位置: ", num3)
# 将该染色体制反(即若为1 则置为0 反之)
if (individual[num3] == 0):
individual[num3] = 1
else:
individual[num3] = 0
# 更新该个体的值
binary1 = ''
# 交换完成后要更新个体的值
for j in range(1, len(individual)):
binary1 += str(individual[j])
individual[0] = int(binary1, 2)
print("个体变异后的值: ", individual)
# 更新种群
population1[num1][num2] = individual
# 循环次数-1
num = num - 1
return population1
# 选择操作
"""
选择操作是先算出每一个个体的适应度,然后根据适应度算出累积概率。
算出累积概率后,随机生成一个0-1之间的小数 判断其在哪个范围内。
选择次数是根据
"""
def selection(population1):
# 随机选择种群数量中的一组 这里每一组为20个物种
num = random.randint(0, M // NUM_VARIABLES - 1)
print("选择种群组数为: ", num)
population = population1[num]
print(population)
fitnesses = [fitness(individual[0]) for individual in population]
print("每个个体的适应度为: ", fitnesses)
min_fitness = min(fitnesses)
max_fitness = max(fitnesses)
print("最大适应度为: ", max_fitness)
total_fitness = sum(fitnesses)
cumulative_probability = [] # 累积概率
cul = 0
for i in range(len(fitnesses)):
cul += fitnesses[i]
cumulative_probability.append(cul / total_fitness)
print(cumulative_probability)
select = [] # 用于存放不同个体被选择的次数
for b in range(NUM_VARIABLES):
select.append(0)
# 选择次数(这里采用轮盘赌选择方式)
for s in range(SELECT_NUMBERS):
probability = random.random() # 随机生成一个概率
# 判断这个概率在哪个之间 则该个体的选择次数加1
for p in range(NUM_VARIABLES):
if probability < cumulative_probability[p]:
select[p] = select[p] + 1
break
print(select)
# 表格头部
print('物种适应度\t选择次数')
# 表格内容
for ani, sel in zip(fitnesses, select):
print(f'{ani}\t\t{sel}')
# 主程序
population = generate_individual()
print("初始种群为: ", population)
print()
# 选择该种群数值最大的一个
max1 = 0
for i in range(M // NUM_VARIABLES):
for j in range(NUM_VARIABLES):
if (population[i][j][0] > max1):
max1 = population[i][j][0]
print("该种群个体数值最大为: ", max1)
# 选择
selection(population)
for i in range(T): # 迭代T次
print("迭代次数: ", i)
croPopulation = crossover(population) # 交叉操作
mutPopulation = mutate(croPopulation) # 变异操作
population = mutPopulation
print()
# 选择该种群数值最大的一个
max = 0
for i in range(M // NUM_VARIABLES):
for j in range(NUM_VARIABLES):
if (population[i][j][0] > max):
max = population[i][j][0]
print("该种群个体数值最大为: ", max)
结果展示:
总共迭代了50次中间迭代结果就不给出了,如果想要看,自己运行即可。
一般经过交叉变异之后,该种群都会变得比较好,这里好的定义就是 该种群中个体的适应度值,最初最大是1001,经过交叉变异之后1020.