启发式算法-遗传算法
遗传算法,模拟自然界基因的遗传的过程,得出优化问题解
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")