学会了前面两篇遗传算法,但是那都是针对抽象的数学问题求解的,那么应用到现实中的实际问题中,我们又该怎样把遗传算法套进去呢,然后我第一个接触到的问题就是车辆路径优化问题VRP,当然也是找到了一篇比较好的文章,物流管理论文实现:基于遗传算法的带时间窗和载重约束的车辆路径优化
这位博主的代码写的非常不错,因为我复制过来运行的时候没有报错,但是,看的时候也比较费劲,因为这个博主比较厉害,他在里面定义了一个类class,然后就会有很多self,与普通的定义函数有了些许的差别,导致我这种小白,看起来特别不舒服,不知道具体的选择、交叉和变异操作是怎么实现的,所以我就凭借前两篇的学习和自己的理解,把代码重新写了一遍,其中目标函数原本比较复杂,也被我简化了,只需要求距离最短即可,因为我就想看一下这个遗传算法套在实际问题里的样子,当然越简单越好,我觉得我能看懂,大部分人应该都可以了。
而且在这个过程中,还学会了一个技巧,在出现错误运行不下去的时候,就加个try,except,把错误跳过去,继续运行,就能得出结果了。
先定义一下问题里给的条件,初始化参数
geneNum = 100 # 种群数量
generationNum = 300 # 迭代次数
CENTER = 0 # 配送中心
# HUGE = 9999999
# PC = 1 #交叉率,没有定义交叉率,也就是说全部都要交叉,也就是1
PM = 0.1 # 变异率 以前是用的vary
n = 25 # 客户点数量
m = 2 # 换电站数量
k = 3 # 车辆数量
Q = 5 # 额定载重量, t
# dis = 160 # 续航里程, km
length = n+m+1
# 坐标 第0个是配送中心 1-25是顾客 26和27是换电站 一共28个位置 行驶距离要通过这个坐标自己来算
X = [56, 66, 56, 88, 88, 24, 40, 32, 16, 88, 48, 32, 80, 48, 23, 48, 16, 8, 32, 24, 72, 72, 72, 88, 104, 104, 83,32]
Y = [56, 78, 27, 72, 32, 48, 48, 80, 69, 96, 96, 104, 56, 40, 16, 8, 32, 48, 64, 96, 104, 32, 16, 8, 56, 32, 45, 40]
# 需求量
t = [0, 0.2, 0.3, 0.3, 0.3, 0.3, 0.5, 0.8, 0.4, 0.5, 0.7, 0.7, 0.6, 0.2, 0.2, 0.4, 0.1, 0.1, 0.2, 0.5, 0.2, 0.7,0.2,0.7, 0.1, 0.5, 0.4, 0.4]
编码:根据实际问题来编码,那就采用实数编码好了,需要求得内容都放到染色体里面
分两步才能产生符合条件的初始个体,先产生无序列表,并在首尾位置插入配送中心0,然后再根据一辆车运输的需求量总和不超过车的负载,往这个无序列表里面随机插入0作为从配送中心新的开始,也就表示了有几辆车
注意:这里产生初始种群就不像前两篇的纯数学问题那么简单了,还要写成一个函数,才能产生满足要求的初始解,也就是初始种群。
def getGene(length):
##先产生一个无序的列表
data = list(range(1,length)) ##先产生一个有序的列表
np.random.shuffle(data) ##有序列表打乱成无序列表
data.insert(0, CENTER) ##在开始插入0
data.append(CENTER) ##在结尾插入0
#再插入车
sum = 0
newData = []
for index, pos in enumerate(data):
sum += t[pos]
if sum > Q:
newData.append(CENTER)
sum = t[pos]
newData.append(pos)
return newData
def getpop(length,geneNum):
pop = []
for i in range(geneNum):
gene = getGene(length)
pop.append(gene)
return pop
计算适应度值,计算一个个体的适应度值,然后得到整个种群的适应度列表
注意:适应度值就是距离函数,需要根据各个点的坐标自己表示出来
##计算一个个体的适应度值
def getfit(gene):
distCost = 0
dist = [] # from this to next
# 计算距离
i = 1
while i < len(gene):
calculateDist = lambda x1, y1, x2, y2: math.sqrt(((x1 - x2) ** 2) + ((y1 - y2) ** 2))
dist.append(calculateDist(X[gene[i]], Y[gene[i]], X[gene[i - 1]], Y[gene[i - 1]]))
i += 1
# 距离成本
distCost = sum(dist) #总行驶距离
fit = 1/distCost ##fitness越小表示越优秀,被选中的概率越大,
return fit
##得到整个种群的适应度列表
def getfitness(pop):
fitness = []
for gene in pop:
fit = getfit(gene)
fitness.append(fit)
return np.array(fitness)
选择,利用轮盘赌,适应度值越大越有可能被选择出来到下一代
def select(pop,fitness):
fitness = fitness / fitness.sum() # 归一化
idx = np.array(list(range(geneNum)))
pop_idx = np.random.choice(idx, size=geneNum, p=fitness) # 根据概率选择
for i in range(geneNum):
pop[i] = pop[pop_idx[i]]
return pop
这里的交叉也比较麻烦,因为在这个问题里面不能随便交叉,因为如果你用5交叉换回来一个6,但是其实这个个体里面已经有6了,每个客户只能拜访一次,这就不符合问题规定了,所以要进行一些操作铺垫
选择路径实现的效果如下:

然后再是两个个体的交叉,效果如下:

拿gene1来说,就是把gene2里面有的,但是gene1前面那个路径里面没有的数字加到gene1的后面,得到newgene1,newgene2也是同理,然后就交叉完了。
并且只选择了适应度较高的前1/3种群进行交叉,不用交叉概率的,所有的都要交叉,但是交叉完得到的种群也只有1/3的个体,所以再把这1/3的个体替换到原种群最后面适应度较低的那一部分,最终合并成一个完整的种群。
#选择路径
def moveRandSubPathLeft(gene):
import random
path = random.randrange(k) # 选择路径索引,随机分成k段
print('path:',path)
try:
index = gene.index(CENTER, path+1) #移动到所选索引
# move first CENTER
locToInsert = 0
gene.insert(locToInsert, gene.pop(index))
index += 1
locToInsert += 1
# move data after CENTER
print('index:',index)
try:
print('len(gene):',len(gene))
while gene[index] != CENTER:
gene.insert(locToInsert, gene.pop(index))
index += 1
print('执行完index+1,index=',index)
locToInsert += 1
return gene
# assert(length+k == len(gene))
except:
print('出错啦,index:',index)
return gene
except:
print('0 is not in list',gene)
return gene
# 选择复制,选择适应度最高的前 1/3,进行后面的交叉
def choose(pop):
num = int(geneNum/6) * 2 # 选择偶数个,方便下一步交叉
# sort genes with respect to chooseProb
key = lambda gene: getfit(gene)
pop.sort(reverse=True, key=key) ##那就是说按照适应度函数降序排序,选了适应度值最高的那1/3
# return shuffled top 1/3
return pop[0:num]
交叉 这个就不考虑交叉概率了,因为轮流所有的都会交叉,但是代码先写交叉一对,再把前面选择出来的适应度较高的前1/3种群进行交叉
##交叉一对
def crossPair(i,gene1, gene2, crossedGenes):
gene1 = moveRandSubPathLeft(gene1)
gene2 = moveRandSubPathLeft(gene2)
newGene1 = []
newGene2 = []
# copy first paths
centers = 0
firstPos1 = 1
for pos in gene1:
firstPos1 += 1
centers += (pos == CENTER)
newGene1.append(pos)
if centers >= 2:
break
centers = 0
firstPos2 = 1
for pos in gene2:
firstPos2 += 1
centers += (pos == CENTER)
newGene2.append(pos)
if centers >= 2:
break
# copy data not exits in father gene
for pos in gene2:
if pos not in newGene1:
newGene1.append(pos)
for pos in gene1:
if pos not in newGene2:
newGene2.append(pos)
# add center at end
newGene1.append(CENTER)
newGene2.append(CENTER)
# 计算适应度最高的
key1 = lambda gene1: getfit(gene1)
possible1 = []
try:
while gene1[firstPos1] != CENTER:
newGene = newGene1.copy()
newGene.insert(firstPos1, CENTER)
possible1.append(newGene)
firstPos1 += 1
print('第{}位置:{}'.format(i,len(possible1)))
if len(possible1) == 0:
crossedGenes.append(newGene1)
else:
possible1.sort(reverse=True, key=key1)
crossedGenes.append(possible1[0])
except:
print('交叉出错啦:firstPos1', firstPos1)
key2 = lambda gene2: getfit(gene2)
possible2 = []
try:
while gene2[firstPos2] != CENTER:
newGene = newGene2.copy()
newGene.insert(firstPos2, CENTER)
possible2.append(newGene)
firstPos2 += 1
print('第{}:{}'.format(i,len(possible2)))
if len(possible2) == 0:
crossedGenes.append(newGene2)
else:
possible2.sort(reverse=True, key=key2)
crossedGenes.append(possible2[0])
print('交叉完成第:', i)
except:
print('交叉出错啦:',i)
# 交叉
def cross(genes):
crossedGenes = []
for i in range(0, len(genes), 2):
# print('gene[i]:',genes[i])
# print('gene[i+1]:', genes[i])
crossPair(i,genes[i], genes[i+1], crossedGenes)
print('交叉完成')
return crossedGenes
# 合并
def mergeGenes(genes, crossedGenes):
# sort genes with respect to chooseProb
key = lambda gene: getfit(gene)
genes.sort(reverse=True, key=key) ##先把原来的种群100按照适应度降序排列,然后,将交叉得到的32个个体替换到种群的最后32个
pos = geneNum - 1
for gene in crossedGenes:
genes[pos] = gene
pos -= 1
return genes
变异,先写一下个体怎么变异,然后再根据变异概率对整个交叉完的种群变异
注意:这里的变异就很简单了,直接用产生初始种群中个体的方法产生一个新的个体,但是这里也采用了,多产生几个个体,选其中适应度最高的那个个体的方法来减小误差。
# 变异一个
def varyOne(gene):
varyNum = 10
variedGenes = []
for i in range(varyNum): # 先按照这种方法变异10个,选择适应度最高的那个作为变异完的子代
p1, p2 = random.choices(list(range(1,len(gene)-2)), k=2)
newGene = gene.copy()
newGene[p1], newGene[p2] = newGene[p2], newGene[p1] # 交换
variedGenes.append(newGene)
key = lambda gene: getfit(gene)
variedGenes.sort(reverse=True, key=key)
return variedGenes[0]
# 变异
def vary(genes):
for index, gene in enumerate(genes):
# 精英主义,保留前三十,这个意思就是前三十个一定不变异,到后面的个体才按照变异概率来变异
if index < 30:
continue
if np.random.rand() < PM:
genes[index] = varyOne(gene)
return genes
遗传算法主体
import numpy as np
import random
from tqdm import * # 进度条
import matplotlib.pyplot as plt
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
best_fitness = []
min_cost = []
J = []
pop = getpop(length, geneNum) # 初始种群
# 迭代
for j in tqdm(range(generationNum)):
print('j=',j)
chosen_pop = choose(pop) # 选择 选择适应度值最高的前三分之一,也就是32个种群,进行下一步的交叉
crossed_pop = cross(chosen_pop) # 交叉
pop = mergeGenes(pop, crossed_pop) # 复制交叉至子代种群
pop = vary(pop) # under construction
key = lambda gene: getfit(gene)
pop.sort(reverse=True, key=key) # 以fit对种群排序
cost = 1/getfit(pop[0])
print(cost)
min_cost.append(cost)
J.append(j)
print(J)
print(min_cost)
# key = lambda gene: getfit(gene)
# pop.sort(reverse=True, key=key) # 以fit对种群排序
print('\r\n')
print('data:', pop[0])
print('fit:', 1/getfit(pop[0]))
plt.plot(J,min_cost, color='r')
plt.show()
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru
我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht