启发式算法-遗传算法

启发式算法-遗传算法

遗传算法,模拟自然界基因的遗传的过程,得出优化问题解

import math
import matplotlib.pyplot as plt
import random
import numpy as np

一、优化问题

# 求解函数在 2>= x >= -2 区间的最大值, 精确到小数点后5位
def func(x):
    return x * math.sin(10 * math.pi * x) + 5

二、编码基因方式

将 x 转换编码19位的01列表,并作为对应的基因
比如 -2.00000 对应 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-1.99999 对应 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]

# 将 x 编码到 0,1 序列, 基因位数为n
n = math.log2(4 / (10 ** -5))
print(n)
n = math.ceil(n)
print("至少需要基因数:",n)

#  编码
def encode(x):
    if x < -2 or x > 2:
        return None
    m = (x + 2) % (10 ** -5)
    v = (x + 2) // (10 ** -5)
    v = v if m == 0 else v + 1
    if v == 0:
        return [0] * 19
    
    print(f"value: {v} <-- {x}")
    
    gene = []
    while v > 0:
        b = v % 2
        v = v // 2
#         print(f"b: {b}")
        gene.append(int(b))
    
    gene = gene[::-1]
    while len(gene) < 19:
#         print("插入0")
        gene.insert(0, 0)
    
    return gene

# 解码
def decode(gene: list):
    v = 0
    for i, value in enumerate(gene[::-1]):
        v += value * (2 ** i)
#     print(f"v: {v}")
    v = v * (10 ** -5) - 2
    if v > 2 or v < -2:
        v = None  # 非法编码, 没哟对应值
    return v
    

def test(x=0):
    g_ = encode(x)
    print(f"{x} --> {g_}")
    d_ = decode(g_)
    print(f"{g_} --> {d_}")

test(x=0)
test(x=-2)
test(x=1)
test(x=2)
18.609640474436812
至少需要基因数: 19
value: 200000.0 <-- 0
0 --> [0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0]
[0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0] --> 0.0
-2 --> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] --> -2.0
value: 300000.0 <-- 1
1 --> [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
[1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0] --> 1.0000000000000004
value: 400000.0 <-- 2
2 --> [1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0] --> 2.0

三、算法实现

# 算法类
class GeneticAlgorithm:

    def __init__(self, gene_length=19, mask=None, func=None):
        self.gene_length = gene_length

        if not mask:  # 均匀交叉的位置
            mask = [random.randint(0, 1) for i in range(gene_length)]

        self.mask = mask

        self.func = func  # 适应度函数

    def population_make(self, m=100):  # 生成随机种群
        n = self.gene_length

        data = []
        for i in range(m):
            d = [random.randint(0, 1) for x in range(n)]
            data.append(d)

        print(f"生成种群:{len(data)}")
        return data

    def decode(self, gene: list):
        v = 0
        for i, value in enumerate(gene[::-1]):
            v += value * (2 ** i)
        v = v * (10 ** -5) - 2
        if v > 2 or v < -2:
            v = None  # 非法编码, 没哟对应值
        return v

    def selecttion(self, population, **kwargs):
        return self.selection_tournament(population, **kwargs)

    def crossover(self, population, **kwargs):
        return self.crossover_uniformly(population, **kwargs)

    def mutation(self, population):  # 变异
        new_data = []

        for gene in population:
            temp = []
            for v in gene:
                if random.random() < 0.1:
                    a = 0 if v == 1 else 1
                    temp.append(a)
                else:
                    temp.append(v)
            new_data.append(temp)

        return new_data

    def crossover_uniformly(self, population, num=100):
        # 均匀交叉, 有放回的交叉
        mask = self.mask
        population = list(population)

        l = len(population)
        new_data = []
        for i in range(num):
            # 随机选择两个个体
            while True:
                i0 = random.randint(0, l - 1)
                i1 = random.randint(0, l - 1)
                if i0 != i1:
                    break

            d0 = population[i0]
            d1 = population[i1]
            new_0 = []
            new_1 = []
            # 交叉
            for j, ma in enumerate(mask):
                if ma == 0:
                    new_0.append(d0[j])
                    new_1.append(d1[j])
                else:
                    new_0.append(d1[j])
                    new_1.append(d0[j])

            new_data.append(new_0)
            new_data.append(new_1)
            if len(new_data) + l >= num:
                return population + new_data

    def selection_tournament(self, population, m=50, c=5):  # 锦标赛选择种群
        pl = len(population)
        population = list(population)
        new_population = []
        for i in range(m):
            max_value = None
            max_individuality = None
            max_index = 0
            for j in range(c):
                l = len(population)
                index = random.randint(0, l - 1)
                d = population[index]
                value = self.decode(d)
                if value is None:
                    continue
                value = self.func(value)

                if max_value is None or value > max_value:
                    max_value = value
                    max_individuality = list(d)
                    index = index

            if max_value is not None:
                new_population.append(max_individuality)
                population.pop(max_index)

#         print(f"选择数据量:{pl}, 返回:{len(new_population)}")
        return new_population

    def run(self, n=3000):
        m = 300
        select_num = m // 2
        # 生成初始种群
        population = self.population_make(m=m)
        for i in range(n):
            # 淘汰不合理基因个体
            population = [x for x in population if self.decode(x) is not None]
            # 选择
            selects = self.selecttion(population, m=select_num)
            if i % 60 == 0:
                print(f"loop: {i} {len(selects)}", selects[0])
            num = m
            # 交叉
            new_population = self.crossover(selects, num=num)
            # 变异
            new_population = self.mutation(new_population)
            population = new_population

        best_individuality = None
        best_value = None
        for i in population:
            x = self.decode(i)
            if x is None:
                continue

            y = self.func(x)
            if best_value is None or y > best_value:
                best_value = y
                best_individuality = i

        best_x = self.decode(best_individuality)
        print("最优结果:", best_value, best_x, best_individuality)

        return best_value, best_x, best_individuality


# 求解函数在 2>= x >= -2 区间的最大值, 精确到小数点后5位
# def func(x):
#     return x * math.sin(10 * math.pi * x) + 5

ga = GeneticAlgorithm(gene_length=19, func=func)
ga.run()
生成种群:300
loop: 0 150 [0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1]
loop: 60 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1]
loop: 120 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0]
loop: 180 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1]
loop: 240 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]
loop: 300 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0]
loop: 360 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1]
loop: 420 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1]
loop: 480 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1]
loop: 540 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0]
loop: 600 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1]
loop: 660 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1]
loop: 720 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]
loop: 780 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1]
loop: 840 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1]
loop: 900 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1]
loop: 960 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0]
loop: 1020 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
loop: 1080 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]
loop: 1140 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1]
loop: 1200 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0]
loop: 1260 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0]
loop: 1320 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1]
loop: 1380 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0]
loop: 1440 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1]
loop: 1500 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1]
loop: 1560 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0]
loop: 1620 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0]
loop: 1680 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1]
loop: 1740 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1]
loop: 1800 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0]
loop: 1860 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0]
loop: 1920 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1]
loop: 1980 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
loop: 2040 150 [0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0]
loop: 2100 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1]
loop: 2160 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0]
loop: 2220 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0]
loop: 2280 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0]
loop: 2340 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1]
loop: 2400 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
loop: 2460 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0]
loop: 2520 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1]
loop: 2580 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1]
loop: 2640 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1]
loop: 2700 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1]
loop: 2760 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0]
loop: 2820 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0]
loop: 2880 150 [0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1]
loop: 2940 150 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0]
最优结果: 6.850273760901909 -1.85055 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1]





(6.850273760901909,
 -1.85055,
 [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1])

四、绘制函数图对比结果

X = np.linspace(-2, 2, 10000)
Y = [func(i) for i in X]
Y = np.array(Y)
print(f"max:{max(Y)}")
max:6.850249664621326
plt.plot(X, Y, "-b")

在这里插入图片描述


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值