遗传算法求极值(英文版,GA for Extremum)

本文介绍了遗传算法(GA),它由美国的John Holland在20世纪70年代提出,可用于解决优化问题,如今也应用于机器学习等领域。通过Python代码演示了用GA求解函数最小值,包括编码种群、解码、选择新种群、交叉与变异等步骤,最终能在短时间内找到高精度的全局最优解。

Introduction and thinking of GA

GA(Genetic Algorithm) was proposed by John Holland of America in 1970s. GA is simulated natural selection as Reproduction, Crossover, Mutation in DNA. When we calculate the complex questions of optimization, GA will be used successfully in short time.

Nowadays, GA can also be uesd in machine learning, signal processing, adaptive control.
\section{selecting the function, optimizing by GA}

The codes are consisted with main function and some subfunction.This is a simple example to demonstrate finding the minimum of function with Python. To simplify the assumption, we can use the simple function:
\begin{equation}
y=\sin x + 0.01 \exp ^x
\end{equation}
\subsection{length of chromosome}
Input the precisionanddefinitiondomainprecision and definition domainprecisionanddefinitiondomain(精度和定义域),then we can get the length of chromosomes. Here we use binary encoding.

# 根据解的精度确定染色体(chromosome)的长度
# 需要根据决策变量的上下边界来确定
def getEncodedLength(delta=0.0001, boundarylist=[]):
    # 每个变量的编码长度
    lengths = []
    for i in boundarylist:    # i在第一个循环取boundarylist的第一行1和2列,第二次取第二行1和2列
        lower = i[0]
        upper = i[1]
        # lamnda 代表匿名函数f(x)=0,50代表搜索的初始解
        res = fsolve(lambda x: ((upper - lower) * 1 / delta) - 2 ** x - 1, 50)
        length = int(np.floor(res[0]))
        lengths.append(length)
    return lengths
    pass

Code the population

Definite the population size is 10, so the number of chromosomes is 10. The function of these codes is inputing length and quantity, outputing initial chromosomes.

def getIntialPopulation(encodelength, populationSize):
    # 随机化初始种群为0
    chromosomes = np.zeros((populationSize, sum(encodelength)), dtype=np.uint8)
    for i in range(populationSize):
        chromosomes[i, :] = np.random.randint(0, 2, sum(encodelength))
    return chromosomes

Decoded

After decoding the genome generated by the above code, the function independent variable can be obtained:

def decodedChromosome(encodelength, chromosomes, boundarylist, delta=0.0001):
    populations = chromosomes.shape[0]
    variables = len(encodelength)
    decodedvalues = np.zeros((populations, variables))
    for k, chromosome in enumerate(chromosomes):
        chromosome = chromosome.tolist()
        start = 0
        for index, length in enumerate(encodelength):
            # 将一个染色体进行拆分,得到染色体片段
            power = length - 1
            demical = 0
            for i in range(start, length + start):
                demical += chromosome[i] * (2 ** power)
                power -= 1
            lower = boundarylist[index][0]
            upper = boundarylist[index][1]
            decodedvalue = lower + demical * (upper - lower) / (2 ** length - 1)
            decodedvalues[k, index] = decodedvalue
            start = length # 开始去下一段染色体的编码
    return decodedvalues

Finding the mathematic function

def getFitnessValue(func, chromosomesdecoded):
    population, nums = chromosomesdecoded.shape
    fitnessvalues = np.zeros((population, 1)) # 初始化种群的适应度值为0
    for i in range(population):  # 计算适应度值
        fitnessvalues[i, 0] = func(chromosomesdecoded[i, :])
    # 计算每个染色体被选择的概率
    probability = fitnessvalues / np.sum(fitnessvalues)
    # 得到每个染色体被选中的累积概率
    cum_probability = np.cumsum(probability)
    return fitnessvalues, cum_probability

Select new population

Input the code of chromosomes and cum_probability,random a number in 0 ∼\sim 1, compared with the cum_probability. Preserve, reproduct and outpute the chromosome if the cum>random.

def selectNewPopulation(chromosomes, cum_probability):
    m, n = chromosomes.shape
    newpopulation = np.zeros((m, n), dtype=np.uint8)
    # 随机产生M个概率值
    randoms = np.random.rand(m)
    for i, randoma in enumerate(randoms):
        logical = cum_probability >= randoma
        index = np.where(logical == 1)   # 寻找logical里面等于1的项,将其保留
        newpopulation[i, :] = chromosomes[index[0][0], :]
    return newpopulation
    pass

Crossover& Mutation

Crossover and mutation are not necessary. There are probabilities of them:
CrossoverprobabilityPc=0.4∼0.99MutationprobabilityPm=0.0001∼0.1 Crossover probability P_c = 0.4 \sim 0.99\\ Mutation probability P_m = 0.0001 \sim 0.1 CrossoverprobabilityPc=0.40.99MutationprobabilityPm=0.00010.1

The crossover probability is the probability of individual crossover. Mutation probability is the probability of mutation of each gene (encoded in binary, 1 to 0 and o to 1). Considered there are too many genes in a population, probability ×\times× sample can be thought as the number of each crossover or mutation.

def crossover(population, Pc=0.8): # 新种群交叉
    # 根据交叉概率计算需要进行交叉的个体个数
    m, n = population.shape
    numbers = np.uint8(m * Pc)
    # 确保进行交叉的染色体个数是偶数个
    if numbers % 2 != 0:
        numbers += 1
    updatepopulation = np.zeros((m, n), dtype=np.uint8)
    index = random.sample(range(m), numbers)
    for i in range(m): # 不交叉的染色体进行复制
        if not index.__contains__(i):
            updatepopulation[i, :] = population[i, :]
    # crossover
    while len(index) > 0:
        a = index.pop()
        b = index.pop()
        # 随机产生一个交叉点
        crossoverPoint = random.sample(range(1, n), 1)
        crossoverPoint = crossoverPoint[0]
        # one-single-point crossover
        updatepopulation[a, 0:crossoverPoint] = population[a, 0:crossoverPoint]
        updatepopulation[a, crossoverPoint:] = population[b, crossoverPoint:]
        updatepopulation[b, 0:crossoverPoint] = population[b, 0:crossoverPoint]
        updatepopulation[b, crossoverPoint:] = population[a, crossoverPoint:]
    return updatepopulation
    pass
    
    def mutation(population, Pm=0.005): # 染色体变异
        updatepopulation = np.copy(population)
        m, n = population.shape
        # 计算需要变异的基因个数
        gene_num = np.uint8(m * n * Pm)
        mutationGeneIndex = random.sample(range(0, m * n), gene_num)
        # 确定每个将要变异的基因在整个染色体中的基因座(即基因的具体位置)
        for gene in mutationGeneIndex:
            # 确定变异基因位于第几个染色体
            chromosomeIndex = gene // n
            # 确定变异基因位于当前染色体的第几个基因位
            geneIndex = gene % n
            if updatepopulation[chromosomeIndex, geneIndex] == 0:
                updatepopulation[chromosomeIndex, geneIndex] = 1
            else:
                updatepopulation[chromosomeIndex, geneIndex] = 0
        return updatepopulation
        pass

Fitness

def fitnessFunction():
    return lambda x: - np.sin(x[0]) - 0.01 * np.exp(x[1])
    pass

Main

def main(max_iter=5000):
    optimalSolutions = []
    optimalValues = []
    decisionVariables = [[-5, 5], [-5, 5]] # 决策变量的取值范围
    # 得到染色体编码长度
    lengthEncode = getEncodedLength(boundarylist=decisionVariables)
    for iteration in range(max_iter):
        # 得到初始种群编码
        chromosomesEncoded = getIntialPopulation(lengthEncode, 10)
        # 种群解码
        decoded = decodedChromosome(lengthEncode, chromosomesEncoded, decisionVariables)
        # 得到个体适应度值和个体的累积概率
        evalvalues, cum_proba = getFitnessValue(fitnessFunction(), decoded)
        # 选择新的种群
        newpopulations = selectNewPopulation(chromosomesEncoded, cum_proba)
        # 进行交叉操作
        crossoverpopulation = crossover(newpopulations)
        # 进行变异操作
        mutationpopulation = mutation(crossoverpopulation)
        # 将变异后的种群解码,得到每轮迭代最终的种群
        final_decoded = decodedChromosome(lengthEncode, mutationpopulation, decisionVariables)
        # 适应度评价
        fitnessvalues, cum_individual_proba = getFitnessValue(fitnessFunction(), final_decoded)
        # 搜索每次迭代的最优解,以及最优解对应的目标函数的取值
        optimalValues.append(np.max(list(fitnessvalues)))
        index = np.where(fitnessvalues == max(list(fitnessvalues)))
        optimalSolutions.append(final_decoded[index[0][0], :])
    # 搜索最优解
    optimalValue = np.max(optimalValues)
    optimalIndex = np.where(optimalValues == optimalValue)
    optimalSolution = optimalSolutions[optimalIndex[0][0]]
    return optimalSolution, optimalValue

Finaly:

solution, value = main()
print('最优解: x1, x2')
print(solution[0], solution[1])
#Measuring run time
elapsedtime = timeit.timeit(stmt=main, number=1)
print('Searching Time Elapsed:(S)', elapsedtime)

run, the solution can be got:x1=4.719386587319752,x2=−4.731898985275044x_{1}=4.719386587319752, x_{2}=-4.731898985275044x1=4.719386587319752,x2=4.731898985275044,minf(x):−0.9998874195924371,SearchingTimeElapsed:(S)3.7949634000000003-0.9998874195924371,Searching Time Elapsed:(S) 3.79496340000000030.9998874195924371SearchingTimeElapsed:(S)3.7949634000000003
The actual solution of minf(x) is about −0.9999326-0.99993260.9999326,as x1=4.71239x_{1}=4.71239x1=4.71239,$ x_{2}=-5$. Compared with GA, it can find the global optimal solution with high precision and short time (about 3.79s) after 5000 iterationsiterationsiterations(迭代)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MATLAB卡尔曼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值