遗传算法求解最大值问题详解(附python代码)

问题:求 y = x sin 10 x + x cos 2 x y=x\sin{10x}+x\cos{2x} [ 0 , 5 ] [0, 5] 上的最大值。
解题步骤:
1、随机生成种群 p o p pop ,每个种群中含有 100 100 条染色体。每个染色体上含有10个基因(以二进制表示自变量,故每条染色体上表示的数字范围是 [ 0 , 1023 ] [0, 1023] )。
2、计算这些染色体的适应度,适应度大的在下一步被选到的概率大。
3、依概率从100条染色体中有放回地抽取100条染色体(有重复染色体),令这新的100条染色体组成新的种群 p o p n e w pop_{new}
4、将 p o p n e w pop_{new} 复制为 p o p c o p y pop_{copy} 来表示另一条父染色体用来与 p o p n e w pop_{new} 中的染色体进行交叉配对。
5、从 p o p n e w pop_{new} 中选出第一条染色体进行交叉检验。
6、假设交叉的概率为 0.8 0.8 ,则 p o p n e w pop_{new} 中的每条染色体都有 80 80% 的概率需要交叉,假设第一条染色体是要进行交叉的。(若未被选中则继续第二条染色体的交叉检验,直到遍历完 p o p n e w pop_{new} 中所有染色体)
7、随机在 p o p c o p y pop_{copy} 中选择一条染色体与 p o p n e w pop_{new} 中第一条染色体进行交叉,假设选中 p o p c o p y pop_{copy} 中第三条染色体。
8、选择 p o p n e w pop_{new} 中第一条染色体上需要改变的基因是哪些,假设为前三个基因。
9、将 p o p c o p y pop_{copy} 中第三条染色体上的前三个基因取出放到 p o p n e w pop_{new} 中第一条染色体上的前三个位置。交叉完毕,得到一条子染色体 c h i l d child
10、对 c h i l d child 进行编变异操作,假设变异概率为 0.003 0.003
11、依次对 c h i l d child 上所有基因进行变异检验,如果某基因需要变异,则将此处位置的数字取反( 0 1 1 0 0\rightarrow1或1\rightarrow0 )。
12、用经过了交叉变异的 c h i l d child 代替 p o p n e w pop_{new} 中的父染色体(即第一条染色体)。
13、重复以上操作,遍历完 p o p n e w pop_{new} 中所有染色体后,第一次种群更新完成。
以下为 p y t h o n python 代码:

import numpy as np

DNA_SIZE = 10  # DNA length
POP_SIZE = 100  # population size
CROSS_RATE = 0.8  # mating probability (DNA crossover)
MUTATION_RATE = 0.003  # mutation probability
N_GENERATIONS = 200
X_BOUND = [0, 5]  # x upper and lower bounds

def F(x): 
    return np.sin(10*x)*x + np.cos(2*x)*x     # to find the maximum of this function
    
def get_fitness(pred):
    return pred - np.min(pred) + 1e-3  # 防止出现非正值,因为 select 时概率非负。
    
def translateDNA(pop):
    # np.arange(DNA_SIZE)[::-1] --> array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
    # 2 ** np.arange(DNA_SIZE)[::-1] --> array([512, 256, 128,  64,  32,  16,   8,   4,   2,   1], dtype=int32)
    # pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) --> 将二进制转换为十进制
    # (2 ** DNA_SIZE - 1) --> 1023
    # pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / (2 ** DNA_SIZE - 1) --> 计算染色体值占最大值(1023)的比例
    # pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / (2 ** DNA_SIZE - 1) * X_BOUND[1] --> 将范围从[0, 1023]缩放到[0, 5]
    
    return pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / (2 ** DNA_SIZE - 1) * X_BOUND[1]
    
def select(pop, fitness):
    # 在 np.arange(POP_SIZE) 中依概率 p 选出 size 个染色体代号
    # replace=True 表示选出的染色体可以有重复的
    idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True, p=fitness / fitness.sum())
    return pop[idx]
    
def crossover(parent, pop):
    if np.random.rand() < CROSS_RATE:
        i_ = np.random.randint(0, POP_SIZE, size=1)  # 选定 pop 中需要交叉的染色体,如 i_ = 1,表示第二条染色体
        
        # 选择需要改变的基因,如 cross_points = [False  True  True False False  True False  True  True False]
        # 表示第2,3,6,8,9个位置上的基因会改变
        cross_points = np.random.randint(0, 2, size=DNA_SIZE).astype(np.bool)
        
        # pop[i_, cross_points] 会得到 pop 种群中 i_ 这条染色体上的 cross_points 中 True 位置上的基因
        # parent[cross_points] = pop[i_, cross_points] 将刚刚得到的基因放到 parent 这条染色体的 cross_points 中 True 的位置上
        parent[cross_points] = pop[i_, cross_points]
    return parent
    
def mutate(child):
    for point in range(DNA_SIZE):
        if np.random.rand() < MUTATION_RATE:
            child[point] = 1 if child[point] == 0 else 0
    return child
    
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE))   # initialize the pop DNA
print(pop.shape)

for _ in range(N_GENERATIONS):
    F_values = F(translateDNA(pop))    # compute function value by extracting DNA

    # GA part (evolution)
    fitness = get_fitness(F_values)
    print("Most fitted DNA: ", pop[np.argmax(fitness), :], "适应度为:", round(fitness[np.argmax(fitness)], 3))
    pop = select(pop, fitness)
    pop_copy = pop.copy()
    for parent in pop:
        child = crossover(parent, pop_copy)
        child = mutate(child)
        parent = child       # 用经过交叉变异的下一代(新的染色体)来代替上一代(pop 种群中选出的)
print('最大值为:', F(translateDNA(pop[np.argmax(fitness), :])))
发布了36 篇原创文章 · 获赞 1 · 访问量 567

猜你喜欢

转载自blog.csdn.net/qq_36758914/article/details/103332484
今日推荐