Improved genetic algorithm (IGA) + python code implementation

This article is a continuation of my last article on the advanced understanding of genetic algorithm python + paper reproduction (pure dry goods, with predecessors' summary and guidance) . First of all, I declare that brothers and sisters must have a certain understanding of genetic algorithms before reading this article.

Not much nonsense, this article improves the genetic algorithm based on the existing literature, but what is given in the literature is only literal logic and some pseudocode, and this article will reproduce it with python and test it with use cases.

IGA is from easy to difficult. I will introduce the improvement of mutation probability , crossover probability and fitness function in turn .

Note: The test cases in this article are all from the papers reproduced in my previous article Genetic Algorithm Python Advanced Understanding + Paper Reproduction (pure dry goods, with predecessors’ summary and guidance). The meaning and settings of various parameters are in the previous article are described in detail.

1. Improvement of mutation probability

(1) Single point mutation

First of all, let’s recall the most commonly used basic bit mutation operator : Whether the individual’s DNA mutates and the location of the mutation is randomly generated:

def mutation(child, MUTATION_RATE=0.005):
    if np.random.rand() < MUTATION_RATE:  # 以MUTATION_RATE的概率进行变异
        mutate_point = np.random.randint(0, DNA_SIZE * 4)  # 随机产生一个实数,代表要变异基因的位置
        child[mutate_point] = child[mutate_point] ^ 1  

The above paragraph is easy to understand, and many articles explain it. Next, let's look at the design idea of ​​crossover probability in reference 1 1 , which is essentially a multi-point mutation :

(2) Multi-point mutation

The specific process of "parthenogenesis" is as follows: Randomly generate a reference bit N (1≤N≤80) of mutation (the so-called mutation is to flip some bits of the binary string, that is, 0 becomes 1 or 1 becomes 0); Randomly generate the reference starting position P of the variation (1≤P≤79); determine the starting and ending positions a and b of the variation (obviously a≤b), if N≥P, then a=0, b=P-1; if N <P, then a=N, b=P. In this way, k bits (k=b-a+1) need to be mutated in the binary string representing the optimal individual. The new population contains the same number of individuals as the previous generation population.

Writing down its logic in the above passage is actually the following steps:
insert image description here
Therefore, unlike the single-point mutation above, this method specifies an interval [a,b], and all points in this small interval are mutated , a total of k points (k=b-a+1) were mutated. The program is written as:

# 参考文献1中的多点变异
def mutation2(child, MUTATION_RATE=0.005):
    N = np.random.randint(1, 81)  # 1≤N≤80
    P = np.random.randint(1, 80)  # 1≤P≤79
    a = 0
    b = 0
    if N >= P:
        a = 0
        b = P - 1
    else:
        a = N
        b = P
    for i in range(a, b + 1):
        child[i] = child[i] ^ 1

(3) Selective mutation probability

This method comes from reference 2 2 , first look at the method description in the article:
insert image description here
the above is the pseudocode of the improved mutation algorithm. First, the simple mutation sets a relatively large mutation rate (0.025). When the generated random number is less than the mutation rate, any gene on the chromosome of the incoming child will be mutated. In this paper, we improve this algorithm by setting a small mutation rate and then selectively mutating each gene on the chromosome of the incoming child. That is, when the generated random number is less than the mutation rate, the gene mutates. When traversing gene positions greater than half the length of the chromosome, the mutation rate is set to double (genes in the lower half have relatively little effect on the results) . This ensures that the first half of the gene and the second half of the gene have the same chance of mutation respectively and can be mutated at the same time.

Write it out with a program:

def mutation_develop(child, MUTATION_RATE=0.005):  
# 选择性地突变概率,即当遍历的基因位置大于染色体长度的一半时,设置突变率为原来的a两倍
    mutate_point = np.random.randint(0, DNA_SIZE * 4)  #随机产生变异基因的位置
    if mutate_point >= DNA_SIZE / 2:
        MUTATION_RATE = MUTATION_RATE * 2
    if np.random.rand() < MUTATION_RATE:  
        child[mutate_point] = child[mutate_point] ^ 1 

After compiling the program, we modify the corresponding position of the code to the mutation probability operator we want, and we can run out.

2. Improvement of crossover probability

The second is the improvement of the crossover probability. This method is still derived from reference 2 2 . First look at the method description:
!](https://img-blog.csdnimg.cn/53a8c79dcfe748778954c6b541249be4.png)
insert image description here
The above is the improved crossover algorithm Python pseudocode.

The common crossover algorithm is to generate a random number within the length range of the parental chromosome, then intercept the first half of the father's chromosome and the second half of the mother's chromosome, and cross the child according to the generated random number.

This paper improves the algorithm, trying to cross the genes within the length range of parental chromosomes one by one, calculate the fitness, and screen out the children with the highest fitness. Experimental data show that this improvement can reduce the number of iterations and speed up the convergence of fitness.

The above paragraphs are the article's explanation of the improved crossover algorithm. We can know by reading the pseudocode that it needs to input two genes, one paternal gene and one maternal gene , a function to calculate the fitness of a single individual , and finally output the best individual gene in the present generation .

Therefore, in order not to lose universality, when programming, firstly, a parent body and a mother body are randomly generated among all individuals. The fitness function used before is based on the matrix of contemporary individuals (dimension 150 80), which is two-dimensional, and this method is to calculate fitness for a single individual (dimension 1 80), so it cannot be used here Called directly, it needs to be adjusted to one dimension.

In order to facilitate the viewing of the results, I finally output two values: the optimal individual and the corresponding optimal fitness.

Calculating the fitness requires decoding the binary of the gene to decimal to calculate, so we first adjust the decoding program:

def translate_singleDNA(pop):
    # 这里表示对单个个体的DNA进行解码,维度是1维
    p1_pop = pop[:20]
    p2_pop = pop[20:40]
    q1_pop = pop[40:60]
    q2_pop = pop[60:]

    # pop:(POP_SIZE,DNA_SIZE)*(DNA_SIZE,1) --> (POP_SIZE,1) 进行解码的过程
    p1 = p1_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (p1_BOUND[1] - p1_BOUND[0]) + p1_BOUND[
        0]
    p2 = p2_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (p2_BOUND[1] - p2_BOUND[0]) + p2_BOUND[
        0]
    q1 = q1_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (q1_BOUND[1] - q1_BOUND[0]) + q1_BOUND[
        0]
    q2 = q2_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (q2_BOUND[1] - q2_BOUND[0]) + q2_BOUND[
        0]
    return p1, p2, q1, q2


def calcu_fitness(singlepop):
    p1, p2, q1, q2 = translate_singleDNA(singlepop)
    pred = F(p1, p2, q1, q2)
    # 求最小值问题,目标函数值越小,适应度越高
    # 这里借用sigmoid函数,目标函数值越小,适应度越高
    return (1 + np.exp(pred)) ** (-1)  

def crossover_develop(pop):
    mother = pop[np.random.randint(POP_SIZE)]
    father = pop[np.random.randint(POP_SIZE)]
    best_child = np.zeros(father.size)
    best_fitness = 0
    for i in range(len(father)):
        current_child = np.append(father[0:i], mother[i:])  
        # np.append()用于合并两个数组
        current_fitness = calcu_fitness(current_child)  
        if current_fitness > best_fitness:
            best_fitness = current_fitness
            best_fitness = np.log(best_fitness ** (-1) - 1)  
            # 取反函数,得到原本的适应度
            best_child = current_child
    return best_child, best_fitness

Because it is a random algorithm, the result of each run of the program is different. Intercept the result of one run:
insert image description here
decode the above best_child into decimal translate_singleDNA(best_child) , and the four parameter values ​​p1, p2, q1, q2 are:
insert image description here

This method is essentially an exhaustive crossover to find an optimal individual gene given the parental and maternal genes.

It should be noted that we can only get one individual through this method. If we want to get the current individual population, we can consider writing a for loop and call this function continuously until the specified number of populations is generated.

3. Improvement of the fitness function

The third point is the improvement of the fitness function. In fact, the calculation of the fitness function has been mentioned in the second improvement, and the important sigmoid function(1 + np.exp(pred)) ** (-1) will be used here .

The design idea of ​​this method comes from reference 3 3 .

(1) sigmoid function

Since the fitness function design usually requires its value range between [0,1], consider the logistic curve: y = 1 1 + exy=\frac{1}{1+e^{x}}y=1+ex1The value range is exactly between (0,1), the value range is between (0.5,1) when it is less than 0, and the value range is between (0,0.5] when it is greater than or equal to 0, and its value range is [-10 ,10] The function graph between , as shown in the figure:
insert image description here

(2) Design idea of ​​fitness function

The design of the fitness function requires controlling the fitness value of individuals with higher fitness in the early stage of evolution, reducing the difference between it and other fitness values, and limiting its number of replications;

In the later stage of evolution, it is necessary to prevent the problem of low competitiveness caused by the similarity between the average fitness value and the best fitness value, so the difference between the individual fitness value and the best individual fitness value should be increased, so that The competitiveness of species increases.

Therefore, when the individual fitness value in the population is close to the average fitness value, it means that the individuals in the population are relatively similar, so it is necessary to increase the difference between the individual fitness values ​​in the population to strengthen the competitiveness of the individuals in the population. But the logistic curve is outside [-10,10], and the function value is close to 1 or 0. If there are more individuals whose fitness values ​​are close to 1 or 0, then the individual fitness values ​​are very close. This will lead to less competitiveness.

Therefore, this method will retain the top 30% of the excellent models that do not participate in crossover and mutation. If there are many individuals whose fitness value is close to 1 in the early evolutionary population, these individuals will quickly occupy the population. In general, these individuals will not be the global optimal solution, which will cause premature phenomenon. In order to prevent this phenomenon from happening, the judgment of the number of individuals whose fitness value is close to 1 or 0 is added in the design of the fitness function.

Therefore, the fitness function designed by this method is:
insert image description here
where, f is the original fitness, if the problem itself is to find the minimum value, f can be the objective function, if the problem itself is to find the maximum value, then the problem needs to be transformed into a minimum value The problem.

f a v g f_{avg} favgis the average fitness of the current population. Each individual's fff- value andfavg f_{avg}favgAs a difference, g represents the number of individuals outside (-10,10); c represents fff- value andfavg f_{avg}favgThe order of magnitude of the maximum absolute value of the difference.

f a v g f_{avg}favgThe method of finding avg_fitnessis exactly what you want:

def origin_fitness(pop):  # 原适应度函数
    p1, p2, q1, q2 = translateDNA(pop)  
    # 这里的pi、qi都是长度为150的向量
    pred = F(p1, p2, q1, q2)  
    return -(pred - np.max(pred)) + 1e-3  

# 当代个体适应度的平均值
avg_fitness = np.mean(origin_fitness(pop))  

After the original fitness function is combined with the sigmoid function, that is get_fitness1(pop):

def get_fitness1(pop):  
    return (1 + np.exp(origin_fitness(pop))) ** (-1)

When g>=30% nnWhen n , that isget_fitness2(pop):

# 求每个个体适应度-当代个体适应度的最大值的绝对值
def get_fitness2(pop):  # g>=0.3*n时
    b = [np.abs(i - np.max(origin_fitness(pop))) for i in origin_fitness(pop)]
    c = np.max(b)
    return (1 + np.exp((origin_fitness(pop) - avg_fitness) / c)) ** (-1)

当g<30% n n When n , that isget_fitness3(pop):

# g<0.3*n时
def get_fitness3(pop):  
    return (1 + np.exp(origin_fitness(pop) - avg_fitness)) ** (-1)

The above is the theoretical basis for analyzing this approach, but there are still several problems in converting it into program implementation:

  • When there is more than one parameter, it is not easy to judge whether x falls outside (-10,10). For example, the number of parameters in this question is 4. And its approach is essentially to find extreme values ​​close to 0 or 1, so we can transform the judgment of x into the judgment of y. That is, calculate its fitness function for each set of solutions (p1, p2, q1, q2), and add 1 to the count when the fitness value meets certain conditions, until the count exceeds 30% of the number of contemporary individuals, and substitute into the corresponding fitness expression .

  • In the early stage of evolution, it is necessary to prevent premature maturity; in the later stage of evolution, it is necessary to enhance the competitiveness of individuals. However, the article did not point out when to judge "the fitness is close to the extreme value". But what we can expect is that it must be after some algebras of evolution to examine how many individuals in the current generation have a fitness close to 0 or 1. Therefore, for the feasibility of the program, I make the conditional judgment of the above formula when the evolutionary algebra is halfway through.

Convert the judgment that x falls outside the [-10,10] area to the value range of y:

def expfun(x):
    return (1 + np.exp(x)) ** (-1)

upper = expfun(-10)  # 0.9999546021312976
lower = expfun(10)  # 4.5397868702434395e-05

Therefore, in the improvement of the fitness function in the third part, the whole program can be roughly divided into two parts. The first part is in the first half of the evolution, we use the get_fitness1(pop)function to calculate the fitness of the individual;

Because it has been evolving for a period of time, there may be a small number of excellent individuals whose fitness is close to 0 or 1, and then we use the above formula to judge, when the number of such excellent individuals is greater than or equal to 30% of the population (ie POP_SIZE) When , we substitute into get_fitness2(pop)the calculation of individual fitness, otherwise substitute into get_fitness3(pop)the function.

The programming is as follows:

# 生成初试种群
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE * 4)) 
# 在前半程的进化中
for _ in range(int(N_GENERATIONS / 2)):  
    pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE))  # 交叉和变异
    fitness = get_fitness1(pop)
    pop = select(pop, fitness)  # 选择

# 下面进行上式的条件判断,来分别代入get_fitness2(pop)或者get_fitness3(pop)

# 在后半程的进化中
for _ in range(int(N_GENERATIONS / 2), N_GENERATIONS):
    i = 0
    pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE))
    for i in fitness:
        if i >= upper or i <= lower:
            i += 1
        if i >= 0.3 * POP_SIZE:
            fitness = get_fitness2(pop)
        else:
            fitness = get_fitness3(pop)
        pop = select(pop, fitness)
       
print_info(pop)

The result of the final run:
insert image description here

Note: I also encountered a problem in the above programming, which is to find the top 30% of excellent individuals and ensure that they do not participate in the subsequent crossover and mutation. But if you think about it, you will know that it is very troublesome to compile.

Because the first 30% of the seeds are not easy to correspond to one by one, even if they can correspond, the relevant parameters in the selection function and cross-variation function must be modified. Interested students can study further, and please let me know if you come out!

The complete code is attached below:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import warnings

warnings.filterwarnings('ignore')

DNA_SIZE = 20  # DNA长度(二进制编码长度)
POP_SIZE = 150  # 初始种群数量
CROSSOVER_RATE = 0.95  # 交叉率
MUTATION_RATE = 0.005  # 变异率 将0.005改为0.01
N_GENERATIONS = 1000  # 进化代数 进化代数在 800—1200 之间比较适合,本文选取进化1000代
p1_BOUND = [0, 1]  # 确定参数的范围
p2_BOUND = [0, 1]
q1_BOUND = [0, 1]
q2_BOUND = [0, 1]

dic_liver = {
    
    0.167: 0.681, 0.5: 0.436, 1: 0.709, 2: 0.263, 6: 0.12}  # 键表示时间(h),值表示肝内的浓度
dic_lung = {
    
    0.167: 1.069, 0.5: 0.689, 1: 0.666, 2: 0.342, 6: 0.162}  # 表示肺内的浓度
dic_stomach = {
    
    0.167: 4.827, 0.5: 3.866, 1: 1.67, 2: 1.638, 6: 0.798}  # 表示胃内的浓度


def F(p1, p2, q1, q2):  # 设计适应度函数 法一
    fun = 0
    for key, value in dic_liver.items():
        fun = ((p1 * np.exp(-q1 * key) + p2 * np.exp(-q2 * key)) - value) ** 2 + fun
    return 1 / (1 + np.exp(-fun))
    # return fun


def F2(p1, p2, q1, q2):  # 设计适应度函数 法二
    result = [((p1 * np.exp(-q1 * i) + p2 * np.exp(-q2 * i)) - j) ** 2 for i, j in zip(l1, l2)]
    total = 0
    for i in range(len(result)):
        total = total + result[i]
    return total

# 求最小值 函数值越小的可能解对应的适应度应该越大
def get_fitness(pop):
    p1, p2, q1, q2 = translateDNA(pop)
    pred = F(p1, p2, q1, q2)
    return -(pred - np.max(pred)) + 1e-3  


def translateDNA(pop):  # pop表示种群矩阵
    p1_pop = pop[:, :20]  
    p2_pop = pop[:, 20:40]  
    q1_pop = pop[:, 40:60]
    q2_pop = pop[:, 60:]

    p1 = p1_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (p1_BOUND[1] - p1_BOUND[0]) + p1_BOUND[
        0]
    p2 = p2_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (p2_BOUND[1] - p2_BOUND[0]) + p2_BOUND[
        0]
    q1 = q1_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (q1_BOUND[1] - q1_BOUND[0]) + q1_BOUND[
        0]
    q2 = q2_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (q2_BOUND[1] - q2_BOUND[0]) + q2_BOUND[
        0]
    return p1, p2, q1, q2
    

def crossover_and_mutation(pop, CROSSOVER_RATE=0.95):  # 单点交叉
    new_pop = []
    for father in pop:  # 遍历种群中的每一个个体,将该个体作为父亲
        child = father  
        if np.random.rand() < CROSSOVER_RATE:  # 产生子代时不是必然发生交叉,而是以一定的概率发生交叉
            mother = pop[np.random.randint(POP_SIZE)]  # 再种群中选择另一个个体,并将该个体作为母亲
            cross_points = np.random.randint(low=0, high=DNA_SIZE * 4)  # 随机产生交叉的点
            child[cross_points:] = mother[cross_points:]  # 孩子得到位于交叉点后的母亲的基因
        mutation(child)  # 每个后代有一定的机率发生变异
        new_pop.append(child)
    return new_pop
    

# 基本位变异算子
def mutation(child, MUTATION_RATE=0.005):
    if np.random.rand() < MUTATION_RATE:  # 以MUTATION_RATE的概率进行变异
        mutate_point = np.random.randint(0, DNA_SIZE * 4)  # 随机产生一个实数,代表要变异基因的位置
        child[mutate_point] = child[mutate_point] ^ 1 

def select(pop, fitness):  
# 描述了从np.arange(POP_SIZE)里选择每一个元素的概率,概率越高约有可能被选中,最后返回被选中的个体即可
    idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
                           p=(fitness) / (fitness.sum()))
    return pop[idx]


def print_info(pop):
    fitness = get_fitness(pop)
    min_fitness_index = np.argmin(fitness)  # 表示为array的最大值/最小值对应的索引
    print("min_fitness:", fitness[min_fitness_index])
    p1, p2, q1, q2 = translateDNA(pop)
    print("最优的基因型:", pop[min_fitness_index])
    print("(p1, p2, q1, q2):",
          (p1[min_fitness_index], p2[min_fitness_index], q1[min_fitness_index], q2[min_fitness_index]))


# --2023.1.3 遗传算法的改进,以我复现的论文为例-----------------------------------

# ---NO.1 对适应度函数的改进---------------------------------------------------

# 第一步:将适应度函数代入sigmoid函数中,y=1/(1+np.exp(x)),此时适应度函数的范围调整至(0,1)之间
# 第二步:当进化代数进行到一半时(我自己定的),对它进行条件判断
# 第三步:这里对x范围的判断实际上是为了确定极端值的数量,所以可以将它转换为对y范围的判断
# 第四步:更新每次循环后的适应度函数


def origin_fitness(pop):  # 原适应度函数
    p1, p2, q1, q2 = translateDNA(pop)  
    pred = F(p1, p2, q1, q2)  
    return -(pred - np.max(pred)) + 1e-3 


avg_fitness = np.mean(origin_fitness(pop))  # 当代个体适应度的平均值


def get_fitness1(pop):  # 代入sigmoid函数后
    return (1 + np.exp(origin_fitness(pop))) ** (-1)


def get_fitness2(pop):  # g>=0.3*n时
    b = [np.abs(i - np.max(origin_fitness(pop))) for i in origin_fitness(pop)]
    c = np.max(b)
    return (1 + np.exp((origin_fitness(pop) - avg_fitness) / c)) ** (-1)


def get_fitness3(pop):  # g<0.3*n时
    return (1 + np.exp(origin_fitness(pop) - avg_fitness)) ** (-1)


# 将x在(-10,10)的范围的判断转换到对y的判断上来
def expfun(x):
    return (1 + np.exp(x)) ** (-1)


upper = expfun(-10)  # 0.9999546021312976
lower = expfun(10)  # 4.5397868702434395e-05

if __name__ == "__main__":

    pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE * 4)) 
    for _ in range(int(N_GENERATIONS / 2)):  
        pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE))  # 进行交叉和变异
        fitness = get_fitness1(pop)
        pop = select(pop, fitness)  # 选择

    # 下面进行件判断,来分别代入get_fitness2(pop)或者get_fitness3(pop)
    for _ in range(int(N_GENERATIONS / 2), N_GENERATIONS):
        i = 0
        pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE))
        for i in fitness:
            if i >= upper or i <= lower:
                i += 1
        if i >= 0.3 * POP_SIZE:
            fitness = get_fitness2(pop)
        else:
            fitness = get_fitness3(pop)
        pop = select(pop, fitness)
    print_info(pop)


# ---NO.2 对变异概率的改进--------------------------------------
def mutation_develop(child, MUTATION_RATE=0.005):  # 改进的变异算法,即当遍历的基因位置大于染色体长度的一半时,设置突变率为原来的a两倍
    mutate_point = np.random.randint(0, DNA_SIZE * 4)  
    if mutate_point >= DNA_SIZE / 2:
        MUTATION_RATE = MUTATION_RATE * 2
    if np.random.rand() < MUTATION_RATE:  
        child[mutate_point] = child[mutate_point] ^ 1 
        

# ----本文给出的变异概率设计思路--------------
# 本文的多点交叉变异
def mutation(child, MUTATION_RATE=0.005):
    N = np.random.randint(1, 81)  # 1≤N≤80
    P = np.random.randint(1, 80)  # 1≤P≤79
    a = 0
    b = 0
    if N >= P:
        a = 0
        b = P - 1
    else:
        a = N
        b = P
    for i in range(a, b + 1):
        child[i] = child[i] ^ 1


# --NO.3 交叉概率的改进--------
# 注:这种改进利用穷举交叉点的方法,返回的仅是单个最优个体,而不是像上面的方法返回的是个体矩阵

def translate_singleDNA(pop):
    # 这里表示对单个个体的DNA进行解码,维度是1维
    p1_pop = pop[:20]
    p2_pop = pop[20:40]
    q1_pop = pop[40:60]
    q2_pop = pop[60:]

    p1 = p1_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (p1_BOUND[1] - p1_BOUND[0]) + p1_BOUND[
        0]
    p2 = p2_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (p2_BOUND[1] - p2_BOUND[0]) + p2_BOUND[
        0]
    q1 = q1_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (q1_BOUND[1] - q1_BOUND[0]) + q1_BOUND[
        0]
    q2 = q2_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (q2_BOUND[1] - q2_BOUND[0]) + q2_BOUND[
        0]
    return p1, p2, q1, q2


def calcu_fitness(singlepop):
    p1, p2, q1, q2 = translate_singleDNA(singlepop)
    pred = F(p1, p2, q1, q2)
    # 求最小值问题,目标函数值越小,适应度越高
    return (1 + np.exp(pred)) ** (-1)  


def crossover_develop(pop):
    mother = pop[np.random.randint(POP_SIZE)]
    father = pop[np.random.randint(POP_SIZE)]
    best_child = np.zeros(father.size)
    best_fitness = 0
    for i in range(len(father)):
        current_child = np.append(father[0:i], mother[i:])  # np.append()用于合并两个数组
        current_fitness = calcu_fitness(current_child)  
        if current_fitness > best_fitness:
            best_fitness = current_fitness
            # 取反函数,得到原本的适应度
            best_fitness = np.log(best_fitness ** (-1) - 1)  
            best_child = current_child
    return best_child, best_fitness

if __name__ == "__main__":
	crossover_develop(pop)

The collection and understanding of the above documents and the reproduction of the code took me a lot of time and effort, just like my own son.

So please indicate the source of the reprint, I hope it will be helpful to all brothers and sisters!


  1. Sun Liang, Li Junli, Cheng Jianping. Application of a Synthetic Algorithm Based on Genetic Algorithm and Gauss-Newton Method in Biokinetic Data Analysis of Radiopharmaceuticals[J]. Nuclear Technology, 2006(12):927-931. ↩︎

  2. Gao Q , Qi K , Lei Y , et al. An Improved Genetic Algorithm and Its Application in Artificial Neural Network Training[C]// 2005 5th International Conference on Information Communications & Signal Processing. 0. ↩︎ ↩︎

  3. Li Yanmei. An Improved Genetic Algorithm and Its Application [D]. South China University of Technology, 2012. ↩︎

Guess you like

Origin blog.csdn.net/golden_knife/article/details/128535624