遗传算法求解极大值问题

首先参考下上篇博客:遗传算法求解背包问题

1. 极大值问题

假设有一个函数 z=ysin(x)+xcos(y) ,图形如下:

这里写图片描述

这时要求这个函数在x位于[-10,10]和y位于[-10,10]之间的最大值。

这时想象这是个地形图,且这个地区无规律的放置很多人,有的在谷底,有的在半山腰,下面让他们一代代生生不息的繁殖下去,凡是能爬的更高的就留下,按照这个思路走下去就有了遗传算法的应用。

2. 应用遗传算法

  • 基因编码:
    这里和背包问题的基因编码不同,背包问题的基因编码是离散的,而这里是连续的,但是在精度可允许的范围内是可以使其离散化的。这里的[-10,10]范围内如果取到小数点后三位的精度的话,就要把上述范围分割成20001种取值。如果用二进制进行基因编码应选用15位,即 214=16384,215=32768 ,这里用第一位作为正负数的标志,剩下的14位作为具体数字的标识符,这样15位的数字就成为了[-16383,0],[0,16383]。最后,表示二进制的到实数值的函数 H(x) 定义如下:

    H(x)=10F(x)+11638410F(x)116384F(x)0F(x)0

    其中 F(x) 表示二进制到十进制的转换。

  • 设计初始群体:
    这里设计的初始群体每个染色体有两条基因,分别是x和y,这两条基因各有15个基因点,也就是有 215 个可能,随机产生8组基因。

  • 适应度计算:
    适应度在这个场景里用 z=ysin(x)+xcos(y) 即可。

  • 产生下一代:
    这里用的是对应两条染色体的x基因交叉组合,y基因交叉组合,还有就是一条基因的x和另一条染色体的y基因交叉组合,然后翻过来。这样8组作为初始种群的大小就有 C28=28 种组合方式,而每种组合产生2个后代,那么实际上就产生56个染色体。

这里允许基因突变,对56个个体的适应度进行排序,只取出排名前8的个体。然后在8个个体中随机找到2个个体,让2个个体其中一个染色体x发生变异,而让另一个个体y染色体发生变异。之后再两两重组,产生下一代。

这里要注意:染色体的断开点的位置,理论上是随机的,但是断开点靠左对数值影响变化大,自变量的变化范围也就大。反之亦然。还有就是基因变异的位置和断开点的位置影响是一样的,同样是靠左影响大。

2. 代码

# coding=utf-8
import random
import math
import numpy as np


#极大值问题
#染色体 基因X 基因Y
X = [
    [1, 000000100101001, 101010101010101],
    [2, 011000100101100, 001100110011001],
    [3, 001000100100101, 101010101010101],
    [4, 000110100100100, 110011001100110],
    [5, 100000100100101, 101010101010101],
    [6, 101000100100100, 111100001111000],
    [7, 101010100110100, 101010101010101],
    [8, 100110101101000, 000011110000111]]


#染色体长度
CHROMOSOME_SIZE = 15


#判断退出(判断最近3代的最大值如何变化)
def is_finished(last_three):
    s = sorted(last_three)
    if s[0] and s[2] - s[0] < 0.01 * s[0]:
        return True
    else:
        return False

#初始染色体样态
def init():
    chromosome_state1 = ['000000100101001', '101010101010101']
    chromosome_state2 = ['011000100101100', '001100110011001']
    chromosome_state3 = ['001000100100101', '101010101010101']
    chromosome_state4 = ['000110100100100', '110011001100110']
    chromosome_state5 = ['100000100100101', '101010101010101']
    chromosome_state6 = ['101000100100100', '111100001111000']
    chromosome_state7 = ['101010100110100', '101010101010101']
    chromosome_state8 = ['100110101101000', '000011110000111']
    chromosome_states = [chromosome_state1,
                         chromosome_state2,
                         chromosome_state3,
                         chromosome_state4,
                         chromosome_state5,
                         chromosome_state6,
                         chromosome_state7,
                         chromosome_state8]
    return chromosome_states


# 计算适应度
def fitness(chromosome_states):
    fitnesses = []
    for chromosome_state in chromosome_states:
        if chromosome_state[0][0] == '1': # 判断正负号
            # 其中的int()函数把二进制表示成十进制
            x = 10 * (-float(int(chromosome_state[0][1:], 2) - 1)/16384) 
        else:
            x = 10 * (float(int(chromosome_state[0], 2) + 1)/16384)
        if chromosome_state[1][0] == '1':
            y = 10 * (-float(int(chromosome_state[1][1:], 2) - 1)/16384)
        else:
            y = 10 * (float(int(chromosome_state[1], 2) + 1)/16384)
        z = y * math.sin(x) + x * math.cos(y) # 计算适应度
        #print x, y, z
        fitnesses.append(z)

    return fitnesses


# 筛选
def filter(chromosome_states, fitnesses):
    # top 8 对应的索引值
    chromosome_states_new = []
    top1_fitness_index = 0
    for i in np.argsort(fitnesses)[::-1][:8].tolist():
        chromosome_states_new.append(chromosome_states[i])
        top1_fitness_index = i
    return chromosome_states_new, top1_fitness_index


# 产生下一代
def crossover(chromosome_states):
    chromosome_states_new = []
    while chromosome_states:
        chromosome_state = chromosome_states.pop(0) # 弹出首个染色体,也就是遍历所有的组合
        #print 'chromosome_state:',chromosome_state
        for v in chromosome_states:
            pos = random.choice(range(8, CHROMOSOME_SIZE - 1))
            # 先是对应的交叉,再是相互交叉,得到的是两条新的染色体
            chromosome_states_new.append([chromosome_state[0][:pos] + v[0][pos:], chromosome_state[1][:pos] + v[1][pos:]])
            chromosome_states_new.append([v[0][:pos] + chromosome_state[1][pos:], v[0][:pos] + chromosome_state[1][pos:]])
    return chromosome_states_new


# 基因突变
def mutation(chromosome_states):
    n = int(5.0 / 100 * len(chromosome_states)) # 随机找到群体里的2个个体进行变异处理
    print 'n:',n
    while n > 0:
        n -= 1
        chromosome_state = random.choice(chromosome_states)
        index = chromosome_states.index(chromosome_state)
        print 'index:',index
        pos = random.choice(range(len(chromosome_state))) 
        #print 'pos:',pos
        # 选择基因断开点的位置,也就是基因变异的位置,这里固定选择0,1是为了加大数值变化范围
        x = chromosome_state[0][:pos] + str(int(not int(chromosome_state[0][pos]))) + chromosome_state[0][pos+1:]
        y = chromosome_state[1][:pos] + str(int(not int(chromosome_state[1][pos]))) + chromosome_state[1][pos+1:]
        chromosome_states[index] = [x, y] # 得到新的染色体


if __name__ == '__main__':
    chromosome_states = init() # 设计初始群体,每条染色体有x,y两条基因,每个基因有15基因信息点
    last_three = [0] * 3 # 得到[0,0,0]
    last_num = 0
    n = 100
    while n > 0: # 循环100次
        n -= 1
        chromosome_states = crossover(chromosome_states)
        print 'len of crossover():',len(chromosome_states)
        mutation(chromosome_states) # 加入变异后的染色体组
        fitnesses = fitness(chromosome_states) # 适应度计算
        #print 'fitnesses:',fitnesses
        chromosome_states, top1_fitness_index = filter(chromosome_states, fitnesses)
        #print chromosome_states, top1_fitness_index

        #print chromosome_states
        last_three[last_num] = fitnesses[top1_fitness_index]
        print 'last_three:',last_three
        print fitnesses[top1_fitness_index]
        if is_finished(last_three):
            break
        if last_num >= 2:
            last_num = 0
        else:
            last_num += 1
        print '---------%d-----------' % n

运行结果:

len of crossover(): 56
n: 2
index: 51
index: 18
last_three: [3.5925181502851835, 0, 0]
3.59251815029
---------99-----------
len of crossover(): 56
n: 2
index: 16
index: 1
last_three: [3.5925181502851835, 6.127668819486251, 0]
6.12766881949
---------98-----------
len of crossover(): 56
n: 2
index: 10
index: 35
last_three: [3.5925181502851835, 6.127668819486251, 8.896330026626941]
8.89633002663
---------97-----------
len of crossover(): 56
n: 2
index: 5
index: 4
last_three: [8.974057864608891, 6.127668819486251, 8.896330026626941]
8.97405786461
---------96-----------
len of crossover(): 56
n: 2
index: 12
index: 4
last_three: [8.974057864608891, 8.993358420730987, 8.896330026626941]
8.99335842073
---------95-----------
len of crossover(): 56
n: 2
index: 5
index: 6
last_three: [8.974057864608891, 8.993358420730987, 9.02454209561596]
9.02454209562

注意这里的收敛条件是:计算每一代的适应函数最大值,并判断最近3代的最大值如何变化。如果最近3代的适应度函数比较,第一大(最大)的比第三大(最小)的增益小于1%,也即是增长很慢了,就判断为收敛,这种收敛速度是很快的。当然这里可以采用其他的额参数和方法,可以继续考究。。

3. 补充

多运行几次可以发现有时得到的最大值不同,当然最大的才是正确的,其实这里是在繁殖下一代的时候又出现一次播散的情况,但是播散不均匀,导致趋向于错误峰值的种群表现良好,反而趋向于正确峰值的种群,这时可以采取的措施有:

  • 初始种群扩大化
  • 每一代的遴选增加名额

这两种方法都可以让找到最优解的概率大大增加

4. 笔记

Python int() 函数

int() 函数用于将一个字符串会数字转换为整型。
用法:class int(x, base=10)

  • x – 字符串或数字。
  • base – 进制数,默认十进制。
  • 返回整型数据

即是:如果base参数有说明,则x对应相应的进制,而输出对应的是其十进制的整数。

参考:《白话大数据与机器学习》

要使用遗传算法工具箱函数的最大值,首先需要确定以下几个步骤: 1. 定义适应度函数:适应度函数用于评估个体的适应度,即评估个体在问题空间中的优劣程度。在函数最大值问题中,适应度函数应该与目标函数相关,且能够根据个体的表现给出一个数值作为适应度值。 2. 初始化种群:使用遗传算法需要首先初始化一个种群,种群由多个个体组成,每个个体表示问题空间中的一个解。初始时,可以随机生成一定数量的个体,并根据问题的约束条件进行适当的限制。 3. 选择操作:选择操作是指从当前种群中选择一部分个体作为下一代种群的父代。选择操作的目标是根据适应度函数的评估结果,选择适应度较高的个体作为父代。常用的选择操作包括轮盘赌选择、锦标赛选择等。 4. 交叉操作:交叉操作是指从父代中选取一对个体,并通过某种方式交换它们的基因信息,产生新的子代。交叉操作通常会产生多个子代,其中一些子代可能继承了父代的优良特征。 5. 变异操作:变异操作是指对新生成的子代进行随机的基因变异。变异操作的目的是引入新的基因组合,增加种群的多样性,避免陷入局部最优解。 6. 更新种群:根据选择、交叉和变异操作生成的子代,更新当前种群,形成下一代种群。 7. 终止条件:可以设置一些终止条件来结束遗传算法的迭代过程,例如达到最大迭代次数、种群适应度达到一定阈值等。 通过迭代执行上述步骤,直到满足终止条件,就可以得到逼近函数最大值的解。具体实现时,可以使用遗传算法工具箱,如Python中的DEAP(Distributed Evolutionary Algorithms in Python)库或MATLAB中的Global Optimization Toolbox等。这些工具箱提供了丰富的函数和方法,方便进行遗传算法的实现和调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值