将排列作为基因,进行变异和交叉操作
[1,2,3,4,5]
变异,任意调换两个位置的数字
交叉
[1,2,3,4,5]
[5,4,3,2,1]
由于排列中每个数字只能且必须出现一次,所以交叉操作略微有点麻烦。。。
先进行普通的交叉处理,之后使用set去除重复,并且记录没有使用的数字,在下一个重复的地方放入没有使用的数字即可
import math
import random
dis = [
[0.00, 24.04, 68.37, 37.66, 58.81, 75.77, 65.20, 57.44, 59.37, 18.61],
[24.04, 0.00, 89.58, 57.41, 82.04, 95.54, 59.86, 78.53, 73.57, 16.23],
[68.37, 89.58, 0.00, 69.97, 18.91, 11.62, 86.73, 11.05, 34.42, 75.40],
[37.66, 57.41, 69.97, 0.00, 52.75, 80.83, 101.03, 61.86, 78.96, 56.26],
[58.81, 82.04, 18.91, 52.75, 0.00, 30.52, 92.05, 16.56, 45.24, 69.97],
[75.77, 95.54, 11.62, 80.83, 30.52, 0.00, 85.08, 19.42, 31.47, 80.50],
[65.20, 59.86, 86.73, 101.03, 92.05, 85.08, 0.00, 78.57, 53.61, 48.83],
[57.44, 78.53, 11.05, 61.86, 16.56, 19.42, 78.57, 0.00, 28.99, 64.41],
[59.37, 73.57, 34.42, 78.96, 45.24, 31.47, 53.61, 28.99, 0.00, 57.41],
[18.61, 16.23, 75.40, 56.26, 69.97, 80.50, 48.83, 64.41, 57.41, 0.00],
]
dis = []
with open('dis17.txt', encoding='utf8', mode='r') as f:
for line in f.readlines():
row = []
for i in line.strip().split(' '):
if i.strip() != '':
row.append(float(i.strip()))
dis.append(row)
def getDis(path):
d = sum([dis[path[i]][path[i + 1]]
for i in range(len(path) - 1)] + [dis[path[-1]][path[0]]])
return d
class GA():
def __init__(self, length, count):
# 染色体长度
self.length = length
# 种群中的染色体数量
self.count = count
# 随机生成初始种群
self.population = self.gen_population(length, count)
def evolve(self, retain_rate=0.1, random_select_rate=0.5, mutation_rate=0.05):
"""
进化
对当前一代种群依次进行选择、交叉并生成新一代种群,
然后对新一代种群进行变异
"""
parents = self.selection(retain_rate, random_select_rate)
self.crossover(parents)
self.mutation(mutation_rate)
def gen_chromosome(self, length):
"""
随机生成长度为length的染色体,每个基因的取值是0或1
这里用一个bit表示一个基因
"""
chromosome = list(range(length))
random.shuffle(chromosome)
return chromosome
def gen_population(self, length, count):
"""
获取初始种群(一个含有count个长度为length的染色体的列表)
"""
return [self.gen_chromosome(length) for i in range(count)]
def fitness(self, chromosome):
"""
计算适应度,将染色体解码为0~9之间数字,代入函数计算
因为是求最大值,所以数值越大,适应度越高
"""
return -getDis(chromosome)
def selection(self, retain_rate, random_select_rate):
"""
选择
先对适应度从大到小排序,选出存活的染色体
再进行随机选择,选出适应度虽然小,但是幸存下来的个体
"""
# 对适应度从大到小进行排序
graded = [(self.fitness(chromosome), chromosome)
for chromosome in self.population]
graded = [x[1] for x in sorted(graded, reverse=True)]
# 选出适应性强的染色体
retain_length = int(len(graded) * retain_rate)
parents = graded[:retain_length]
# 选出适应性不强,但是幸存的染色体
for chromosome in graded[retain_length:]:
if random.random() < random_select_rate:
parents.append(chromosome)
return parents
def gen(self, fa, ma):
cross_x = random.randint(0, self.length)
c = fa[:cross_x]
d = ma[cross_x:]
ret = c + d
used = set()
for i in range(self.length):
if ret[i] in used:
unused = set(range(self.length)) - used
ret[i] = list(unused)[0]
used.add(ret[i])
else:
used.add(ret[i])
return ret
def gen2(self, fa, ma):
cross_x = random.randint(0, len(fa))
cross_y = random.randint(0, len(fa))
cross_x = min(cross_x, cross_y)
cross_y = max(cross_x, cross_y)
c = fa[:cross_x]
d = ma[cross_y:]
ret = c + fa[cross_x:cross_y] + d
# print(cross_x, cross_y)
# print(ret)
used = set()
for i in range(len(fa)):
if ret[i] in used:
unused = set(range(len(fa))) - used
ret[i] = list(unused)[random.randint(0, len(unused) - 1)]
used.add(ret[i])
else:
used.add(ret[i])
return ret
def crossover(self, parents):
"""
染色体的交叉、繁殖,生成新一代的种群
"""
# 新出生的孩子,最终会被加入存活下来的父母之中,形成新一代的种群。
children = []
# 需要繁殖的孩子的量
target_count = len(self.population) - len(parents)
# 开始根据需要的量进行繁殖
while len(children) < target_count:
male = random.randint(0, len(parents) - 1)
female = random.randint(0, len(parents) - 1)
if male != female:
# 随机选取交叉点
male = parents[male]
female = parents[female]
children.append(self.gen2(male, female))
children.append(self.gen2(female, male))
# 经过繁殖后,孩子和父母的数量与原始种群数量相等,在这里可以更新种群。
self.population = parents + children
def mutation(self, rate):
"""
变异
对种群中的所有个体,随机改变某个个体中的某个基因
"""
for i in range(len(self.population)):
if random.random() < rate:
m, n = random.randint(0, self.length - 1), random.randint(0, self.length - 1)
t = self.population[i][m]
self.population[i][m] = self.population[i][n]
self.population[i][n] = t
def result(self):
"""
获得当前代的最优值,这里取的是函数取最大值时x的值。
"""
graded = [(self.fitness(chromosome), chromosome)
for chromosome in self.population]
graded = [x[1] for x in sorted(graded, reverse=True)]
return self.fitness(graded[0])
if __name__ == '__main__':
ga = GA(len(dis), 1024)
# 进化次数
for i in range(200000):
ga.evolve()
if (i + 1) % 100 == 0:
print(ga.result())
扫描二维码关注公众号,回复:
1428977 查看本文章
可以使用实数数组保存基因,更加简单
[0.1,0.2,0.3,0.4]
安照对应位置的数字在数组中的大小决定是第几次遍历,上面转化后 为【1,2,3,4】
变异
随机挑选一个位置修改数字即可,比如修改下标为0的基因
[0.1,0.2,0.3,0.4] --> [0.5,0.2,0.3,0.4]
[1,2,3,4] --> [4,1,2,3]
交叉操作
普通交叉即可,不需要考虑每次数字出现一次,实现更简单,运行也更快,效果比排列稍微好一点
import math
import random
dis = []
with open('dis48.txt', encoding='utf8', mode='r') as f:
for line in f.readlines():
row = []
for i in line.strip().split(' '):
if i.strip() != '':
row.append(float(i.strip()))
dis.append(row)
def getDis(path):
d = sum([dis[path[i]][path[i + 1]]
for i in range(len(path) - 1)] + [dis[path[-1]][path[0]]])
return d
class GA():
def __init__(self, length, count):
# 染色体长度
self.length = length
# 种群中的染色体数量
self.count = count
# 随机生成初始种群
self.population = self.gen_population(length, count)
def evolve(self, retain_rate=0.1, random_select_rate=0.5, mutation_rate=0.05):
"""
进化
对当前一代种群依次进行选择、交叉并生成新一代种群,
然后对新一代种群进行变异
"""
parents = self.selection(retain_rate, random_select_rate)
self.crossover(parents)
self.mutation(mutation_rate)
def gen_chromosome(self, length):
"""
随机生成长度为length的染色体,每个基因的取值是0或1
这里用一个bit表示一个基因
"""
chromosome = [random.random() for _ in range(self.length)]
return chromosome
def gen_population(self, length, count):
"""
获取初始种群(一个含有count个长度为length的染色体的列表)
"""
return [self.gen_chromosome(length) for i in range(count)]
def fitness(self, chromosome):
"""
计算适应度,将染色体解码为0~9之间数字,代入函数计算
因为是求最大值,所以数值越大,适应度越高
"""
b = sorted([(i, index) for index, i in enumerate(chromosome)])
path = [i[1] for i in b]
return -getDis(path)
def selection(self, retain_rate, random_select_rate):
"""
选择
先对适应度从大到小排序,选出存活的染色体
再进行随机选择,选出适应度虽然小,但是幸存下来的个体
"""
# 对适应度从大到小进行排序
graded = [(self.fitness(chromosome), chromosome)
for chromosome in self.population]
graded = [x[1] for x in sorted(graded, reverse=True)]
# 选出适应性强的染色体
retain_length = int(len(graded) * retain_rate)
parents = graded[:retain_length]
# 选出适应性不强,但是幸存的染色体
for chromosome in graded[retain_length:]:
if random.random() < random_select_rate:
parents.append(chromosome)
return parents
def gen(self, fa, ma):
cross_x = random.randint(0, self.length)
c = fa[:cross_x]
d = ma[cross_x:]
ret = c + d
return ret
def crossover(self, parents):
"""
染色体的交叉、繁殖,生成新一代的种群
"""
# 新出生的孩子,最终会被加入存活下来的父母之中,形成新一代的种群。
children = []
# 需要繁殖的孩子的量
target_count = len(self.population) - len(parents)
# 开始根据需要的量进行繁殖
while len(children) < target_count:
male = random.randint(0, len(parents) - 1)
female = random.randint(0, len(parents) - 1)
if male != female:
# 随机选取交叉点
male = parents[male]
female = parents[female]
children.append(self.gen(male, female))
children.append(self.gen(female, male))
# 经过繁殖后,孩子和父母的数量与原始种群数量相等,在这里可以更新种群。
self.population = parents + children
def mutation(self, rate):
"""
变异
对种群中的所有个体,随机改变某个个体中的某个基因
"""
for i in range(len(self.population)):
if random.random() < rate:
m, n = random.randint(0, self.count - 1), random.randint(0, self.length - 1)
self.population[m][n] = random.random()
def result(self):
"""
获得当前代的最优值,这里取的是函数取最大值时x的值。
"""
graded = [(self.fitness(chromosome), chromosome)
for chromosome in self.population]
graded = [x[1] for x in sorted(graded, reverse=True)]
return self.fitness(graded[0])
if __name__ == '__main__':
ga = GA(len(dis), 128)
# 进化次数
for i in range(200000):
ga.evolve()
if (i + 1) % 100 == 0:
print(ga.result())