遗传算法——人工智能概论大作业
遗传算法在求解函数优化问题上的应用研究
一.算法介绍
1.1 遗传算法的基本思想
遗传算法把问题的解表示成 “ 染色体 ” ,在算法中即是以 一定方式编码的串 。并且,在执行遗传算法之前,给出 一群 “ 染色体 ” ,也即假设解(候选解)。然后,把这些假设解置于问题的 “ 环境 ” 中,并按 适者生存的原则 ,从中选择出较适应环境的 “ 染色体 ” 进行复制,再通过 交叉,变异 过程产生更适应环境的新一代 “ 染色体 ” 群。这样,一代一代地进化,最后就会收敛到最适应环境的一个 “ 染色体 ” 上,它就是问题的最优解。
1.2 遗传算法的基本概念
串:串是遗传算法GA(ongenetic algorithms)操作的基本对象,即个体形式,对应于遗传学中的 染色体(Chromosome),每个染色体是一个知识结构,代表求解问题的一个可能解,在遗传算法中为二进制串或者其它编码方式的串。
种群(Population): 个体的集合称为种群,串是种群的元素
种群规模(Population Size):在种群中个体的数量称为种群的规模。
基因(Gene):基因是串中的元素,基因用于表示个体的特征
适应度(Fitness): 表示某一个体对于环境的适应程度, 其值越大(适应度大), 相应染色体所代表的解越优。
1.3 算法实现的步骤
遗传算法是一类借鉴生物界的 进化规律--适者生存、优胜劣汰演 化而来的随机搜索
方法。它在计算机上进行实现时其 基本步骤可以概括为编码机制、种 群初始化、适应度函 数,遗传算子四部分,其中遗传算子 包括选择、交叉、变异等操作,用于模 拟自然界中种群优 胜劣汰的过程。遗传算法的具体实现流程图如图1 。
图1 遗传算法流程图
1.3.1 个体编码
用遗传算法解决问题时,首先要解决问题的是合理有效的对处理问题的描述,也就是要对问题的模型结构和参数进行编码,一般用字符串表示。编码机制是遗传算法的基础,
通过把处理对象统一赋于由特定符号(字母)按一定顺序排成的串(string)。正如研究生物遗传,是从染色体着手,染色体则是由基因排成的串。常见的编码方式有:二进制编码、浮点编码、格雷码编码、符号编码等。当遇到问题较为复杂时,也可以采取这些编码的混合编码。
1.3.2 种群的初始化
种群是包含若干个体的集合,每代遗传算法的进化都是以种群为单位。种群的初始化就是在选定编码方式后通过一定方式产生N个染色体,其中N为种群规模。 产生初始群体的方法通常有两种: 一种是完全随机的方法产生的, 适合于对问题的解无任何先验知识的情况; 另一种是将某些先验知识转变为必须满足的一组要求, 然后在满足这些要求的解中再随机地选取样本, t=0; 随机产生n个个体形成一个初始群体Q(t) , 该群体代表优化问题的一些可能解的集合。
1.3.3 适应度评价函数
在遗传算法的执行过程中许多不同的个体(染色体 ),保留哪个染色体,淘汰哪个染色体,需要引进对染色体进行度量的适应度函数。适应度函数用来评价每个染色体的对环境的适应能力,适应性强的个体有更多的机会保留下来。按编码规则, 将群体Q(t) 中的每一个个体的基因码所对应的自 变量取值(Xi, Yi, Zi) 代入目标函数, 算出其函数值Fi, i=1, 2, . . . , n, Fi越大, 表示该个体有较高的适应度, 更适合于f所定义的生存环境, 适应度Fi为群体进化提供了依据。
1.3.4 选择操作
选择算子从种群Q(t)中按某一概率Pi成对选择个体,作为双亲用于繁殖后代, 产生新的个体加入下一个群体Q(t+1) 中。某个体 xi被选择的概率 Pi与其适应度值成正比。最通常实现方法是轮盘赌 (roulette(roulette(roulette(roulette (roulettewheel)wheel) wheel) 模型 。
1.3.5 交叉操作
交叉算子就是将被选中的两个体的基因链按概率Pc进行交叉 ,交叉位置随机,生成两个新的个体。其中 Pc是一 个系统参数, Pc 太小时难以向前搜索 , 太大则容易破坏高适应值的结构 。 一般取 Pc=0.25-0.75。
1.3.6 变异操作
变异算子将新个体的基因链的各位按概率P m 进行变异 ,对二值基因链(0, 1 编码) ) 来说即是取反。变异概率 Pm 太小时难以产生新的基因结构 , 太大使遗传算法成了单纯的随机搜索 。 一般取Pm=0.1-0.2。
1.3.7终止条件判断
对产生新一代的群体返回步骤3在进行评价, 交叉, 变异如此循环往复, 使群体中个体的适应度和平均适应度不断提高, 直至最优个体的适应度达到某一限制或最有个体的适应度和群体的平均适应度不再提高, 则迭代过程收敛, 算法结束。
二.实验部分
如题,优化以下函数:
优化思路:由于轮盘算法只能做最大值优化的算法,所以就取适应度值的倒数作为新的适应度值,这样找出来的最大新适应度再倒数一下就是原适应度值的最小值
相关代码:
import math
import random
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcParams['font.sans-serif']=['SimHei']#设中文编码
##编码
# 计算编码位数
def get_length(start, end, precision): # 取值范围,编码精度
code_length = 0
code_length = math.log2((end - start) / precision + 1)
return code_length
# 计算编码精度
def get_precision(start, end, length):
precision = (end - start) / (pow(2, length) - 1)
return precision
# 十进制浮点数转化为二进制
def ten_two(num_float, code_length): # 浮点数,保留精度
num_int = int(num_float)
int_num = str(bin(num_int))[2:]
length = len(int_num)
code_length = code_length - length
if int(code_length) == 0:
return int_num
else:
for i in range(int(code_length)):
int_num = '0' + str(int_num)
return int_num
# 二进制转十进制
def two_ten(num):
num_str = str(num)
sum = 0
length = len(num_str)
for i in range(len(num_str)):
if num_str[i] == '1':
sum = sum + pow(2, length - i - 1)
return sum
# 编码
def encode(num, start, end, precision): # 未解码的数字,取值范围,精度
code_length = get_length(start, end, precision)
bit = ((num - start) * (pow(2, code_length) - 1) / (end - start))
Bit = ten_two(bit, code_length)
return Bit
# 解码
def decode(code, start, end, precision): # 码(不含0b),取值范围,精度
code_length = get_length(start, end, precision)
code = two_ten(code)
num = start + (end - start) / ((pow(2, code_length) - 1)) * code
return int(num*1000)/1000
# print(decode('0b01010101001110101',-5,5,get_precision(-5,5,17)))
# 初始化种群
def initialize_code(num1, start, end, length): # 初始化的个数,取值范围,长度
x1 = []
x2 = []
code1 = []
code2 = []
precision = get_precision(start, end, length)
for i in range(num1):
t1 = random.uniform(start, end)
t1 = int(t1*1000)/1000
x1.append(t1)
t2 = random.uniform(start, end)
t2 = int(t2*1000)/1000
x2.append(t2)
x1_encode = encode(t1, start, end, precision)
x2_encode = encode(t2, start, end, precision)
'''print("生成的随机数x1(十进制):" + str(t1))
print("生成的随机数x2(十进制):" + str(t2))
print("二进制编码x1:" + str(x1_encode))
print("二进制编码x2:" + str(x2_encode))'''
code1.append(x1_encode)
code2.append(x2_encode)
return x1, x2, code1, code2
# 计算适应度值
def get_fitness1(x1, x2):
f = pow(x1, 2) + pow(x2, 2)
return f,1/f
def get_fitness2(x1, x2):
f = abs(x1) + abs(x2) + abs(x1 * x2)
return f,1/f
def get_fitness3(x1, x2):
f = 2 * pow(x1, 2) + pow(x2, 2)
return f,1/f
def get_fitness4(x1, x2):
f = (-x1) * math.sin(math.sqrt(abs(x1))) -x2 * math.sin(math.sqrt(abs(x2)))
return f,1/f
def get_fitness5(x1, x2):
f = pow(x1, 2) - 10 * math.cos(2 * math.pi * x1) + 10 + pow(x2, 2) - 10 * math.cos(2 * math.pi * x2) + 10
return 1/f,f
def get_fitness6(x1, x2):
f = 20 * (1 - math.exp(-0.2 * math.sqrt(x1 * x1 + x2 * x2 / 2)) + math.exp(1) - math.exp(
((math.cos(2 * math.pi * x1) + (math.cos(2 * math.pi * x2) / 2)))))
return f,1/f
def get_x(code,start,end,precision):
code = np.array(code)
x=[]
length = code.shape[0]
for i in range(length):
x.append(decode(code[i],start,end,precision))
x = np.array(x)
return x
'''x1, x2, code1, code2 = initialize_code(2,-10,10,4)
print(x1)
x1 = get_x(code1,-10,10,get_precision(-10,10,4))
print(x1)'''
# 拼接矩阵
def match(code1, code2):
code1 = np.array(code1)
code2 = np.array(code2)
code = np.empty(code1.shape[0])
code = np.vstack((code1, code2))
return code
# 显示
def show(code1, code2):
code1 = np.array(code1)
code2 = np.array(code2)
for i in range(code1.shape[0]):
print('第%d个染色体:%s%s' % (i + 1, code1[i], code2[i]))
# 配对选择操作-轮盘法
def Roulette(x1, x2, code1, code2):
x1 = np.array(x1)
x2 = np.array(x2)
f = np.zeros(x1.shape[0])
F = np.zeros(x1.shape[0])
max = 0
Max = 0
fm = np.zeros(f.shape[0])
r = np.zeros(f.shape[0])
sum = 0
for i in range(x1.shape[0]):
F[i],f[i] = get_fitness6(x1[i], x2[i]) # 求适应度值
if f[i] > max:
Max = F[i]
max = f[i]
sum = sum + f[i]
for i in range(f.shape[0]):
f[i] = f[i] / sum # 求被选择概率
'''print('第%d个数概率为:%f' % (i, f[i]))'''
for j in range(i + 1):
fm[i] = f[j] + fm[i] # 求累计概率
num = []
for i in range(r.shape[0]):
r[i] = random.uniform(0, 1)
j = 0
while fm[j] < r[i]:
j = j + 1
num.append(j)
y1 = []
y2 = []
c1 = []
c2 = []
num = np.array(num)
for i in range(num.shape[0]):
y1.append(x1[num[i]])
y2.append(x2[num[i]])
c1.append(code1[num[i]])
c2.append(code2[num[i]])
'''print('轮盘法筛出的第%d个:%s%s,原编号为%d' % (i, code1[num[i]], code2[num[i]], num[i]))'''
y1 = np.array(y1)
y2 = np.array(y2)
c1 = np.array(c1)
c2 = np.array(c2)
print('当前集群中个体最大适应值为:',str(max))
print('对应的F值为:%s',str(Max))
return y1, y2, c1, c2,max,Max
# 交叉操作
def exchange(code1, code2, x, length, flag):
code1 = list(code1)
code2 = list(code2)
if flag == 1:
for i in range(x, length):
t = code1[i]
code1[i] = code2[i]
code2[i] = t
else:
for i in range(length):
t = code1[i]
code1[i] = code2[i]
code2[i] = t
code1 = ''.join(code1)
code2 = ''.join(code2)
return code1, code2
def recombination(r, code1, code2, start, end, length, precision): # 交叉概率,编码,范围
j = []
code1 = np.array(code1)
code2 = np.array(code2)
for i in range(int(code1.shape[0] / 2)):
rm = random.uniform(0, 1)
if rm < r:
j.append(i) # 确定重组的染色体组
j = np.array(j)
for i in range(j.shape[0]):
position = j[i]
def combination(code1,code2,start,end,length,position):
rm = random.uniform(1, length * 2)
if rm - length < 1 and rm > length:
rm = rm + 1
if (rm < length):
code2[position * 2], code2[position * 2 + 1] = exchange(code2[position * 2], code2[position * 2 + 1],
length, length, 2)
code1[position * 2], code1[position * 2 + 1] = exchange(code1[position * 2], code1[position * 2 + 1],
int(rm), length, 1)
else:
code2[position * 2], code2[position * 2 + 1] = exchange(code2[position * 2], code2[position * 2 + 1],
int(rm - length), length, 1)
if (decode(code2[position * 2], start, end, precision) < start
or decode(code2[position * 2], start, end, precision) > end
or decode(code2[position * 2 + 1], start, end, precision) < start
or decode(code2[position * 2 + 1], start, end, precision) > end
or decode(code1[position * 2], start, end, precision) < start
or decode(code1[position * 2], start, end, precision) > end
or decode(code1[position * 2 + 1], start, end, precision) < start
or decode(code1[position * 2 + 1], start, end, precision) > end
):
code1,code2 = combination(code1,code2,start,end,length,position)
return code1,code2
code1,code2 = combination(code1,code2,start,end,length,position)
return code1, code2
#c1, c2 = recombination(0.99, code1, code2, -50, 50, 3, get_precision(-50, 50, 3))
# 变异操作
def reserve(code, position): # 编码,变异的位置
code = list(code)
if code[position] == '0':
code[position] = '1'
else:
code[position] = '0'
return ''.join(code)
def variation(r, code1, code2): # 概率,种群
code1 = np.array(code1)
code2 = np.array(code2)
length = len(code1[0]) + len(code2[0])
for i in range(code1.shape[0]):
for j in range(length):
rd = random.uniform(0, 1)
if rd < r:
if (j < length / 2):
code1[i] = reserve(code1[i], j)
'''print('第%d 个染色体上的第%d位发生了基因突变' % (i + 1, j + 1))'''
else:
code2[i] = reserve(code2[i], int(j - length / 2))
'''print('第%d 个染色体上的第%d位发生了基因突变' % (i + 1, j + 1))'''
return code1, code2
# 环境选择
def test(N,M,start,end,length,Pc,Pm):#种群规模,进化代数,范围,编码长度,交叉概率,变异概率
x1, x2, code1, code2 = initialize_code(N,start, end, length)
precision = get_precision(start, end, length)
f = []
for i in range(M):
print('当前为第%d代:' %i)
i=i+1
x1,x2,code1,code2,max,Max=Roulette(x1,x2,code1,code2)
f.append(Max)
code1,code2 = variation(Pm,code1,code2)
code1,code2 = recombination(Pc,code1,code2,start,end,length,precision)
x1=get_x(code1,start,end,precision)
x2=get_x(code2,start,end,precision)
f = np.array(f)
return f
def drew(f):
# 设置画布
plt.figure(dpi=80, figsize=(10, 8))
x = range(0,100)
# 画图
plt.plot(
x,f, c='y',
)
# 设置网格
plt.grid(linewidth=1, alpha=1)
plt.title('进化')
plt.xlabel('进化代数')
plt.ylabel('函数值')
# 显示
plt.show()
f = test(100,100,-100,100,30,0.8,0.1)
drew(f)
运行结果(第一个函数为例):

为单峰函数,
在第25代时达到最小值0。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)