一直想写一个遗传算法的样例,正好最近有空,写了一个。
遗传算法是一种无约束优化算法,借鉴了遗传理论,从一组初始点开始,计算目标函数,然后根据计算结果,对初始点进行交叉和变异操作,获得一组新的点。这一组新获得的点平均值优于初始点。如此往复迭代,直到达到所需要的精度为止。
遗传算法的步骤如下:
1. 令k=0,产生一个初始种群P(0)
2. 评估P(k),计算P(k)中每个个体的适应度
3. 如果满足停止规则,停止迭代
4. 从P(k)中选择新种群M(k)
5. 进化M(k),构成新种群P(k+1)
6. 令k=k+1,回到第2步
下面给出样例代码:
GenericAlgorithm.py
# -*- coding: utf-8 -*-
import numpy
# 遗传算法求最大值
class GenericAlgorithm:
# func为要求最大值的函数
# encoding决定是否对自变量进行编码
# min为args对应最小值
# max为args对应最大值
# dnaLength为dna编码长度
def __init__(self, func, min, max, encoding = False, dnaLength = 128):
self._func = func
self._min = min
self._max = max
self._flag = encoding
self._dnaLen = dnaLength
def __del__(self):
return
# 编码,仅在交叉和变异时使用,此处用最简单的二进制编码
def _encode(self, matrix, length):
rows = matrix.shape[0]
columns = matrix.shape[1]
realen = int(length / columns)
bmax = 2**(realen) - 1
r = [self._max[i] - self._min[i] for i in range(len(self._min))]
mkb = []
for i in range(rows):
tmp = ''
for j in range(columns):
bstr = str(bin(int((matrix[i][j] - self._min[j])/r[j] * bmax)))
bstr = bstr[2:].zfill(realen)
tmp += bstr
mkb.append(tmp)
return mkb
# 解码,同上
def _decode(self, matrix, length):
rows = len(matrix)
columns = len(self._min)
realen = int(length / columns)
bmax = float(2**(realen) - 1)
r = [self._max[i] - self._min[i] for i in range(len(self._min))]
mk = numpy.zeros((rows, columns))
for i in range(rows):
for j in range(columns):
tmp = matrix[i][j*realen:(j+1)*realen]
mk[i][j] = int(tmp,2) / bmax * r[j] + self._min[j]
return mk
def _evolve(self, mk, prop_cross, prop_mut):
mk = self._cross(mk, prop_cross)
pk = self._mutation(mk, prop_mut)
return pk
# 交叉
def _cross(self, mk, prop_cross):
rows = mk.shape[0]
columns = mk.shape[1]
idx = numpy.zeros(1)
while (idx.shape[0] % 2 != 0):
idx = numpy.where(numpy.random.rand(rows,1) < prop_cross)[0]
if (self._flag):
mkb = self._encode(mk, self._dnaLen)
for i in range(0, idx.shape[0], 2):
bit = int(numpy.random.rand()*self._dnaLen)
tmp1 = mkb[idx[i]][:bit] + mkb[idx[i+1]][bit:]
tmp2 = mkb[idx[i+1]][:bit] + mkb[idx[i]][bit:]
mkb[idx[i]] = tmp1
mkb[idx[i+1]] = tmp2
mk = mkb
else:
for i in range(0, idx.shape[0], 2):
rn = numpy.random.rand()
w = numpy.random.randn(1,columns)
tmp1 = rn*mk[idx[i]] + (1-rn)*mk[idx[i+1]] + w
tmp2 = (1-rn)*mk[idx[i]] + rn*mk[idx[i+1]] - w
mk[idx[i]] = tmp1
mk[idx[i+1]] = tmp2
for i in range(rows):
for j in range(columns):
mk[i][j] = numpy.clip(mk[i][j], self._min[j], self._max[j])
return mk
# 变异
def _mutation(self, mk, prop_mut):
if (self._flag):
rows = len(mk)
for i in range(rows):
tmp = list(mk[i])
idx = numpy.where(numpy.random.rand(self._dnaLen) < prop_mut)[0]
for j in range(len(idx)):
if (tmp[idx[j]] == '0'):
tmp[idx[j]] = '1'
elif (tmp[idx[j]] == '1'):
tmp[idx[j]] = '0'
else:
print("just for extension.")
mk[i] = ''.join(tmp)
mk = self._decode(mk, self._dnaLen)
else:
rows = mk.shape[0]
columns = mk.shape[1]
idx = numpy.where(numpy.random.rand(rows,1) < prop_mut)[0]
mk[idx] = mk[idx] + numpy.random.randn(idx.shape[0], columns)
for i in range(rows):
for j in range(columns):
mk[i][j] = numpy.clip(mk[i][j], self._min[j], self._max[j])
return mk
# 轮盘赌选择法
def _select(self, values, pk):
indexes = []
min = numpy.min(values)
values -= min # 将values的值转换为正值,方便下面的操作
summation = numpy.sum(values)
population = values.shape[0]
data = [(pk[i], values[i]) for i in range(population)]
sorteddata = sorted(data, key = lambda ele: ele[1])
for i in range(population):
test = numpy.random.rand()
sum = 0.0
for j in range(population):
sum += sorteddata[j][1]
if (sum >= test*summation):
indexes.append(j)
break
mk = numpy.zeros(pk.shape)
for i in range(population):
idx = indexes[i]
mk[i] = sorteddata[idx][0]
return mk
# iterations: 最大迭代次数
# population: 种群数量
# prop_cross: 交叉概率
# prop_mut: 变异概率
def run(self, iterations, population, prop_cross, prop_mut):
columns = len(self._min)
p0 = numpy.random.randn(population, columns) # 初始种群,每行为一组输入/一个个体
r = [(self._max[i] - self._min[i])/2.0 for i in range(columns)]
centerPoint = [self._min[i] + r[i] for i in range(columns)]
for i in range(population):
for j in range(columns):
p0[i][j] = centerPoint[j] + p0[i][j]*r[j]
p0[i][j] = numpy.clip(p0[i][j], self._min[j], self._max[j])
values = numpy.zeros(population)
pk = p0
max = 0.0
x_max = 0.0
for iter in range(iterations): # 开始迭代
for i in range(population):
values[i] = self._func(*pk[i])
idx = numpy.argmax(values)
x = pk[idx]
y = values[idx]
if (y > max):
max = y
x_max = x
mk = self._select(values, pk) # 选择mk
pk = self._evolve(mk, prop_cross, prop_mut) # 进化
return (x_max, max)
代码中有注释,比较容易懂。但由于时间问题,部分代码仅实现了功能,没好好优化,导致无论代码格式还是算法本身,都比较丑。
接下来给出用法和测试代码:
test.py
# -*- coding: utf-8 -*-
import GenericAlgorithm as GA
def function(x, y):
term1 = 3*numpy.power(1-x, 2)*numpy.exp(-numpy.power(x,2)-numpy.power(y+1,2))
term2 = -10*(x/5 - numpy.power(x,3) - numpy.power(y,5))*numpy.exp(-numpy.power(x,2)-numpy.power(y,2))
term3 = -numpy.exp(-numpy.power(x+1,2)-numpy.power(y,2))/3
return term1 + term2 + term3
ga = GA.GenericAlgorithm(func=function, min=[-3,-3], max=[3,3], encoding=True, dnaLength=32)
(x, y) = ga.run(50, 20, 0.75, 0.0075)
print("GA: x = ", x, ", y = ", y)
xmax = (-0.0093, 1.5814)
ymax = function(xmax[0], xmax[1])
print("Real: x = ", xmax, ", y = ", ymax)
最终计算结果如下: