数学建模之遗传算法

文章目录

  • 前言
  • 遗传算法
    • 算法思想
    • 生物的表示
    • 初始种群的生成
    • 下一代种群的产生
      • 适应度函数
      • 轮盘赌
      • 交配
      • 变异
      • 混合产生新种群
    • 停止迭代的条件
    • 遗传算法在01背包中的应用
      • 01背包问题介绍
      • 01背包的其它解法
      • 01背包的遗传算法解法
        • 生物的表示
        • 初始种群的生成
        • 下一代种群的产生
          • 适应度函数
          • 轮盘赌
          • 交配
          • 变异
          • 混合产生新种群
        • 停止迭代的条件
      • 一个优化
      • 代码
    • 遗传算法的优缺点
      • 优点
        • 可以全局搜索
        • 适用范围广
      • 缺点
        • 参数调节困难
        • 可能陷入局部最优
    • 遗传算法的时间复杂度
  • 总结


前言

遗传算法是美国教授Holland于1975年提出的一种基于模仿生物遗传学的优化算法。这种算法很难得到问题的精确答案,但是能够在允许的时间复杂度内得到一个较优的答案。常用来解决一些目前不存在多项式算法的问题,如旅行商问题(TSP问题),背包问题。

遗传算法

算法思想

我们假设在自然界中,存在一个种群。根据达尔文的生物进化论,生物会进行交配和变异,从而慢慢进化。而我们的目标,就是让这群生物往我们希望的方向进化,进化得越来越优秀。而通过数代进化之后,最优秀的那个个体,就是我们问题的解。

生物的表示

在实际问题中,我们用一个n位的01二进制串来表示一个生物。每一位取0或1,表示一个信息。比如,在01背包问题中,一共有n个物品。可以用第i位取1表示取第i个物品,取0表示不取第i个物品。这样,一个方案就可以用一个生物来表示。一个种群包含很多个生物,于是,一个种群可以理解为很多个方案。而我们的目标,就是要选出最优的一个方案。

初始种群的生成

初始种群的生成,可以用随机数来生成。每一位随机取0或1,就可以生成初始种群。当然,这样生成的初始生物可能不太好,我们可以通过贪心思想等生成一些较为优良的初始生物,以提升算法准确度。

下一代种群的产生

生成完初始种群之后,我们需要生成下一代种群。为了比较生物的优劣,我们需要定义适应度函数。

适应度函数

适应度函数是根据实际问题,自己定义的一个函数,较优的方案具有较高的适应度,较差的方案拥有较低的适应度。本文用 f ( x i ) f(x_i) f(xi)表示第i个生物的适应度。

轮盘赌

为了产生下一代,我们需要选择两个生物作为父母,使其进行交配。那么,如何选择生物呢?常用的一个方法是轮盘赌。即第i个生物被选中的概率为 f ( x i ) ∑ i = 1 n f ( x i ) \frac{f(x_i)}{\sum_{i=1}^{n}{f(x_i)}} i=1nf(xi)f(xi)。为什么这个方法被称为轮盘赌呢,是因为其指导思想是将每个生物按照其适应度在轮盘上分配位置,适应度越大,分配到的位置越大。然后转动轮盘,指针指到哪里,就抽到哪个生物。这样做的有点在于适应度越高的生物被选中的概率越大,越有可能保留优良基因。让优秀的基因传承下去,才能使得种群总体适应度越来越高。

交配

我们通过轮盘赌选择了两个生物,现在要让它们生成一个新的生物。也就是说,我们要用两个长度为n的01串 x i x_i xi x j x_j xj,生成一个新的长度为n的01串。我们的做法是产生一个1~n-1的随机数,记为y。用 x i x_i xi的1~y位和 x j x_j xj的y+1~n位组成一个新的01串。这个新的01串就是 x i x_i xi x j x_j xj的孩子。

变异

由于初始种群的随机度较高,可能无法通过交配产生一些生物。为了丰富生物多样性,我们需要以适当的概率对生物进行变异。通常采用的变异方法是以固定的变异率对生物的某一位进行取反操作。一般变异率保持在0.2左右。

混合产生新种群

通过交配和变异,我们产生了一个新个体。下一代的新种群由这一代的部分个体和下一代的新个体按某种比例混合产生。一般是由20%的这一代生物和80%的新产生 的生物组成。

停止迭代的条件

随着一代一代的新种群产生,生物的总体适应度也将越来越高。我们需要一个停止迭代的条件,一般有两种。第一种是按照固定的迭代次数进行,比如10000次。第二种是比较相邻两代中的平均适应度,如果平均适应度的增加小于一个我们规定的值,那么就可以停止迭代了。我们选取最后一代中最优秀的生物作为我们的最终方案。

遗传算法在01背包中的应用

我们举个具体的例子,来看一看遗传算法在实际问题中的应用。

01背包问题介绍

假设背包的容量为m,有n个物品,第i物品的重量为 w [ i ] w[i] w[i],价值为 v [ i ] v[i] v[i],我们要从中选取一些物品放入背包,在不超过背包容量的前提下,使装入背包的物品总价值最高。

01背包的其它解法

01背包的标准解法是利用动态规划算法进行解决,时间复杂度为 O ( n m ) O(nm) O(nm),空间复杂度为 O ( m ) O(m) O(m)。但是,使用动态规划算法的前提是, m m m为整数,且 w [ i ] w[i] w[i]均为整数。对于更一般的01背包问题,目前没有找到多项式时间的解法,是一个NP难问题。而对于这样一类问题,遗传算法就非常适用。遗传算法虽然不能给出一个精确解,但是能在可接受的时间范围内给出一个较优的答案。

01背包的遗传算法解法

通过遗传算法,可以让算法的时间复杂度与背包容量 m m m无关。并且可以应对 m m m不是整数的情况。

生物的表示

我们用 c [ i ] c[i] c[i]表示一个01串,代表第i个生物。 c [ i ] [ j ] c[i][j] c[i][j]表示第i个生物的第j位是1还是0。对应到01背包问题,就表示第i种方案的第j个物品选或不选。

初始种群的生成

我们首先对于所有物品,按照性价比(即 v [ i ] / w [ i ] v[i]/w[i] v[i]/w[i])从高到低进行排序。然后我们随机生成一个长度为n的01串。由于随机生成0和1的方案不一定能满足背包容量的要求。所以我们基于贪心思想,把一个随机01串改造成符合背包容量的01串。在产生的随机方案的基础上,从性价比最高的物品开始,如果随机方案选了,且选择之后背包容量没有超,则选取该物品,否则不选该物品。但是通过这种方式,背包可能有剩余容量,于是我们再次从性价比最高的物品开始,如果我们的方案没有选择它,且选择它之后没有超出背包容量,那么我们就把它加入背包。通过这种基于贪心的选择方式,可以生成一系列较优的初始方案。这些方案组成了初始种群。

下一代种群的产生
适应度函数

每个生物的适应度应该与该方案的总价值相关,总价值越大,适应度越高。我们一般有几种不同的适应度函数。第一种直接用总价值来表示适应度,即 f ( x i ) = ∑ j = 1 n c [ i ] [ j ] ∗ v [ j ] f(x_i)=\sum_{j=1}^{n}{c[i][j]*v[j]} f(xi)=j=1nc[i][j]v[j]。第二种为了使后面的轮盘赌能更大程度区分优秀方案和劣质方案,用该方案的总价值减去这一代方案中最低的总价值来表示适应度,即 f ( x i ) = ∑ j = 1 n c [ i ] [ j ] ∗ v [ j ] − m i n { ∑ j = 1 n c [ i ] [ j ] ∗ v [ j ] } f(x_i)=\sum_{j=1}^{n}{c[i][j]*v[j]}-min\{\sum_{j=1}^{n}{c[i][j]*v[j]}\} f(xi)=j=1nc[i][j]v[j]min{j=1nc[i][j]v[j]}

轮盘赌

我们通过轮盘赌的方法选取两个方案,来产生下一个方案。在轮盘赌中,每个方案被选择的概率为 f ( x i ) ∑ i = 1 n f ( x i ) \frac{f(x_i)}{\sum_{i=1}^{n}{f(x_i)}} i=1nf(xi)f(xi)。通过生成随机数,来选择两个方案。

交配

通过轮盘赌,选择了两个方案,假设为第 a a a个和第 b b b个方案,假设新产生的方案为d,在1~n-1中产生的随机数为 z z z,那么
d [ j ] = { c [ a ] [ j ] 1 ≤ j ≤ z c [ b ] [ j ] z + 1 ≤ j ≤ n d[j]=\begin{cases} c[a][j] & 1\leq j \leq z \\ c[b][j] & z+1 \leq j \leq n \end{cases} d[j]={c[a][j]c[b][j]1jzz+1jn
如果该方案不满足背包容量的要求,就重新用轮盘赌选择两个方案,重新生成新的方案。

变异

我们设定变异率为一个常数,对新产生的方案进行变异。我们生成一个0~1的随机数,若这个随机数小于变异率,那么就进行变异。我们再生成一个1~n的随机数 z z z,表示对新方案的第 z z z位进行取反操作。即
d [ j ] = { d [ j ] j ≠ z 1 − d [ j ] j = z d[j]=\begin{cases} d[j] & j\not=z \\ 1-d[j] & j=z \end{cases} d[j]={d[j]1d[j]j=zj=z

混合产生新种群

我们通过轮盘赌,将20%的这一代的方案和80%的新产生的方案混合在一起,组成新一代的方案。值得说明的是,需要将每一代产生的最优方案记录下来。

停止迭代的条件

我们固定一个迭代次数,运行完之后,我们记录的最优秀的方案就是我们程序的运行结果。由于程序的随机性较高,所以一般重复多次运行程序,取最优结果作为我们的最终答案。对于迭代次数、变异率、种群大小等常数,需要根据实际情况灵活选取,在保证准确率的情况下,使得程序运行效率较高。

一个优化

由于算法随机性较高,有一些非常优秀的方案可能不一定能够保存下来。所以我们采用优先队列,人为把每一代中最优秀的两个方案保存下来,放到下一代中。对于这个问题,也有很多人在研究,可能也存在很多更合理的优化。这里只是选取了一个能够大大提升算法准确性的优化。

代码

下面是我用遗传算法写的01背包问题的C++代码

#include<cstdio>
#include<iostream>
#include<stdlib.h>
#include<time.h>
#include<queue>
#include<vector>
using namespace std;
int n,m,w[10005],v[10005];
vector<bool> c[105];
double d[105];
const int populationSize = 20;//设定种群大小为20 
const int generations = 10000;//设定迭代次数为10000 
double mutationRate = 0.3;//设定变异率为0.3 
vector<bool> q;
int num=0;
bool check(vector<bool> &a)//对于一个方案,验证其是否超过背包容量 
{int s=0;for (int i=0;i<n;i++) s+=a[i]*w[i];return s<=m;
}
double jisuan(vector<bool> &a)//计算给定方案的总价值 
{if (!check(a)) return 0;//若该方案超出背包容量,则价值为0 double s=0,t=0;for (int i=0;i<n;i++) {s+=a[i]*v[i];t+=a[i]*w[i];}return s;
}
vector<bool> jiaocha(vector<bool> &a,vector<bool> &b)//对于两个方案,进行交叉 
{int x=rand()%n;vector<bool> t;for (int i=0;i<x;i++) t.push_back(a[i]);for (int i=x;i<n;i++) t.push_back(b[i]);return t;
}
void bianyi(vector<bool> &a)//对于一个方案,进行变异 
{double x=rand()/double(RAND_MAX);double y;	if (x<mutationRate) {y=rand()%n;a[y]=!a[y];}
}
int lunpandu()//通过轮盘赌,选出一个方案的编号 
{double x=rand()/double(RAND_MAX);for (int i=1;i<=populationSize;i++){if (d[i]>=x) return i;}return populationSize;
}
struct Compare {  //根据方案价值的高低来在优先队列中排序 bool operator()(vector<bool>& a,vector<bool>& b) {  return jisuan(a) < jisuan(b);  }  
};
int main()
{srand(time(NULL));cin>>m>>n;//m表示背包容量,n表示物品数量 for (int i=0;i<n;i++){cin>>w[i]>>v[i];//w[i]表示物品重量,v[i]表示物品价值  }for (int i=0;i<n;i++)//对所有物品关于性价比从高到低进行排序 {for (int j=0;j<n-i;j++){if ((double)v[j]/w[j]<(double)v[j+1]/w[j+1]){int t=v[j];v[j]=v[j+1];v[j+1]=t;t=w[j];w[j]=w[j+1];w[j+1]=t;}}}int ans=0;for (int k=1;k<=10;k++) //程序重复运行10次 {num=0; srand(time(NULL));for (int i=1;i<=populationSize;i++)//生成初始种群 {c[i].clear();for (int j=0;j<n;j++) c[i].push_back(rand()%2);//生成随机01串 int weight=0;for (int j=0;j<n;j++) {if (weight+c[i][j]*w[j]<=m) weight+=c[i][j]*w[j];//若加入该物品后背包重量没有超,就加入该物品 else c[i][j]=0;}for (int j=0;j<n;j++)//背包还有剩余空间,可以再加入一些物品 {if (c[i][j]==0&&weight+w[j]<=m) {c[i][j]=1;weight+=w[j];}}}priority_queue<vector<bool>, vector<vector<bool> >, Compare> p;//定义优先队列 vector<bool> x;for (int i=1;i<=generations;i++){while (!p.empty()) p.pop();//清空优先队列 double s=0,zuixiaozhi=1000000;bool b=0;for (int j=1;j<=populationSize;j++) {if (j==1) //把上一代最优的方案加入优先队列 {p.push(c[j]);b=1;}if (b==1&&c[j]!=p.top()) //把上一代次优的方案加入优先队列 {p.push(c[j]); b=0;}d[j]=jisuan(c[j]);if (d[j]<zuixiaozhi) zuixiaozhi=d[j];s+=d[j];}s-=zuixiaozhi*populationSize;for (int j=1;j<=populationSize;j++) d[j]=d[j-1]+(d[j]-zuixiaozhi)/s;//d[j]表示第j个方案在轮盘赌中被选中的概率 for (int j=1;j<=populationSize;j++)//产生下一代 {int x1=lunpandu(),x2=lunpandu();//根据轮盘赌,选择两个方案 x=jiaocha(c[x1],c[x2]);//两个方案进行交叉 bianyi(x);//对新得到的方案进行变异 p.push(x);//把新得到的方案加入优先队列 }for (int j=1;j<=populationSize&&!p.empty();j++)//把较优的一些方案作为下一代,从而继续产生下下代 {c[j]=p.top();p.pop();}}for (int i=1;i<=populationSize;i++)//把最优的方案记录下来 {if (check(c[i])){cout<<k<<' '<<jisuan(c[i])<<"\n";//输出第k次的最优值 if (jisuan(c[i])>ans) ans=jisuan(c[i]);break;}}}cout<<ans;//输出10次的最优值 
}

遗传算法的优缺点

优点

可以全局搜索

由于遗传算法的多样性搜索性质,它可以在搜索空间内找到许多可能的解,于是可以在较短时间内全局最优或近似最优的解。

适用范围广

作为一种优化算法,它的适用范围非常广,可以基于初始解,产生近似最优解。而且可以人为定义适应度,可以应用于一些难以定量的问题。

缺点

参数调节困难

该算法中有多个参数,比如种群大小,迭代次数,变异率等。如何通过修改参数使得能取得更优秀的解,是一个比较困难的问题。

可能陷入局部最优

若父代的相似度较高,则产生的子代相似度也很可能较高,从而使得陷入局部最优。

遗传算法的时间复杂度

设迭代次数为G,种群规模为P,01串长度为n,则不加任何优化的遗传算法的时间复杂度为 O ( G P n ) O(GPn) O(GPn)。G和P都是人为控制的参数。通过这种方法,把原来的 O ( 2 n ) O(2^n) O(2n)的时间复杂度降为了关于n的线性表达式,这已经是一个极大的进步了。

总结

遗传算法主要应用于一些不存在多项式算法的问题,使得在较短时间内能够得到较优的答案。但要得到真正最优的解还是有一定困难的。遗传算法在数学建模方面也有着广泛的应用,比如用于函数的求最值等问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/87549.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构之【动态数组】

1. 线性表 概念&#xff1a;线性表是n个具有相同特性的数据元素的有限序列。 常见的线性表有&#xff1a;数组、链表、栈、队列、字符串…… 特点&#xff1a; 保存在这个结构中的元素都是相同的数据类型。元素之间线性排列&#xff0c;元素之间在逻辑上是连续的。 线性表…

分类预测 | MATLAB实现WOA-CNN-BiGRU-Attention数据分类预测(SE注意力机制)

分类预测 | MATLAB实现WOA-CNN-BiGRU-Attention数据分类预测&#xff08;SE注意力机制&#xff09; 目录 分类预测 | MATLAB实现WOA-CNN-BiGRU-Attention数据分类预测&#xff08;SE注意力机制&#xff09;分类效果基本描述模型描述程序设计参考资料 分类效果 基本描述 1.MATLA…

loadEnv是vite的工具函数

loadEnv()函数返回一个对象&#xff0c;这个对象就是根据开发模式还是生产环境加载的.env.development文件里的环境变量&#xff0c;有系统自带的也有自己手写的 loadEnv(第1个参数&#xff0c;第2个参数&#xff0c;第3个参数) 注意&#xff1a;第3个参数如果是“”空字符…

LL库实现正交编码器数据采集

1&#xff0c;首先打开STM32CubeMX&#xff0c;配置一下工程&#xff0c;这里使用的芯片是STM32F103C8T6。 我这里选择了定时器2和3&#xff0c;因为我有两个电机&#xff0c;在定时器模式这边&#xff0c;我们在Combined Channels这个选项里面我们选择Encoder Mode&#xff0c…

关于Julia语言 PyPlot,PyCall报错问题

前提&#xff0c;使用的是annaconda&#xff0c;jupyter 具体的报错为&#xff1a;LoadError: PyCall not properly installed. Please run Pkg.build("PyCall") 如果按照它的提示Pkg.build("PyCall")&#xff0c;也会报错&#xff0c;这可能是因为julia…

OpenCV实现图像的礼帽和黑帽

礼帽运算 黑帽运算 参数 cv.morphologyEx(img,op,kernel)参数&#xff1a; img : 要处理的图像op: 处理方式 代码实现 import numpy as np import cv2 as cv import matplotlib.pyplot as plt from pylab import mplmpl.rcParams[font.sans-serif] [SimHei]#读取图像img1 …

Hyper-V 虚拟机CentOS配置网络(三)

总目录 https://preparedata.blog.csdn.net/article/details/132877836 文章目录 总目录一、创建交换机二、共享网络给交换机三、虚拟机配置交换机四、配置CentOS网络 一、创建交换机 二、共享网络给交换机 打开物理宿主机的控制面板的网络链接中心&#xff0c;此时已经多了一…

【从0学习Solidity】52. EIP712 类型化数据签名

【从0学习Solidity】 52. EIP712 类型化数据签名 博主简介&#xff1a;不写代码没饭吃&#xff0c;一名全栈领域的创作者&#xff0c;专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构&#xff0c;分享一些项目实战经验以及前沿技术的见解。关注我们的主页&#…

iOS设置作为蓝牙外设

文章目录 一、 说明二、设置蓝牙外设1. 初始化外设中心2. 创建外设服务3. 获取外设蓝牙状态4. 发送蓝牙数据 三、外设代理1. 状态变更回调2. 中心设备读取数据的时候回调3. 中心设备写入数据的时候回调4. 订阅提示 四、发送广播数据1. 发送不同广播2. beacon广播3. 停止广播 一…

Yarn安装报错和使用

yarn 安装 Yarn 支持 Windows、MacOS 和 Linux 等多个平台。 我们也可以前往官网下载安装包&#xff1a;https://classic.yarnpkg.com/en/docs/install。 同时&#xff0c;也可以使用命令行进行安装&#xff0c;安装命令如下&#xff1a; npm install -g yarn安装报错 ~ % n…

配电室电力监控系统:实时掌握电力运行状况

配电室是电力系统的重要组成部分&#xff0c;负责将发电厂输送的电力分配到各个用电终端。随着电力需求的不断增长&#xff0c;配电室的规模和复杂性也在增加。为了确保电力系统的稳定运行&#xff0c;采用配电室电力监控系统变得至关重要。 一、配电室电力监控系统概述 力…

Python库学习(七):科学计算库Numpy[续篇一]:结构数组

1.介绍 结构数组是NumPy中的一种高级数据结构&#xff0c;它允许用户在单个数组中存储多种数据类型的元素。与普通的NumPy数组不同&#xff0c;结构数组的每个元素可以具有不同的数据类型&#xff0c;并且可以使用字段名来引用这些元素。这使得结构数组非常适合处理表格数据、…

TCP 和 UDP哪个更好

传输控制协议 &#xff08;TCP&#xff09; 和用户数据报协议 &#xff08;UDP&#xff09; 是互联网的基础支柱&#xff0c;支持从网络源到目的地的不同类型的数据传输。TCP更可靠&#xff0c;而UDP优先考虑速度和效率。本文解释了两种协议的工作原理&#xff0c;并详细讨论了…

【遥遥领先】Eolink IDEA 插件:零代码入侵,自动生成接口

省流版&#xff1a; Eolink 有 IDEA 插件吗&#xff1f; 有&#xff0c;而且遥遥领先&#xff01;我们在一年半之前就发布了&#xff0c;而且功能更丰富&#xff01; IDEA 插件市场搜索“Eolink Apikit”即可安装使用。 &#x1f680;使用指引&#xff1a;Eolink - IntelliJ ID…

virtualbox无界面打开linux虚拟机的bat脚本,以及idea(代替Xshell)连接linux虚拟机的方法

virtualbox无界面打开linux虚拟机的bat脚本&#xff0c;以及idea连接linux虚拟机的方法 命令行运行代码成功运行的效果图 idea连接linux虚拟机的方法【重要】查看虚拟机的IP地址idea中选择菜单&#xff08;该功能可代替Xshell软件&#xff09;配置设置连接成功进入idea中的命令…

20230925工作心得

1、如果使用map的时候&#xff0c;担心key重复&#xff0c;覆盖掉值 那么直接加个if/else判断就好了。 如果map.containsKey&#xff0c;那么就把值追加上去&#xff0c;否则就直接put。 2、list的removeAll方法 list.removeAll(list2);//list要removeAll谁,就是看list自己比…

【含2023java面试题】分布式锁方案设计:防止取消订单误支付Bug

AI绘画关于SD,MJ,GPT,SDXL百科全书 面试题分享点我直达 2023Python面试题 2023最新面试合集链接 2023大厂面试题PDF 面试题PDF版本 java、python面试题 项目实战:AI文本 OCR识别最佳实践 AI Gamma一键生成PPT工具直达链接 玩转cloud Studio 在线编码神器 玩转 GPU AI…

Java on Azure Tooling 8月更新|以应用程序为中心的视图支持及 Azure 应用服务部署状态改进

作者&#xff1a;Jialuo Gan - Program Manager, Developer Division at Microsoft 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎阅读 Java on Azure 工具的八月更新。在本次更新中&#xff0c;我们将推出新的以应用程序为中心的视图支持&#xff0c;帮助开发人员在一个项…

德大黄鱼开捕 年产量20万吨 京东超市多举措保障黄鱼品质

作为“中国大黄鱼之都”&#xff0c;宁德大黄鱼占全国大黄鱼总产值产量均在90%以上。有关方面预计&#xff0c;今年全年宁德大黄鱼产量达20万吨&#xff0c;年增产10%&#xff0c;直接经济产值超百亿元。 宁德大黄鱼开捕活动&#xff0c;由宁德市政府、宁德渔业协会、京东超市…

文举论金:黄金原油全面走势分析策略指导。

分析永远是辅助&#xff0c;策略才是盈利的基本。对于技术&#xff0c;没有对错&#xff0c;关键性的分水岭&#xff0c;易成为行情转折的拐点抑或助推趋势的延伸。但是&#xff0c;交易中或实盘操作中&#xff0c;咱们必须果断&#xff0c;在对的方向里坚持&#xff0c;而当方…