数学建模——遗传算法(Genetic Algorithm, GA)

参考博客:https://zhuanlan.zhihu.com/p/43546261

遗传算法(Genetic Algorithm, GA)起源于对生物系统所进行的计算机模拟研究。它是模仿自然界生物进化机制发展起来的随机全局搜索和优化方法,借鉴了达尔文的进化论和孟德尔的遗传学说。

其本质是一种高效、并行、全局搜索的方法,能在搜索过程中自动获取和积累有关搜索空间的知识,并自适应地控制搜索过程以求得最佳解。

相关术语:

基因(genotype)性状染色体的内部表现
表现型(phenotype)染色体决定的性状的外部表现,或者说,根据基因型形成的个体的外部表现
进化(evolution)种群逐渐适应生存环境,品质不断得到改良。生物的进化是以种群的形式进行的
适应度(fitness)度量某个物种对于生存环境的适应程度
选择(selection)以一定的概率从种群中选择若干个个体。一般,选择过程是一种基于适应度的优胜劣汰的过程
复制(reproduction)细胞分裂时,遗传物质DNA通过复制而转移到新产生的细胞中,新细胞就继承了旧细胞的基因
交叉(crossover)两个染色体的某一相同位置处DNA被切断,前后两串分别交叉组合形成两个新的染色体。也称基因重组或杂交
变异(mutation)复制时可能(很小的概率)产生某些复制差错,变异产生新的染色体,表现出新的性状
编码(coding)DNA中遗传信息在一个长链上按一定的模式排列。遗传编码可看作从表现型到基因型的映射
解码(decoding)基因型到表现型的映射
个体(individual)指染色体带有特征的实体
种群(population)个体的集合,该集合内个体数称为种群的大小

遗传算法是从代表问题可能潜在的解集的一个种群(population)开始的,而一个种群则由经过基因(gene)编码的一定数目的个体(individual)组成。因此,第一步需要实现从表现型到基因型的映射即编码工作。初代种群产生之后,按照适者生存和优胜劣汰的原理,逐代(generation)演化产生出越来越好的近似解,在每一代,根据问题域中个体的适应度 (fitness)大小选择个体,借助于自然遗传学的遗传算子(genetic operators)进行组合交叉和变异,产生出代表新的解集的种群。这个过程将导致种群像自然进化一样,后生代种群比前代更加适应于环境,末代种群中的最优个体经过解码(decoding),可以作为问题近似最优解。

遗传算法的概念

模拟物竞天择的生物进化过程,通过维护一个潜在解的群体执行了多方向的搜索,并支持这些方向上的信息构成和交换。是以面为单位的搜索,比以点为单位的搜索,更能发现全局最优解

比喻说明:在遗传算法中,有很多袋鼠,它们降落到喜玛拉雅山脉的任意地方。这些袋鼠并不知道它们的任务是寻找珠穆朗玛峰。但每过几年,就在一些海拔高度较低的地方射杀一些袋鼠,并希望存活下来的袋鼠是多产的,在它们所处的地方生儿育女。就这样经过许多年,这些袋鼠们竟然都不自觉地聚拢到了一个个的山峰上,可是在所有的袋鼠中,只有聚拢到珠穆朗玛峰的袋鼠被带回了美丽的澳洲。

遗传算法的理解

遗传算法的实现过程实际上就像自然界的进化过程那样。首先寻找一种对问题潜在解进行“数字化”编码的方案。(建立表现型和基因型的映射关系)然后用随机数初始化一个种群(那么第一批袋鼠就被随意地分散在山脉上),种群里面的个体就是这些数字化的编码。接下来,通过适当的解码过程之后(得到袋鼠的位置坐标),用适应性函数对每一个基因个体作一次适应度评估(袋鼠爬得越高,越是受我们的喜爱,所以适应度相应越高)。用选择函数按照某种规定择优选择(我们要每隔一段时间,在山上射杀一些所在海拔较低的袋鼠,以保证袋鼠总体数目持平)。让个体基因变异(让袋鼠随机地跳一跳)。然后产生子代(希望存活下来的袋鼠是多产的,并在那里生儿育女)。遗传算法并不保证你能获得问题的最优解,但是使用遗传算法的最大优点在于你不必去了解和操心如何去“找”最优解。(你不必去指导袋鼠向那边跳,跳多远)而只要简单的“否定”一些表现不好的个体就行了。(把那些总是爱走下坡路的袋鼠射杀,这就是遗传算法的精粹)

遗传算法的基本特征:

智能式搜索:依据适应度(目标函数)进行只能搜索 
渐进式优化:利用复制、交换、突变等操作,使下一代结果优于上一代 
全局最优解:采用交换和突变操作产生新个体,使得搜索得到的优化结果逼近全局最优解
黑箱式结构:根据问题的特性进行编码(输入)和确定适应度(输出),具有只考虑输入与输出关系的黑箱式就够,并不深究输入与输出关系的原因 
通用性强:不要求明确的数学表达式,只需要一些简单的原则要求,可应用于解决离散问题、函数关系不明确的复杂问题 
并行式运算:每次迭代计算都是对群体中所有个体同时进行运算,是并行式运算方式,搜索速度快

遗传算法基本过程

初始化:设置最大迭代进化次数T,随机生成M个个体作为初始种群P(0);
个体评价:计算当前种群P(t)中的个体适应度;
选择:选择的目的是为了从当前群体中选出优良的个体,使它们有机会作为父代为下一代繁衍子孙。根据各个个体的适应度值,按照一定的规则或方法从上一代群体中选择出一些优良的个体遗传到下一代种群中。选择的依据是适应性强的个体为下一代贡献一个或多个后代的概率大;
交叉:遗传算法中的核心部分,通过交叉操作可以得到新一代个体,新个体组合了父辈个体的特性。将群体中的各个个体随机搭配成对,对每一个个体,以交叉概率交换它们之间的部分染色体;
变异:在个体基因的基础上进行变动,模拟自然界的基因突变,其变异结果的好坏不定,对种群中的每一个个体,以变异概率改变某一个或多个基因座上的基因值为其他的等位基因。同生物界中一样, 变异发生的概率很低,变异为新个体的产生提供了机会;
终止条件:若迭代次数达到预先设定的T,将迭代过程中具有最优适应度的个体作为问题的解输出。

遗传算法的三个基本核心操作:选择(Selection)、交叉(Crossover)和变异(Mutation)。

遗传算法具体步骤分析

背包问题:

itemweightsurvival points
sleeping bag1515
rope37
pocket knife210
torch55
bottle98
glucose2017

3.1 初始化(编码)

遗传算法的核心是个体之间的组合交叉变异部分。个体之间的组合交叉变异方式众多,不同的交叉变异方式将对算法的效率产生巨大的影响。

这里我们用遗传算法来解决这个背包问题。第一步是定义我们的总体。总体中包含了个体,每个个体都有一套自己的染色体。

我们知道,染色体可表达为二进制数串,在这个问题中,1 代表接下来位置的基因存在,0 意味着丢失。(译者注:作者这里借用染色体、基因来解决前面的背包问题,所以特定位置上的基因代表了上方背包问题表格中的物品,比如第一个位置上是 Sleeping Bag,那么此时反映在染色体的『基因』位置就是该染色体的第一个『基因』。)

在这里插入图片描述
如图,红色方框代表基因(Gene)
绿色方框代表染色体(Chromosome)
紫色方框代表总体人口;

现在,我们将图中的 4 条染色体看作我们的总体初始值

3.2 编码补充

二进制编码

二进制编码由二进制符号0和1所组成的二值符号集。它有以下一些优点: 1. 编码、解码操作简单易行 2. 交叉、变异等遗传操作便于实现 3. 合最小字符集编码原则 4. 利用模式定理对算法进行理论分析。

二进制编码的缺点是:对于一些连续函数的优化问题,由于其随机性使得其局部搜索能力较差,如对于一些高精度的问题,当解迫近于最优解后,由于其变异后表现型变化很大,不连续,所以会远离最优解,达不到稳定。

格雷码

格雷码编码是其连续的两个整数所对应的编码之间只有一个码位是不同的,其余码位完全相同。

二进制码转为格雷码:异或运算:同则为0,异则为1。公式如下:

在这里插入图片描述

由格雷码转二进制的转换公式为:

在这里插入图片描述
优点:增强遗传算法的局部搜索能力,便于对连续函数进行局部空间搜索。使用非常广泛。

浮点编码法

二进制编码虽然简单直观,但明显地。但是存在着连续函数离散化时的映射误差。个体长度较短时,可能达不到精度要求,而个体编码长度较长时,虽然能提高精度,但增加了解码的难度,使遗传算法的搜索空间急剧扩大。

所谓浮点法,是指个体的每个基因值用某一范围内的一个浮点数来表示。编码长度等于决策变量的个数。 在浮点数编码方法中,必须保证基因值在给定的区间限制范围内,遗传算法中所使用的交叉、变异等遗传算子也必须保证其运算结果所产生的新个体的基因值也在这个区间限制范围内。

优点:

适用于在遗传算法中表示范围较大的数 
适用于精度要求较高的遗传算法 
便于较大空间的遗传搜索 
改善了遗传算法的计算复杂性,提高了运算交率 
便于遗传算法与经典优化方法的混合使用 
便于设计针对问题的专门知识的知识型遗传算子 
便于处理复杂的决策变量约束条件
符号编码法

符号编码法是指个体染色体编码串中的基因值取自一个无数值含义、而只有代码含义的符号集如{A,B,C…}。

优点:

符合有意义积术块编码原则 
便于在遗传算法中利用所求解问题的专门知识 
便于遗传算法与相关近似算法之间的混合使用。

3.3 适应度函数

适应度函数一般使用目标函数;表明个体或解的优劣性。不同的问题,适应性函数的定义方式也不同。

计算前两条染色体的适应度分数,对于本问题,适应度分数越高意味着适应性更强, 对 A1 染色体 [100110]、 A2 染色体 [001110]计算结果如下:

itemweightsurvival points
sleeping bag1515
torch55
bottle98
total2923
itemweightsurvival points
pocket knife210
torch55
bottle98
total1623

对于这个问题,我们认为,当染色体包含更多生存分数时,也就意味着它的适应性更强。因此,由图可知,染色体 1 适应性强于染色体 2。

3.4 选择

接下来我们就是要选择要进行“交配”的染色体了,我们的目的是为了产生优良的下一代,因此选择“交配”的染色体自然也是要优良的。因此我们应该选择生存点数较高的C4染色体和C1染色体,对吧。这样想没错,但是这样的话在几代后染色体之间的相互差异就减少,失去多样性了。因此,我们一般会往里面假如一些随机性。让选择也具有一些随机性。那怎么加入呢?一般是采用「轮盘赌选择法」(Roulette Wheel Selection method)。

解释:
  假设有一个轮盘,我们把它分割成n份(n表示总体中染色体的个数),每条染色体在轮盘上的占用面积跟它的适应值成正比。以前面的例子来看,应如下图:

在这里插入图片描述

现在,将这个轮盘进行旋转,当停下来的时候,指针指中的那片区域所代表的染色体即为第一个亲本,重复操作得到第二个亲本。选中这两个亲本进行“交配”产生下一代。

选择

适应度比例方法,例如轮盘赌选择算法
随机遍历抽样
局部选择

   其中轮盘赌选择算法是最简单也是最常用的选择算法之一。该方法中各个个体被选择的概率和其适应度成正比。个体的适应度越高,其被选择到的概率越大。

3.5 交叉

实值重组:离散重组、中间重组、线性重组、扩展线性重组
二进制交叉:单点交叉、多点交叉、均匀交叉、洗牌交叉、缩小代理交叉

   其中单点交叉是最常用的交叉算法。在操作时随机选择一个交叉点,将两个个体在该点之后的基因结构进行互换,从而形成两个新的个体。例如: 
个体A:1 0 0 1 1 1 1 → 1 0 0 1 0 0 0 新个体 
个体B:0 0 1 1 0 0 0 → 0 0 1 1 1 1 1 新个体

根据交叉点的个数可分为单点交叉和多点交叉。

单点交叉:交叉最基本的形式,随机选择一个交叉点,将交叉点前后的染色体部分进行染色体间的交叉对调从而产生新后代,如下图:

在这里插入图片描述

多点交叉:选择两个交叉点,将交叉点之间的染色体部分进行对调从而产生后代,如下图:

在这里插入图片描述

3.6 变异

实值变异
二进制变异

   遗传算法引入变异的目的有两个:一是使遗传算法具有局部的随机搜索能力。当遗传算法通过交叉算子已接近最优解邻域时,利用变异算子的这种局部随机搜索能力可以加速向最优解收敛。显然,此种情况下的变异概率应取较小值,否则接近最优解的积木块会因变异而遭到破坏。二是使遗传算法可维持群体多样性,以防止出现未成熟收敛现象。此时收敛概率应取较大值。

遗传算法与传统优化算法的区别

遗传算法的初始解设置为一个群体,搜索覆盖面广,不易陷入局部最优;
变异部分使得算法有一定概率跳出局部最优;
遗传算法可同时对多个可行解进行实用性评估,算法容易并行;
具有自组织、自适应、自学习性。算法利用自身组织架构进行搜索,适应度大的个体具有较高存活率,指导了最优解的搜索方向。

在生物学中,在后代的生长过程中,它们体内的基因会发生一些变化,使得它们与父母不同,这个过程我们称为变异。变异是导致子一代的性状与亲一代不一样的原因。也就是说,正是因为变异,才导致种群存在多样性。

一般的,我们定义变异为染色体上发生的随机变化。如下图:

在这里插入图片描述

变异完成后,我们就得到了新的个体,完成一次进化。整个过程如下图(图来源:http://www.jade-cheng.com/au/coalhmm/optimization/):

在这里插入图片描述

这样就算完成了一次进化,接着我们由要用适应度函数去对这些新的后代进行判定,如果后代的适应度值够大,则将其替代掉总体中那些适应度值较低的染色体(保证种群的个数不变)。接着不断重复选择-交叉-变异这几个步骤,直至达到进化次数。

算法的应用

旅行商问题(Traveling Salesman Problem,TSP):TSP是最基本的路线问题,该问题是在寻求单一旅行者由起点出发,通过所有给定的需求点之后,最后再回到原点的最小路径成本。
指派问题(Assignment problem):在满足特定指派要求条件下,使指派方案总体效果最佳。如:有若干项工作需要分配给若干人(或部门)来完成。
单车间调度问题(Job-shop scheduling problem, JSP)是最基本最著名的调度问题,也是NP难问题,无最优解精确算法。一般类型的JSP问题可表达为:n个工件在m台机器上加工,每个工件有特定的加工工艺,每个工件加工的顺序及每道工序所花时间给定,安排工件在每台机器上工件的加工顺序,使得某种指标最优。
运输问题:为了把某种产品从若干个产地调运到若干个销地,已知每个产地的供应量和每个销地的需求量,如何在许多可行的调运方案中,确定一个总运输费或总运输量最少的方案。
背包问题(Transportation problem):给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。
设施选址问题(Facility Location Problem):指在给定的若干位置中选择一些来建立设施使得所有顾客的需求得到满足,且总体费用最小。
图划分问题(Graph Partitioning Problem):将图中的节点划分为数量大致相等的若干个分区,使得两个顶点分别在不同分区的边的数量最小化。
图着色问题:对于n个顶点的无环图G,要求对其各个顶点进行着色,使得任意相邻的顶点都有不同的颜色,且所用颜色种类最少。

算法的改进

交叉操作中随机产生交叉点,在此,使用Logistics混沌序列确定交叉点位置。即取一个(0,1)区间上的随机数作为初值,然后利用x(n)=4*x(n-1)(1-x(n-1))迭代产生一个(0,1)区间的混沌值,保存以上混沌值作为产生下一代交叉项的混沌迭代初值,再把这个值分别乘以100再加上2,最后取整作为交叉点,得到整个混沌序列,互换交叉点的基因,形成新的子代,仅仅更换部分点基因,没有连成片段,改动较小,这样可以避免遗传算法在组合优化应用中产生的寻优抖振问题。

MATLAB实现多旅行商问题

待补

遗传算法的MATLAB实现

MATLAB内嵌遗传算法工具箱: gadst

clc
clear all
close all
lbx=-1;ubx=1; %函数自变量x范围[-1,1]
lby=-1;uby=1; %函数自变量y范围[-1,1]
% 定义遗传算法参数
NIND=50; %个体数目
MAXGEN=50; %最大遗传代数
PRECI=20; %变量的二进制位数
GGAP=0.90; %代沟
px=0.97; %交叉概率
pm=0.001; %变异概率
trace=zeros(6,MAXGEN); %寻优结果的初始值
FieldD=[PRECI PRECI PRECI PRECI PRECI;lbx lby lbx lbx lbx;ubx uby ubx ubx ubx;1 1 1 1 1;0 0 0 0 0;1 1 1 1 1;1 1 1 1 1];  %区域描述器
Chrom=crtbp(NIND,PRECI*5);  %初始种群
% 优化
gen=0;  %代计数器
XY=bs2rv(Chrom,FieldD);  %计算初始种群的十进制转换
d1=XY(:,1);
d2=XY(:,2);d3=XY(:,3);d4=XY(:,4);d5=XY(:,5);
ObjV=func(d1,d2,d3,d4,d5,NIND);  %计算目标函数值
while gen<MAXGEN
   FitnV=ranking(-ObjV);   %分配适应度值
   SelCh=select('sus',Chrom,FitnV,GGAP);  %选择
   SelCh=recombin('xovsp',SelCh,px);  %重组
   SelCh=mut(SelCh,pm);  %变异
   XY=bs2rv(SelCh,FieldD);  %子代个体的十进制转换
   d1=XY(:,1);d2=XY(:,2);d3=XY(:,3);d4=XY(:,4);d5=XY(:,5);
   d1n=numel(d1);
   ObjVSel=func(d1,d2,d3,d4,d5,d1n);  %计算子代的目标函数值
   [Chrom,ObjV]=reins(Chrom,SelCh,1,1,ObjV,ObjVSel); %重插入子代到父代,得到新种群
   XY=bs2rv(Chrom,FieldD);
   gen=gen+1   %代计数器增加
   %获取每代的最优解及其序号,Y为最优解,I为个体的序号
   [Y,I]=max(ObjV);
   trace(1:5,gen)=XY(I,:);   %记下每代的最优值
   trace(6,gen)=Y;   %记下每代的最优值
end
figure(2);
plot(1:MAXGEN,trace(6,:));
grid on
xlabel('遗传代数')
ylabel('解的变化')
title('进化过程')
bestc1=trace(1,end)
bestc2=trace(2,end)
bestc3=trace(3,end)
bestc4=trace(4,end)
bestc5=trace(5,end)
bestSIR=trace(6,end)

【例】BP神经网络初始权值和阈值优化

%% I. 清除环境变量
clear all
clc
 
%% II. 声明全局变量
global p     % 训练集输入数据
global t     % 训练集输出数据
global R     % 输入神经元个数
global S2    % 输出神经元个数
global S1    % 隐层神经元个数
global S     % 编码长度
S1 = 10;
 
%% III. 导入数据
%%
% 1. 训练数据
p = [0.01 0.01 0.00 0.90 0.05 0.00;
     0.00 0.00 0.00 0.40 0.50 0.00;
     0.80 0.00 0.10 0.00 0.00 0.00;
     0.00 0.20 0.10 0.00 0.00 0.10]';
t = [1.00 0.00 0.00 0.00;
     0.00 1.00 0.00 0.00;
     0.00 0.00 1.00 0.00;
     0.00 0.00 0.00 1.00]';
%%
% 2. 测试数据
P_test = [0.05 0    0.9  0.12 0.02 0.02;
          0    0    0.9  0.05 0.05 0.05;
          0.01 0.02 0.45 0.22 0.04 0.06;
          0    0    0.4  0.5  0.1  0;
          0    0.1  0    0    0    0]';
%% IV. BP神经网络
%%
% 1. 网络创建
net = newff(minmax(p),[S1,4],{'tansig','purelin'},'trainlm'); 
 
%%
% 2. 设置训练参数
net.trainParam.show = 10;
net.trainParam.epochs = 2000;
net.trainParam.goal = 1.0e-3;
net.trainParam.lr = 0.1;
 
%%
% 3. 网络训练
[net,tr] = train(net,p,t);
 
%%
% 4. 仿真测试
s_bp = sim(net,P_test)    % BP神经网络的仿真结果
 
%% V. GA-BP神经网络
R = size(p,1);
S2 = size(t,1);
S = R*S1 + S1*S2 + S1 + S2;
aa = ones(S,1)*[-1,1];
 
%% VI. 遗传算法优化
%%
% 1. 初始化种群
popu = 50;  % 种群规模
initPpp = initializega(popu,aa,'gabpEval',[],[1e-6 1]);  % 初始化种群
 
%%
% 2. 迭代优化
gen = 100;  % 遗传代数
% 调用GAOT工具箱,其中目标函数定义为gabpEval
[x,endPop,bPop,trace] = ga(aa,'gabpEval',[],initPpp,[1e-6 1 1],'maxGenTerm',gen,...
                           'normGeomSelect',[0.09],['arithXover'],[2],'nonUnifMutation',[2 gen 3]);
%%
% 3. 绘均方误差变化曲线
figure(1)
plot(trace(:,1),1./trace(:,3),'r-');
hold on
plot(trace(:,1),1./trace(:,2),'b-');
xlabel('Generation');
ylabel('Sum-Squared Error');
 
%%
% 4. 绘制适应度函数变化
figure(2)
plot(trace(:,1),trace(:,3),'r-');
hold on
plot(trace(:,1),trace(:,2),'b-');
xlabel('Generation');
ylabel('Fittness');
 
%% VII. 解码最优解并赋值
%%
% 1. 解码最优解
[W1,B1,W2,B2,val] = gadecod(x);
 
%%
% 2. 赋值给神经网络
net.IW{1,1} = W1;
net.LW{2,1} = W2;
net.b{1} = B1;
net.b{2} = B2;
 
%% VIII. 利用新的权值和阈值进行训练
net = train(net,p,t);
 
%% IX. 仿真测试
s_ga = sim(net,P_test)    %遗传优化后的仿真结果
 
  • 12
    点赞
  • 113
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
好的,我了解您的问题了。以下是一个简单的Python实现遗传算法的示例代码: ```python import random # 定义目标函数 def fitness_function(x): return x**2 # 定义种群数量 POPULATION_SIZE = 100 # 定义染色体长度 CHROMOSOME_LENGTH = 20 # 定义交叉概率 CROSSOVER_RATE = 0.8 # 定义变异概率 MUTATION_RATE = 0.1 # 定义种群 population = [] # 初始化种群 for i in range(POPULATION_SIZE): chromosome = [] for j in range(CHROMOSOME_LENGTH): chromosome.append(random.randint(0, 1)) population.append(chromosome) # 迭代次数 GENERATIONS = 500 # 开始迭代 for generation in range(GENERATIONS): # 计算适应度 fitness_values = [] for chromosome in population: x = int(''.join(map(str, chromosome)), 2) fitness_values.append(fitness_function(x)) # 找到最优解 best_fitness = max(fitness_values) best_chromosome = population[fitness_values.index(best_fitness)] # 输出结果 print("Generation:", generation, "Best fitness:", best_fitness, "Best chromosome:", best_chromosome) # 选择 parents = [] for i in range(POPULATION_SIZE): parent1 = population[random.randint(0, POPULATION_SIZE-1)] parent2 = population[random.randint(0, POPULATION_SIZE-1)] if fitness_function(int(''.join(map(str, parent1)), 2)) > fitness_function(int(''.join(map(str, parent2)), 2)): parents.append(parent1) else: parents.append(parent2) # 交叉 offspring = [] for i in range(0, POPULATION_SIZE, 2): parent1 = parents[i] parent2 = parents[i+1] if random.random() < CROSSOVER_RATE: crossover_point = random.randint(1, CHROMOSOME_LENGTH-1) offspring1 = parent1[:crossover_point] + parent2[crossover_point:] offspring2 = parent2[:crossover_point] + parent1[crossover_point:] offspring.append(offspring1) offspring.append(offspring2) else: offspring.append(parent1) offspring.append(parent2) # 变异 for i in range(POPULATION_SIZE): chromosome = offspring[i] for j in range(CHROMOSOME_LENGTH): if random.random() < MUTATION_RATE: chromosome[j] = 1 - chromosome[j] # 更新种群 population = offspring ``` 这个示例实现了一个简单的遗传算法来求解目标函数 $f(x)=x^2$ 的最大值,其中 $x$ 是一个20位的二进制数。在每个迭代中,选择、交叉和变异操作被执行以生成新的种群。最终找到最优解并输出结果。 请注意,这只是一个简单的示例,实际应用中需要根据具体问题进行适当的修改和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何为xl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值