运用遗传算法求解函数极值(fortran)

写在前面

这篇文章适合一些应急学习最优化算法的朋友们,对于大佬级别的同学请直接跳过,毕竟小编也只是一个和计算机没多大关系的传统工科生。

小编第一次遇到遗传算法是在大三的一次美赛中,当时需要学习一些优化算法。然而作为最优化算法的远古级别的遗传算法自然是作为了我第一个学习的目标。

在这里写下我对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

因为计算了很多函数没有及时保存最原始的一份,中间可能有点对应不上。但是大致思路都已经显示出来了

最后再来说一些需要注意的地方

  1. 需要了解二进制与十进制之间的转化。这里是运用插值的手法。
  2. 需要了解fortran中大型程序编写的模块化,以便于数据调用。

最后吧,时间有限,小编又比较懒,有空再来补充。以便于自己以后复习吧。
小编是水工结构的研究生,如果你读到这里了,大家可以多交流。

发布了1 篇原创文章 · 获赞 0 · 访问量 58

猜你喜欢

转载自blog.csdn.net/lameta/article/details/102652779