写在前面
这篇文章适合一些应急学习最优化算法的朋友们,对于大佬级别的同学请直接跳过,毕竟小编也只是一个和计算机没多大关系的传统工科生。
小编第一次遇到遗传算法是在大三的一次美赛中,当时需要学习一些优化算法。然而作为最优化算法的远古级别的遗传算法自然是作为了我第一个学习的目标。
在这里写下我对GA的感觉,这个算法属于智能算法,理论上是可以实现全局最优但是经过一段时间的学习,感觉对于一些复杂的函数想实现全局最优还真的需要点运气。这个算法是最经典的,后期很多类似的算法纷纷出现,粒子群算法,蚁群算法之类的。这里不做评价,小编觉得学习其中一个就好,一通百通嘛。
最后!!!!!这次我用GA算法完整的计算了一些复杂的函数。这里给出一些关键步骤和一些心得以防自己后期回来温习。(ps完整代码后期有空传上去)
遗传算法的前世今生
遗传算法的概念最早是由Bagley J.D
于1967年提出的。后来Michigan大学的J.H.Holland教授于1975年开始对遗传算法(Genetic Algorithm,
GA)的机理进行系统化的研究。遗传算法是对达尔文生物进化理论的简单模拟,其遵循“适者生存”、“优胜略汰”的原理。遗传算法模拟一个人工种群的进化过程,并且通过选择、杂交以及变异等机制,种群经过若干代以后,总是达到最优(或近最优)的状态。自从遗传算法被提出以来,其得到了广泛的应用,特别是在函数优化、生产调度、模式识别、神经网络、自适应控制等领域,遗传算法更是发挥了重大的作用,大大提高了问题求解的效率。遗传算法也是当前“软计算”领域的重要研究课题。
本文首先结合fortran对遗传算法实现过程进行详细的分析,然后通过1个实际的函数优化案例对其应用进行探讨。
算法步骤简介
很多人都已经写过遗传算法的几个基本步骤,我在这里不做赘述,直接把地址放下来,大家可以去细细的品。这个算法原来这么神奇。
https://blog.csdn.net/qq_34374664/article/details/78874956
之前对我启发很大的博客找不到了,但是看完他的文章我相信你会对GA的大致过程有了一个了解
看完记得回来看我接下来的表演哦!
哈哈哈哈哈,是不是觉得竟然如此神奇,不需要多么强的数学理解能力就可以看懂。
遗传算法的主体结构
让我们开始遗传算法的编程环节
首先,我们需要整理一下思路想一下整个计算过程需要用到哪几个函数(子程序)
1.种群初始化
2.计算种群适应度
3.种群适应度排序
4.选择(筛选)操作
5.交叉操作
6.变异操作
以上是需要用到的函数。
其次,我们需要有一定的编大型程序的意识。对于一些数据可以设置为全局变量并且一直保存,所以我们需要提前把一些经常用到的量理出来。
pop_size: 输入种群大小
chromo_size: 输入染色体长度
generation_size: 输入迭代次数
cross_rate: 输入交叉概率
cross_rate: 输入变异概率
elitism: 输入是否精英选择
(这些变量在后面的用到的时候再作解释)
开始求解:
1.种群初始化:
integer i,j,k
integer pop_size, chromo_size,pop_num
real x
call RANDOM_SEED()
do i=1,pop_size
do j=1,pop_num
do k=1,chromo_size
call RANDOM_NUMBER(x)
pop(i,j,k) = nint(x)
end do
end do
end do
语言解释:对大小为pop-size的种群的每个个体,对个体的pop-num个维度,对每个维度的chromo_size大小的染色体长度进行随机赋值。
RANDOM_SEED()__一个基本的随机数的产生函数
2.计算种群个体适应度(对不同的优化目标,此处需要改写)
integer i,j,k
integer pop_size,pop_num,chromo_size
real fitness_value1(pop_size,pop_num).
!do i=1,pop_size(单层维度,一个自变量时的初始化方法)
! fitness_value(i) = 0.
!end do
do i=1,pop_size
do j=1,pop_num
do k=1,chromo_size
if (pop(i,j,k) == 1)then
fitness_value1(i,j) = fitness_value1(i,j)+2**(k-1)
end if
end do
fitness_value1(i,j) = -500+fitness_value1(i,j)*(500-(-500))/(2**chromo_size-1)
fitness_value1(i,j) = fitness_value1(i,j)*sin(sqrt(fitness_value1(i,j))) !***** *********更改函数
fitness_value(i)=fitness_value1(i,j)+fitness_value(i)
end do
end do
3.种群排序
对个体按适应度大小进行排序,并且保存最佳个体
integer pop_size,pop_num,chromo_size
integer i,j,k,m,min,temp
integer temp1(pop_num,chromo_size)
do i=1,pop_size
fitness_table(i) = 0.
end do
min = 1
temp = 1
temp1(pop_num,chromo_size)=0
do i=1,pop_size
min = i
do j = i+1,pop_size
if (fitness_value(j)<fitness_value(min))then
min = j
end if
end do
if (min/=i)then
temp = fitness_value(i)
fitness_value(i) = fitness_value(min)
fitness_value(min) = temp
do m=1,pop_num
do k = 1,chromo_size
temp1(m,k) = pop(i,m,k)
pop(i,m,k) = pop(min,m,k)
pop(min,m,k) = temp1(m,k)
end do
end do
end if
end do
do i=1,pop_size
if (i==1)then
fitness_table(i) = fitness_table(i) + fitness_value(i)
else
fitness_table(i) = fitness_table(i-1) + fitness_value(i)
end if
end do
!fitness_table!***********************????????????????
fitness_avg(G) = fitness_table(pop_size)/pop_size
if (fitness_value(pop_size) > best_fitness)then
best_fitness = fitness_value(pop_size)
best_generation = G
end if
do i=1,pop_num
do j=1,chromo_size
best_individual(i,j) = pop(pop_size,i,j)
end do
end do
4.轮盘赌选择操作
对于分布在“函数山”上不同的种群需要对劣势的种群进行处理。处理的规则是利用第三步中计算的函数适应度的大小作为每一个种群在轮盘上的分布角度大小。
然后转动轮盘,选中谁,谁就可以活下来。从中明显可得:对于低适应度的最小的种群最不可能被选上。
如果运气奇差,导致优秀的种群被排除,我们可以对这些优秀的种群采取“保送”的方式。(这里指的注意的是对这样保送的方式需要适可而止,防治局部最优)
integer pop_size, chromo_size,elitism,pop_num
integer i,j,k,p,r,mid,first,last,idx
real x,w,q
call RANDOM_SEED()
call RANDOM_NUMBER(x)
do i=1,pop_size
r = x * fitness_table(pop_size)
first = 1
last = pop_size
w=(last+first)/2
mid = nint(w)
idx = -1
do while ((first <= last).and.(idx == -1) )
if (r > fitness_table(mid))then
first = mid
else if (r < fitness_table(mid))then
last = mid
else
idx = mid
exit
end if
q=(last+first)/2
mid = nint(q)
if ((last - first) == 1)then
idx = last
exit
end if
end do
do k=1,pop_num
do j=1,chromo_size
pop_new(i,k,j)=pop(idx,k,j)
end do
end do
end do
!**************************保送选择*************************
if (elitism==1)then
p = pop_size-1
else
p = pop_size
end if
do i=1,p
do k=1,pop_num
do j=1,chromo_size
pop(i,k,j) = pop_new(i,k,j)
end do
end do
end do
5.单点交叉操作
implicit none
integer pop_size, chromo_size,cross_rate,pop_num
integer i,j,k,cross_pos,temp
real x
call RANDOM_SEED()
do i=1,pop_size,2
do k=1,pop_num
call RANDOM_NUMBER(x)
if(x < cross_rate)then
cross_pos = nint(x * chromo_size) !交叉位置
if(cross_pos == 0.or.cross_pos == 1)then
cycle
end if
do j=cross_pos,chromo_size
temp = pop(i,k,j)
pop(i,k,j) = pop(i+1,k,j)
pop(i+1,k,j) = temp
end do
end if
end do
end do
6.变异操作
!pop_size: 种群大小
!chromo_size: 染色体长度
!cross_rate: 变异概率
subroutine mutation(pop_size, chromo_size, mutate_rate,pop_num)
use a10
implicit none
integer i,j,mutate_pos
real x
integer pop_size, chromo_size,mutate_rate,pop_num
call RANDOM_SEED()
do i=1,pop_size
do j=1,pop_num
call RANDOM_NUMBER(x)
if (x < mutate_rate)then
mutate_pos = nint(x*chromo_size)
if (mutate_pos == 0)then
cycle
end if
pop(i,j,mutate_pos) = 1 - pop(i,j, mutate_pos)
end if
end do
end do
变异操作与交叉操作类似,但是其影响大小有区别。交叉操作相当于是函数山上的种群之间进行联谊以扩大它们之间的领土,清楚他们中间的位置盲区。他是相对可控制的。反观变异操作,则其就是随机的跳动,种群可能往更好的地方去,也可能去更差的地方。这需要读者自己修改参数以得到最优的结果。
至此,求解过程完成
结果显示:
这里原函数是。。。。。参考适应度计算里的更改函数的那一行,一个三十维度的函数。
下面是主程序:
program GA
use a10
implicit none
!real,external :: fitness***********还是用子程序好用
!integer,external:: rank
!real,external ::selection
!real,external :: crossover
!real,external :: mutation
integer m(30,24),p,j,i
real n,q(30)
integer,save::pop_size !种群大小
integer ,save::pop_num !单个种群维度
integer,save::chromo_size !染色体大小********** *********更改
integer ,save::elitism !选择精英操作
integer ,save::cross_rate !交叉概率
integer ,save::mutate_rate !变异概率
!function [m,n,p,q] = GeneticAlgorithm(pop_size, chromo_size, generation_size, cross_rate, mutate_rate, elitism)
elitism = 1 !选择精英操作
pop_size = 1000 !种群大小
pop_num=30 !维度30
chromo_size = 24 !染色体大小
generation_size = 200 !迭代次数
cross_rate = 0.5 !交叉概率
mutate_rate = 0.01 !变异概率
!print *, '开始了'
fitness_avg = 0.
fitness_value(pop_size) = 0.
best_fitness = 0.
best_generation = 0
call initilize(pop_size,pop_num,chromo_size) !初始化
do G=1,generation_size
call fitness(pop_size,pop_num,chromo_size) !计算适应度
call rank(pop_size,pop_num,chromo_size) !对个体按适应度大小进行排序
call selection(pop_size, chromo_size,elitism,pop_num) !选择操作
call crossover(pop_size, chromo_size, cross_rate,pop_num) !交叉操作
call mutation(pop_size, chromo_size, mutate_rate,pop_num) !变异操作
end do
!**** ******************matlab中打印图像****************
m = best_individual !获得最佳个体
n = best_fitness !获得最佳适应度
p = best_generation !获得最佳个体出现代
!获得最佳个体变量值,对不同的优化目标,此处需要改写
q = 0.
do i=1,pop_num
do j=1,chromo_size
if (best_individual(i,j) == 1)then
q(i)= q(i)+2**(j-1)
end if
end do
!!!!!!!!!!!!!!!!!*********************************更改为对应的自变量的值**********************
q(i) = -500+q(i)*(500-(-500))/(2**chromo_size-1)
end do
write(*,*)"最优个体"
write(*,*) m
write(*,*)"最优适应度"
write(*,*) n
write(*,*)"最优个体自变量"
write(*,*) q
write(*,*)"最优代数"
write(*,*) p
end program GA
因为计算了很多函数没有及时保存最原始的一份,中间可能有点对应不上。但是大致思路都已经显示出来了
最后再来说一些需要注意的地方
- 需要了解二进制与十进制之间的转化。这里是运用插值的手法。
- 需要了解fortran中大型程序编写的模块化,以便于数据调用。
最后吧,时间有限,小编又比较懒,有空再来补充。以便于自己以后复习吧。
小编是水工结构的研究生,如果你读到这里了,大家可以多交流。