遗传算法Python实现(Genetic Algorithm,GA)

遗传算法Python实现

  • 一、GA算法介绍
  • 二、GA算法流程
  • 三、Python实现
  • 四、优缺点及应用
    • 1、不足之处
    • 2、特点
    • 3、应用

一、GA算法介绍

  遗传算法(Genetic Algorithm,GA)最早是由美国的 John holland于20世纪70年代提出,该算法是根据大自然中生物体进化规律而设计提出的。是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。该算法通过数学的方式,利用计算机仿真运算,将问题的求解过程转换成类似生物进化中的染色体基因的交叉、变异等过程。在求解较为复杂的组合优化问题时,相对一些常规的优化算法,通常能够较快地获得较好的优化结果。遗传算法已被人们广泛地应用于组合优化、机器学习、信号处理、自适应控制和人工生命等领域。

二、GA算法流程

  遗传算法的基本运算过程如下:

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

  遗传操作包括以下三个基本遗传算子(genetic operator):选择(selection);交叉(crossover);变异(mutation)。

三、Python实现

import random
import numpy as np


def log(i, a, b):
    print("epoch --> ",
          str(i + 1).rjust(5, " "), " max:",
          str(round(a, 4)).rjust(8, " "), "mean:",
          str(round(b, 4)).rjust(8, " "), "alpha:",
          str(round(a / b, 4)).rjust(8, " "))


class GeneSolve:
## 初始定义,后续引用GeneSolve类方法为(初始种群数,最大迭代数,交叉概率,变异概率,最大适应度/平均适应度(扰动率趋于平稳则越接近1越好))
    def __init__(self, pop_size, epoch, cross_prob, mutate_prob, alpha, print_batch=10):
        self.pop_size = pop_size
        self.epoch = epoch
        self.cross_prob = cross_prob
        self.mutate_prob = mutate_prob
        self.print_batch = print_batch
        self.alpha = alpha
        self.width = 11
        self.best = None

        # 产生初始种群
        self.genes = np.array(
            [''.join([random.choice(['0', '1']) for i in range(self.width)]) for j in range(self.pop_size)])

    def inter_cross(self):
        """对染色体进行交叉操作"""
        ready_index = list(range(self.pop_size))
        while len(ready_index) >= 2:
            d1 = random.choice(ready_index)
            ready_index.remove(d1)
            d2 = random.choice(ready_index)
            ready_index.remove(d2)
            if np.random.uniform(0, 1) <= self.cross_prob:
                loc = random.choice(range(1, self.width - 1))
                d1_a, d1_b = self.genes[d1][0:loc], self.genes[d1][loc:]
                d2_a, d2_b = self.genes[d2][0:loc], self.genes[d2][loc:]
                self.genes[d1] = d1_a + d2_b
                self.genes[d2] = d2_a + d1_b

    def mutate(self):
        """基因突变"""
        ready_index = list(range(self.pop_size))
        for i in ready_index:
            if np.random.uniform(0, 1) <= self.mutate_prob:
                loc = random.choice(range(0, self.width))
                t0 = list(self.genes[i])
                t0[loc] = str(1 - int(self.genes[i][loc]))
                self.genes[i] = ''.join(t0)

    def get_adjust(self):
        """计算适应度(只有在计算适应度的时候要反函数,其余过程全都是随机的二进制编码)"""
        x = self.get_decode()
        return x * np.sin(x) + 12

    def get_decode(self):
        """编码,从表现型到基因型的映射"""
        # 编码其中int(x,2)表示讲x换为二进制编码,12.55则为区间[0,12.55]的delta,2**11则为2^11 这里的11则是编码长度(也就是转换的精度)
        # 求解精度为10^-2,即将一个单位长度(这里是12.55划分成1255份)进行划分,由于2^10 = 1024 < 2^11 = 2048,于是编码长度选取为11
        return np.array([int(x,2) * 12.55 / (2 ** 12 - 1) for x in self.genes])

    def cycle_select(self):
        """通过轮盘赌来进行选择"""
        adjusts = self.get_adjust()
        if self.best is None or np.max(adjusts) > self.best[1]:
            self.best = self.genes[np.argmax(adjusts)], np.max(adjusts)
        p = adjusts / np.sum(adjusts)
        cu_p = []
        for i in range(self.pop_size):
            cu_p.append(np.sum(p[0:i]))
        cu_p = np.array(cu_p)
        r0 = np.random.uniform(0, 1, self.pop_size)
        sel = [max(list(np.where(r > cu_p)[0]) + [0]) for r in r0]
        # 保留最优的个体
        if np.max(adjusts[sel]) < self.best[1]:
            self.genes[sel[np.argmin(adjusts[sel])]] = self.best[0]
        self.genes = self.genes[sel]

    def evolve(self):
        """逐代演化"""
        for i in range(self.epoch):
            self.cycle_select()  #种群选取
            self.inter_cross()   #染色体交叉
            self.mutate()        #计算适应度
            a, b = np.max(gs.get_adjust()), np.mean(gs.get_adjust())
            if i % self.print_batch == self.print_batch - 1 or i == 0:
                log(i, a, b)
            if a / b < self.alpha:
                log(i, a, b)
                print("进化终止,算法已收敛!共进化 ", i + 1, " 代!")
                break

  代码流程图:
遗传算法Python实现(Genetic Algorithm,GA)_第1张图片

四、优缺点及应用

1、不足之处

  1. 编码不规范及编码存在表示的不准确性。
  2. 单一的遗传算法编码不能全面地将优化问题的约束表示出来。考虑约束的一个方法就是对不可行解采用阈值,这样,计算的时间必然增加。
  3. 遗传算法通常的效率比其他传统的优化方法低。
  4. 遗传算法容易过早收敛。
  5. 遗传算法对算法的精度、可行度、计算复杂性等方面,还没有有效的定量分析方法。

2、特点

  1. 算法从问题解的串集开始搜索,而不是从单个解开始。这是遗传算法与传统优化算法的极大区别。传统优化算法是从单个初始值迭代求最优解的;容易误入局部最优解。遗传算法从串集开始搜索,覆盖面大,利于全局择优。
  2. 遗传算法同时处理群体中的多个个体,即对搜索空间中的多个解进行评估,减少了陷入局部最优解的风险,同时算法本身易于实现并行化。
  3. 遗传算法基本上不用搜索空间的知识或其它辅助信息,而仅用适应度函数值来评估个体,在此基础上进行遗传操作。适应度函数不仅不受连续可微的约束,而且其定义域可以任意设定。这一特点使得遗传算法的应用范围大大扩展。
  4. 遗传算法不是采用确定性规则,而是采用概率的变迁规则来指导他的搜索方向。
  5. 具有自组织、自适应和自学习性。遗传算法利用进化过程获得的信息自行组织搜索时,适应度大的个体具有较高的生存概率,并获得更适应环境的基因结构。
  6. 此外,算法本身也可以采用动态自适应技术,在进化过程中自动调整算法控制参数和编码精度,比如使用模糊自适应法。

3、应用

1. 函数优化
  函数优化是遗传算法的经典应用领域,也是遗传算法进行性能评价的常用算例,许多人构造出了各种各样复杂形式的测试函数:连续函数和离散函数、凸函数和凹函数、低维函数和高维函数、单峰函数和多峰函数等。对于一些非线性、多模型、多目标的函数优化问题,用其它优化方法较难求解,而遗传算法可以方便的得到较好的结果。

2. 组合优化
  随着问题规模的增大,组合优化问题的搜索空间也急剧增大,有时在计算上用枚举法很难求出最优解。对这类复杂的问题,人们已经意识到应把主要精力放在寻求满意解上,而遗传算法是寻求这种满意解的最佳工具之一。实践证明,遗传算法对于组合优化中的NP问题非常有效。例如遗传算法已经在求解旅行商问题、 背包问题、装箱问题、图形划分问题等方面得到成功的应用
此外,GA也在生产调度问题、自动控制、机器人学、图象处理、人工生命、遗传编码和机器学习等方面获得了广泛的运用。

3. 车间调度
  车间调度问题是一个典型的NP-Hard问题,遗传算法作为一种经典的智能算法广泛用于车间调度中,很多学者都致力于用遗传算法解决车间调度问题,现今也取得了十分丰硕的成果。从最初的传统车间调度(JSP)问题到柔性作业车间调度问题(FJSP),遗传算法都有优异的表现,在很多算例中都得到了最优或近优解。

你可能感兴趣的:(Python,机器学习,数学模型,python,算法)