目录
1 问题描述
建筑企业是面向项目的一类企业,每一个工程项目的建设过程分为若干个施工作业,这些施工作业要满足一定的前后关系,并且一部分施工作业的开始时间在一定范围内可以调整。每一项施工作业都要耗费资源,如材料、劳动力、机器工时,资源耗用的过程就是承包商成本发生的过程,也是资金耗用的过程。资源的使用量依据每一项施工活动的内容和开始时间确定。每月末,项目部会从业主那里收取到工程进度款,形成项目的资金收入,但是同时在月末的时候还需要缴纳各种间接费用,如管理费、措施费、规费等。由于建筑工程往往耗资巨大,因此工程建设过程中施工企业仅仅依靠自有的资金和业主支付的工程进度款是不够的,项目部经常需要向公司借款,公司会规定一个贷款的限额(200W),同时项目部也根据贷款金额和利息率想公司支付利息,所以项目部可以在一定范围内通过调整各个施工作业的开始时间,来达到调整项目资金使用量的目的,使得项目建设过程中的资金使用量不会超过贷款限额,同时贷款利息最少,即项目利润最大。但是,利息的多少和工期完成是直接关联的,如果在施工的过程中,贷款少,利息少了,那么有可能会造成工期延长,那么也许会使得间接费用管理费、规费、措施费等太大多贷款造成的利息,这样就不符合优化原则,同时如果贷款过多,也许会造成利息过大,同时也会增大整体项目的开销。因此,问题就是要寻找一种最优的方法,使得贷款造成的利息和工期生成的间接费用达到平衡从而使得项目开销最少,利润最大。
2 关于遗传算法的生物基础
19世纪中叶,查尔斯·达尔文(Charles Darwin)在总结前人进化思想的基础上,用大量的科学事实证明了生物进化过程总体上表现为:从低级到高级,从简单到复杂,从不完善到完善,从单一适应到多种适应,从低的有序性到高的有序性,以及沿着物种数目日益增多的方向发展进化。达尔文认为,生物进化的动力和机制在于自然选择,自然选择是以变异为基础,并通过生存竞争实现。凡是具有适应环境的有利变异的个体,在生存竞争中将有更多机会生存和繁殖后代,而较差适应性的个体将被淘汰。因此,生物进化便是“物竞天择,适者生存”的过程。1975年,美国密歇根大学(University of Michigan)的心理学教授、电子工程学与计算机科学教授John.H.Holland研究出了具有开创意义的遗传算法理论和方法。遗传算法是一种借鉴生物界自然选择和进化机制发展起来的高度并行、随机、自适应搜索算法。它使用了群体搜索技术,用种群代表一组问题解,通过对当前种群施加选择、杂交和变异等遗传操作,从而产生新一代种群,并逐步使种群进化到包含有近似最优解的状态。由于其处理问题的简单性,遗传算法已经被广泛使用。近年来,遗传算法与问题求解、优化组合、智能控制等一道成为计算智能研究中的热点,受到了广泛的关注。
3 算法描述
通过实际项目的问题描述,我们不难知道,算法主要解决的问题是对整个建筑项目造成的资金耗费的一个优化,通过算法的优化可以给出一套使得整个项目开销最少,而且同时工期也尽量最短的方案。根据遗传算法的性质,这里主要考虑的是资金问题,因为工期以及进度款等的因素会造成贷款的情况,而贷款又会产生利息,这样就会造成开销增大,但是如果不贷款,而是选择工期的延后,那么又可能会造成整个工期的增长,从而增大了每月的间接费用以及罚款,而算法要做的就是给出一个合理的优化,使得贷款金额以及工期延后造成的开销达到一个平衡。
3.1 算法设计
根据实际的项目,我们需要解决如下的几个方面的问题:
① 选择编码方式,并且给出一个包含各个工序号的顺序码,用来确定可以并行开工的工序的先后开工顺序。
② 根据各个工序的不同的先后顺序以及工序见开工与完成的限制条件,需要给出一个正确的各个工期的开工排序码。
③ 设计一种可以尽量使得适应度最优的贷款策略,设计一条贷款码,为每道工序都设计一个贷款量,并随着编码的交叉和变异也进行变化,从而使得贷款码能够符合遗传算法的要求。
④ 关于算法的选择、交叉、变异和淘汰策略的选择和制定。
⑤ 适应度函数的设计。
3.2 遗传算法的基本流程
遗传算法的3个主要操作是:选择(selection)算子、交叉(crossover)算子、变异(mutation)算子,遗传算法中包含了如下的5个基本要素:
①对参数进行编码;
②设定初始种群大小;
③适应度函数的设计;
④遗传操作设计;
⑤控制参数设定(包括种群大小、最大进化代数、交叉率、变异率等)。
3.3 编码方式-解的标识
任何单个的n个工序的排列表示一个解(对n个工序的数序完工),近似最优解是利润最大,资金耗费最少的那个。若采用二进制编码表示,对次问题的解决没有任何的优点。相反,若采用二进制代码表示,还要求有特殊的修补算法,因为单个位的变化可能引起一个非法的工序。于是,本算法中对解(群体中的个体)的表示统一采用整数编码的方法。如:“1—3—2—5—4—9—7—8—6”表示一个染色体(个体或路径),每一个整数代表一个顶点(工序)的编号。
3.4 适应度的评估
此类问题的适应度函数从直观上来说还是比较容易的,但是调度问题存在许多种情况和各种其他的外部条件约束,因此在实现的时候是比较困难的。但是对于任何可能的解,我们是都可以计算出其最后消耗的资金量和剩余的资金量,而这里就将整个工序的总得资金耗费取作它的适应函数值。在项目群体中,我们可以非常容易地比较其中任何两个染色体的适应函数值,而同样在本算法中对适应函数的评估是通过计算群体中单个染色体在整个工程完结过程中所发生资金耗费包括直接工程费、间接工程费、贷款利息、罚金等之和作为适应函数值来实现的。
3.5 算法参数设置
maxruns:算法执行次数 lchrom:染色体长度 maxgen:最大时世代数
popsize:种群大小 pcross:交叉概率 pmutation:变异概率
3.6 产生初始种群的策略
设置一个临时数组tmp[],用于存储待处理的lchrom个工序的工序号。在tmp[]数组的lchrom个工序号中选择其中一个,记下该工序在临时数组中的位置a,且将这一工序作为初始群体中第一个个体的顺序码中的第一个工序,然后将tmp[]数组中位置为a和lchrom的两个元素交换;再在tmp[]数组的前lchrom-1个工序中随机选择一个工序,记下该工序在临时数组中的位置a,且将这一工序作为初始群体中第一个个体的顺序码中的第二个个体,然后将tmp[]数组中位置为a和lchrom-1的两个元素交换。依此类推,循环lchrom次,即产生了初始群体的第一个个体的顺序码。用以上同样的方法循环POPSIZE次,即可生成初始群体。
4 算法细节
4.1 轮盘赌策略
赌轮盘选择个体方法类似于搏彩游戏中的赌轮盘。群体中的每个个体的适应度值按比例转化为选中概率,将轮盘分成POPSIZE个扇区。产生一个[0,1]之间的随机数m,然后从第一个个体的选中概率开始累加,直到累加概率之和t大于或等于m,将最后被累加选中概率的个体挑选出来。这一操作相当于转动一次轮盘,被选中的个体相当于轮盘停止转动时,指针所指向的轮盘扇区。不过,在处理本实例问题时,由于最优解为群体中适应度值最小的个体,因此,在采用赌轮盘策略时应将群体中的各个个体的适应度值按反比例转化为选中的概率。
4.2 选择算子
选择算子其实就是赌轮盘策略,但是在本实例中,从原始种群中选择新种群的时候,采用的是4选2的选择策略。
在进行交叉和变异之前,需要从oldpop种群中通过赌轮盘策略选择两个个体,然后进行交叉和变异操作会生成两个新个体,那么在像newpop种群中插入新的个体的时候,根据遗传算法的基本原理,我们采用了4选2的策略,从新旧四个个体中选择出两个最优的个体。从而来保证生成的新的种群中不会因为交叉和变异的因素导致优秀的解丢失。
4.3 杂交算子
杂交策略采用的是顺序(OX)杂交策略。
算法的基本思想是:通过赌轮盘策略,从父代中选择两个个体,利用顺序杂交(OX)策略,通过从一个亲体中挑选一个个体的工序序列的子序列并保存另一个亲体的个体的工序序列的相对次序来构造两个新的个体,生成两个新个体,然后采用选择算子将得到的两个个体添加到子代中去。下面详细介绍一下本实例中采用的顺序杂交策略。
假如,有Parent1、Parent2两个父个体(切割点以“|”来区分):
Parent1: 1 2 3 |4 5 6 7| 8 9 Parent2: 4 5 2| 1 8 7 6| 9 3
通过杂交之后,得到子代个体为:
Child1: 2 1 8 |4 5 6 7| 9 3 Child2: 3 4 5 |1 8 7 6 |9 2
在本例中,可以看到,染色体长度lchrom为9,那么从父代进行杂交生成子代要经历一下几个步骤:
① 先随机选择p1、p2点,p1=rand()%( lchrom+1) ;p2=rand()%( lchrom+1) ,然后进行判断,最后使得p2-p1>=1,这时候就取得p1和p2了,这里根据生成的子代我们是假设取得p1=3,p2=7的。
② 取出Parent1中4 5 6 7和P2中的1 8 7 6 分别放到Child1、Child2中对应的位置中 (借助p1、p2) 。
③ 生成Child1,将Parent2中的数据从9开始依次从后转到前面开始存到一个数组tmp [lchrom] 中顺序为:9 3 4 5 2 1 8 7 6。
然后向Child1中插入数据,从tmp中依次取出数据,拿每次取出的数据同Child1中已经存在那几个数据一一对比(这里随着向Child1中不断插入数据,所以,Child1中的数据个数也在变化),如果和Child1中已经存在的相同的数据,那么舍去;如果没有存在与之相同的数据,那么将这个数据顺序插入到Child1中去,这样重复操作,直到tmp数组中数据轮询完毕。同样可以生成Child2。
4.4 变异算子
变异算子的设计相对来说不如交叉算子复杂,其基本思想是:随机从父代中选择两个个体,然后随机选取个体中的两个元素,再交换它们的位置。然后将经变异后的两个个体插入到子代中。
假如,有Parent1、Parent2两个父个体(切割点以“|”来区分):
Parent1: 1 2 |3| 4 5 6 7 |8| 9 Parent2: 4 5 |2| 1 8 7 6 |9| 3
通过变异算子,得到的子代如下:
Child1: 1 2 |8| 4 5 6 7 |3| 9 Child 2: 4 5 |9| 1 8 7 6 |2| 3
变异操作最开始其实和交叉类似,都是需要先从父代种群中随机的取出两个子代,然后,通过随机函数,选取两个点p1,p2,这里,p1不等于p2,而且p1和p2都是小于染色体长度的,取得亮点之后,在父代中找到它们对应的位置,并交换对应位置上的数字,这样就完成了变异。
4.5 贷款码设计
贷款码的设计,类似工序的编码设计,也选择顺序编码,为每一道工序都设计一个贷款码,在发生贷款的时候,根据当前工序对应的贷款码进行计算然后得出贷款金额即可。在本项目中,设计的贷款码长度为10,没道工序对应的贷款码都是从0-10之间的随机数。在生成初始种群的时候,同样也会为每一道工序都附上他们的随机的贷款码。
在父个体交叉生成子个体的时候,贷款码也是会随着变化,之前关于交叉的时候可以知道,在染色体中,有些个体的位置不发生变化,那么它们对应的贷款码也不发生变化,主要变化的部分就是发生变化的部分,发生变化的部分的贷款码,等于它在父个体1中原来的贷款码加上它在新个体中的位置对应的父个体中的贷款码之和除以2。这里可能会存在有小数的情况,那么在程序中采取了变相的四舍五入的操作,即,如果得到的数字下于1的话就让等于1,其他的按照四舍五入的做法来取。
变异的时候,相对来说就比较简单,这里面采取直接进行交换个体的贷款码也随着个体移动而移动。
4.6 淘汰策略
淘汰思想:由两个父代个体生成两个子代个体,但是在选择新种群的个体的时候是从新旧四个个体中选择最优的两个个体放入到新的种群中去的,这样使得最优策略始终都能存在。
5 解函数的设计
关于解函数,其实就是适应度函数的设计。在本项目中,关于适应度函数的设计有一条主线,那就是将给定的无序的工序的顺序码按照各个工序间的先后约束关系以及各个工序间的前后完成开工约束来给出一条符合实际要求的正确的排序码,同时在生成这条排序码的过程中,来计算整个工程的开销、各个工序间的开工时间、结束时间、延后时间、前提时间、贷款金额、利息等等。而根据实际的工程问题,在适应度函数中,需要先独自处理第一道工序,并且还需要独自处理最后一道工序,然后中间的工序交给核心逻辑函数来处理。