遗传算法求解极大值问题

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

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对应相应的进制,而输出对应的是其十进制的整数。

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

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值