人工智能||遗传算法(GA)求解函数极值问题

一、实验目的

(1)熟悉遗传算法的基本进化思想和基本流程;

(2)理解遗传算法中遗传算子(选择、交叉、变异)的设计思想;

(3)重点编程实现利用基本遗传算法求解连续优化问题——二元函数极值或最值问题。

二、编程环境

Python语言、Matlab语言、C语言等。

三、问题描述与算法设计

(1)问题1:求二元函数的最大值。

图像如下图所示,显然函数存在多个极值。

问题2:求二元函数的最大值。

图像如下图所示,显然函数存在多个极值。

2)参数值:

初始化种群数目:NP自定

染色体二进制编码长度:L=自定

最大进化代数:G自定

交叉概率:Pc =0.8

变异概率:Pm =0.1

自变量x的定义域上限:Xs=7

自变量x的定义域:Xx=0

3)编程中的关键问题:

(a) 编码。每个染色体编码成长度为6的二进制串,比如101011。(这里地编码,大家可以重新设计,自由发挥。)

(b) 初始种群。初始种群随机产生,种群规模为40个。

(c) 解码。将二进制数串解码为十进制数

(d) 适应度值。因为求解的是最大值,所以适应度值就是将自变量代入函数后的函数值。

(e) 交叉。交叉采用两点交叉。

(f) 变异。变异采用单点、取反变异

(g) 选择。取上一代种群和交叉变异后的种群的最好的一半。

四、基本遗传算法的一般步骤

第一步:使用随机方法产生一个有NP个染色体的初始种群;

第二步:计算种群中每一个染色体的适应度值,即函数值;

第三步:若达到最大迭代次数G,则算法终止;否则,继续迭代;

第四步:按照概率执行两点交叉操作。将种群分为奇偶两个子种群,即种群中的第1、3、…等归为奇子群,其余为偶子群;然后将两个子种群中的对应个体按照概率Pc执行两点交叉。

第五步:按照概率执行单点变异操作。即在一个染色体中随机选择一个基因位置,若为0则变为1,若为1则变为0;

第六步:将交叉变异前与后的种群,混合在一起,选择适应度值最大的一半,作为下一代种群。

第七步:返回第三步。

五、源代码

# 种群数
population_size=40
generations=200
chorm_length=6
# 交配概率
pc=0.8
# 变异概率
pm=0.1
# 基因编码
genetic_population=[]
# 种群对应的【0,10】
population=[]
fitness=[]
fitness_mean=[]
# 每次迭代所获得的最优解
optimum_solution=[]
bestx_list=[]
besty_list=[]
# 对染色体进行0,1编码,生成初始种群
def chrom_encoding():
    for i in range(population_size):
        population_i=[]
        for j in range(2*chorm_length):
            population_i.append(random.randint(0,1))
#         print(population_i)
        genetic_population.append(population_i)
# 解码
def chorm_decoding():
    population.clear()
    for i in range(population_size):
        s=[]
        valuex=0
        valuey=0
        for j in range(chorm_length):
            valuex+=genetic_population[i][j]*(2**(chorm_length-1-j))
        for j in range(chorm_length,chorm_length*2):
            valuey+=genetic_population[i][j]*(2**(chorm_length*2-1-j))
        s.append(valuex*10/(2**(chorm_length)-1))
        s.append(valuey*10/(2**(chorm_length)-1))
        population.append(s)
#     print(population)
def problem_function(x, y):
    sumx = 0
    sumy = 0
    for i in range(1,6):
        sumx = sumx + (i*math.cos((i+1)*x+i))
        sumy = sumy + (i*math.cos((i+1)*y+i))
    return sumx*sumy
def calculate_fitness():
    sum=0.0
    fitness.clear()
    for i in range(population_size):
        function_value=problem_function(population[i][0],population[i][1])
        if function_value>0.0:
            sum+=function_value
            fitness.append(function_value)
        else:
            fitness.append(0.0)
    return sum/population_size,fitness
def best_value():
    max_fitness=fitness[0]
    max_chorm=0
    for i in range(population_size):
        if fitness[i]>max_fitness:
            max_fitness=fitness[i]
            max_chorm=i
    return max_chorm,max_fitness,population[max_chorm][0],population[max_chorm][1]
# 轮盘赌
def selection():
    fitness_array = np.array(fitness)
    new_population_id = np.random.choice(np.arange(population_size), (population_size,),
                                         replace=True, p=fitness_array/fitness_array.sum())
    new_genetic_population = []
    global genetic_population
    for i in range(population_size):
        new_genetic_population.append(genetic_population[new_population_id[i]])
    genetic_population = new_genetic_population
#     交叉采用两点交叉
def crossover():
    for i in range(0,population_size-1,2):
        if random.random()<pc:
            change_pointa=random.randint(0,2*chorm_length-1)
            change_pointb=random.randint(change_pointa,2*chorm_length-1)
            temp1=[]
            temp2=[]
            temp1.extend(genetic_population[i][0:change_pointa])
            temp1.extend(genetic_population[i+1][change_pointa:change_pointb])
            temp1.extend(genetic_population[i][change_pointb:])
            temp2.extend(genetic_population[i+1][0:change_pointa])
            temp2.extend(genetic_population[i][change_pointa:change_pointb])
            temp2.extend(genetic_population[i+1][change_pointb:])
            genetic_population[i] = temp1
            genetic_population[i+1] = temp2
#    变异
def mutation():
    for i in range(population_size):
        if random.random()<pm:
            mutation_point=random.randint(0,2*chorm_length-1)
            if genetic_population[i][mutation_point]==0:
                genetic_population[i][mutation_point]=1
            else:
                genetic_population[i][mutation_point]=0
# 开始迭代
xy=[]
z=[]
chrom_encoding()
for step in range(generations):
    chorm_decoding()
    fit_mean,fitness=calculate_fitness()
    fit_mean,fitness=calculate_fitness()
    best_id,best_fitness,bestx,besty=best_value()
    optimum_solution.append(best_fitness)
    bestx_list.append(bestx)
    besty_list.append(besty)
    fitness_mean.append(fit_mean)
    if step==1:
        xy=population
        z=fitness
    selection()
    crossover()
    mutation()
# 最优解随迭代次数的变化
fig1 = plt.figure(1)
plt.plot(range(1, generations + 1), optimum_solution)
plt.xlabel('迭代次数', fontproperties='SimHei')
plt.ylabel('最优解', fontproperties='SimHei')

# 平均适应度随迭代次数的变化
fig2 = plt.figure(2)
plt.plot(range(1, generations + 1), fitness_mean)
plt.xlabel('迭代次数', fontproperties='SimHei')
plt.ylabel('平均适应度', fontproperties='SimHei')
plt.show()


class Ga:
    def __init__(self,NP=100,L=20,G=10,Pc=0.8,Pm=0.1,Xs=10,Xx=0,Ys=10,Yx=0,is_max=True) -> None:
        self.NP = NP # 种群数目
        self.L = L # 染色体编码长度
        self.G = G # 最大进化代数
        self.Pc= Pc # 交叉概率
        self.Pm = Pm # 变异概率 
        self.Xs = Xs # x边界
        self.Xx = Xx
        self.Ys = Ys # y边界
        self.Yx = Yx
        self.choose = []
        self.is_max = is_max # 是否求最大值
    # 编码
    def generate_population(self):
        l = 2
        '''产生种群 已经编码好 0 1编码长度 L'''
        x = np.random.randint(2, size=(self.NP, self.L))
        y = np.random.randint(2, size=(self.NP, self.L))
        # return np.array([np.random.randint(2, size=(NP, L)) for i in range(l)])
        return x,y
    # 解码函数
    def decode(self,x,y):
        # 进行解码
        X = x.dot(2**np.arange(self.L)[::-1])/(2**self.L-1)*(self.Xs-self.Xx)+self.Xx
        Y = y.dot(2**np.arange(self.L)[::-1])/(2**self.L-1)*(self.Ys-self.Yx)+self.Yx
        return X,Y
    # 自定义函数
    def f(self,x,y):
        return x**2+y**2
    # 自定义适应度,求最大值和最小值
    def fitness(self,x,y):
        re = self.f(x,y)
        # 由于结果可能为负值 但是再下面的选择阶段不允许出现负值,需要进行处理
        # 0值相当于概率0,所以需要加个非常小的正数
        if self.is_max: # 选最大值
            re = re - np.min(re) + 1e-5
        else:
            re = np.max(re) - re + 1e-5
        return re
    def select_population(self,pre_x,pre_y,x,y):
        x_ ,y_ =self.decode(x,y)
        result = self.fitness(x_,y_)
        # 挑选适应度高的个体 规模为原始的一半////轮盘赌
#         抽取序号
        index = np.random.choice(np.arange(self.NP),replace=True,size=self.NP,p=result/result.sum())

        # pre_x_ ,pre_y_ =self.decode(x,y)
        # result = self.fitness(pre_x_ ,pre_y_,True)
        # index_pre = np.random.choice(np.arange(self.NP),replace=True,size=self.NP//2,p=result/result.sum())

        # # 取上一代种群和交叉变异后的种群的最好的一半
        # selected_x =np.concatenate([x[index], pre_x[index_pre]], axis=0)
        # selected_y =np.concatenate([y[index], pre_y[index_pre]], axis=0)
        selected_x = x[index]
        selected_y = y[index]
        self.choose.append(self.decode(selected_x,selected_y))
        return selected_x,selected_y
    def cross_mutation(self,x,y):
        father = []
        father.extend(x)
        father.extend(y)
        children = []
        for i in father:
            child = i
            # 交叉操作
            if np.random.rand() < self.Pc:
                # 产生随机交叉点
                point = np.random.randint(self.L)
                # 选取母节点
                # print(point)
                child[point:] = i[point:]
            # 变异操作
            if np.random.rand() < self.Pm:
                point = np.random.randint(self.L)
                child[point]^=1 # 取反
            children.append(child)
        return np.array(children[:len(children)//2]),np.array(children[len(children)//2:])

    def population_iteration(self):
        self.process = []
        x,y = self.generate_population()
        # x , y= decode(x,y)
        for i in range(self.G):
            pre_x,pre_y = x , y
            x,y = self.select_population(pre_x,pre_y,x,y)
            x,y = self.cross_mutation(x,y)
            
            a,b =self.decode(x,y)
            self.process.append(self.f(a,b).sum())

        self.x ,self.y= self.decode(x,y)
        self.plot_3d()
        self.plot_coss()
    def plot_3d(self):
        
        fig = plt.figure(figsize=(10,7))
        ax = Axes3D(fig)
        X = np.linspace(self.Xs,self.Xx,100)
        Y = np.linspace(self.Ys,self.Yx,100)
        X,Y = np.meshgrid(X, Y)
        Z = self.f(X, Y)
        ax.plot_surface(X,Y,Z,rstride=1, cstride=1, cmap='rainbow')
        # ax.set_zlim(-10,10)
        ax.set_xlabel('x')
        ax.set_ylabel('y')
        ax.set_zlabel('z')

        ax.scatter(self.x,self.y,self.f(self.x,self.y),c='black')
        plt.title("result")
        plt.show()
    def plot_coss(self):
        plt.plot(range(len(self.process)),self.process)
        plt.title("Total Population Values")
        plt.show()  

六、实验结果

第一个函数

第二个函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值