机器学习--遗传算法解决八数码问题(含python代码)

之前课程上讲了一下遗传算法,也用代码实现了。今天就顺手写一下文章总结一下好了。

以下来自百度百科:

遗传算法的基本运算过程如下:
a)初始化:设置进化代数计数器t=0,设置最大进化代数T,随机生成M个个体作为初始群体P(0)。
b)个体评价:计算群体P(t)中各个个体的适应度。
c)选择运算:将选择算子作用于群体。选择的目的是把优化的个体直接遗传到下一代或通过配对交叉产生新的个体再遗传到下一代。选择操作是
立在群体中个体的适应度评估基础上的。
d)交叉运算:将交叉算子作用于群体。遗传算法中起核心作用的就是交叉算子。
e)变异运算:将变异算子作用于群体。即是对群体中的个体串的某些基因座上的基因值作变动。
群体P(t)经过选择、交叉、变异运算之后得到下一代群体P(t+1)
f)终止条件判断:若t=T,则以进化过程中所得到的具有最大适应度个体作为最优解输出,终止计算。

个人理解:
达尔文的进化论,主要是指自然界的优胜劣汰,自然对生物的选择,生物对自然界的适应而得到进化。
孟德尔的遗传学说,主要指生物的进化主要是通过染色体之间的交叉和变异来完成的。

而遗传算法中,
通过编码,将问题问题的解表示成种群的个体(染色体),适应度函数就是对种群中个体的选择的条件,也是种群中个体进化的方向。
通过适应度函数和选择算子,实现对生物的选择
通过交叉算子和变异算子,实现生物的进化

则:(1)遗传算法是一种随机算法,
    (2)产生近似最优解,(不一定是最优解)
    (3)通过编码表示具体的问题的解,对个体(染色体)解码后可得到一组可行解
    (4)随机生成候选解,编码后产生初代种群。
    (4)基于具体问题的适应度函数相当于自然界的对生物选择条件,通常适应度函数和解的优劣成正比。
    (5)
选择算子:选择算子通过适应度函数得到的种群中个体的适应度,实现对候选解的的选择,选择较优的解,淘汰较差的解。
 选择出的个体参加,再按照一定的概率发生交叉运算、变异运算,遗传到下一代,或直接遗传到下一代。
 轮盘赌选择法 (roulette wheel selection)是最简单也是最常用的选择方法。在该方法中,各个个体的选择概率和其适应度值成比例。
交叉算子:实现两个个体的部分基因结构加以替换重组而生成新个体的。单点交叉,两点交叉等。
变异算子:基本内容是对群体中的个体串的某些基因座上的基因值作变动。改变某个,或某连续几个基因组
     (6)精英遗传策略:即适应度高的个体,直接遗传到下一代

首先介绍八数码问题就是:


图中0可以上下左右移动,找到从状态1变为状态2的路径。


那么遗传算法具体到八数码问题中:
(1)编码:0,1,2,3  分别代表上下左右
(2)适应度函数:
{100 - i * 10}求和i属于(0, 8),终止状态的值为540;
(3)选择算子:一种改进的轮盘赌选择法(适应度越高留下的概率越高)
(4)交叉算子:两点交叉法,将两个染色体的某个区间交换
(5)变异算子:随机选择某位改变
(6)精英遗传策略:直接将适应度高的个体遗传到下一代
(7)初代种群,通过随机产生
(8)所有的随机数都是通过rand()产生的


我举个例子吧,首先初始化基因组,假设有两组且长度为5:[3,3,1,2,3]、[1,3,2,1,0]

0,1,2,3都是随机产生分别代表上下左右。

初始状态为[0,1,2,3,4,5,6,7,8] 目标状态为[1,2,0,3,4,5,6,7,8].

首先看第一个基因,第一次操作为3,就是向右,所以状态变为[1,0,2,3,4,5,6,7,8],重新计算适应度函数,发现不到540。再移一步变成[1,2,0,3,4,5,6,7,8],与目标状态吻合,适应度函数为540说明找到了解。

若没有找到解,则在向下一代进行遗传的时候进行选择、交叉和变异。

具体看注释:

import  math
import  random
C = 540                           #最大适应度
LEN = 140                          #基因长度
maxGene = []
maxi = 0                          #最大值最初出现的进化代数
findAim = False
POPMAX = 32                   #种群数量
P_XOVER = 0.8                    #交叉概率
P_MUTATION = 0.15               #变异概率
MAXGENERATIONS = 1000             #总的进化代数
goal = [1,2,3,8,0,4,7,6,5] # 目标状态
initiate = [2,8,3,1,0,4,7,6,5] #  初始状态
# initiate = [1,0,2,3,4,5,6,7,8]
# goal=[0,1,2,3,4,5,6,7,8]
pop = []                           #种群所有对象


class Gene:
  def __init__(self,gene):
      self.gene = gene            #基因,数组
      self.fitness = 0
      self.rf = 0    #选择的概率
      self.cf = 0    #累积的概率

#随机初始化基因组
def initGenes() :
  count = 0
  maxFit = 100    #随机生成的基因适应度的最大值
  while(count < POPMAX):
      tmp = []
      for j in range(LEN):
          pow = round(random.random() * 3)    #随机生成0上,1下, 2左, 3右
          tmp.append(pow)

      pop.append(Gene(tmp))
      count+=1

#上下左右操作
def move (current, dire):
     space = 0 # 空格位置
     block = 0 # 移动的格子的位置
     for i in range(len(current)):
        if (current[i] == 0):
            space = i
            block = space
            break


     if (dire == 0) :
        if (space - 3 >= 0):
            block = space - 3

     elif(dire == 1 and (space + 3 < 9)) :
            block  = space + 3
     elif(dire == 2) :
          if (space % 3 > 0) :
             block = space - 1

     elif(dire == 3) :
        if (space % 3 < 2) :
            block = space + 1


     current[space], current[block]= current[block], current[space]
     if (space == block):
        return False
     else:
        return True

#计算适应度
def fitness(current) :
    f = 0
    for i in range(len(current)):
        if (current[i] == goal[i]) :
            f += 100 - current[i]*10
    return f


def envaluateFitness(maxi):          #max参数只是用来记录进化代数
  totalFitness = 0
  for i in range(POPMAX):
      s0 = initiate[:] # 每一步移动后的状态
      pop[i].fitness = 0
      for j in range(LEN):
          #每移动一次后计算一次适应度函数,若为540说明已找到解
        move(s0, pop[i].gene[j])
        pop[i].fitness = fitness(s0)
        if (pop[i].fitness == C) :
            global findAim
            findAim = True
            global maxGene
            maxGene = pop[i].gene[0:j+1]
            return totalFitness


      if(pop[i].fitness == 0) :
        pop[i].fitness = 1

      totalFitness += pop[i].fitness

  return totalFitness

#适应度更高的基因有更高的概率往下遗传
def selectBetter(totalFitness): # 轮盘赌选择
  lastCf = 0
  newPop = [None for i in range(POPMAX)]
  global pop
  for i in range(POPMAX):        #计算个体选择概率和累积概率
      pop[i].rf = pop[i].fitness / totalFitness
      pop[i].cf = lastCf + pop[i].rf
      lastCf = pop[i].cf

  for i in range(POPMAX):      #轮盘赌式选择
      p = random.random()
      if(p < pop[0].cf):
          newPop[i] = Gene(pop[0].gene)
          # newPop.append(Gene(pop[0].gene))
      else:
          for j in range(POPMAX-1):
              if(p >= pop[j].cf and p < pop[j+1].cf):
                  newPop[i] = Gene(pop[j+1].gene)
                  # newPop.append(Gene(pop[j+1].gene))
                  break

  if (len(newPop) == 0) :
      # console.log(pop)
      print(pop)
  import  copy
  pop= copy.deepcopy(newPop)

def exChgOver(first,second):         #基因交换函数
  ecc = round(random.random() * LEN)
  for i in range(int(ecc)):
      index = math.floor(random.random() * LEN)
      pop[first].gene[index], pop[second].gene[index] = pop[second].gene[index], pop[first].gene[index]



# 交叉
def crossover():
  first = -1
  for i in range(POPMAX):
      p = random.random()
      if(p < P_XOVER):
          if(first < 0):
              first = i
          else:   #选择了两个随机个体,进行基因交换
              exChgOver(first,i)
              first = -1



def reverseGene(index):        #变异操作函数
  mcc = round(random.random() * LEN)
  for i in range(int(mcc)):
      gi = math.floor(random.random() * LEN)
      pop[index].gene[gi] = 3 - pop[index].gene[gi]


# 变异
def mutation():
  for i in range(POPMAX):
      p = random.random()
      if(p < P_MUTATION):       #只有当随机数小于变异概率才进行变异操作
          reverseGene(i)



initGenes()
f = envaluateFitness(0)
for i in range(MAXGENERATIONS):
    selectBetter(f)
    crossover()
    mutation()
    f= envaluateFitness(i)
    if (findAim) :
        break

# console.log(maxGene);

def transform (gene) :
    s0 = initiate[:]
    options = []
    for i in range(len(gene)):
        if (move(s0, gene[i])) :
            if (gene[i] == 0) :
                options.append('上')
            elif (gene[i] == 1) :
                options.append('下')
            elif (gene[i] == 2) :
                options.append('左')
            elif (gene[i] == 3):
                options.append('右')

    print(options)
    print(s0)


transform(maxGene)

# let m = maxGene
# let s0 = initiate.slice(0)
# for (let i = 0; i< m.length; i++) {
#     move(s0, m[i])
# }
# console.log(s0)

猜你喜欢

转载自blog.csdn.net/wenqiwenqi123/article/details/80804375
今日推荐