多目标优化经典算法——NSGA-II
参考博客链接
https://blog.csdn.net/qq_35414569/article/details/79639848?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0.no_search_link&spm=1001.2101.3001.4242
因为NSGA-II算法是一种遗传算法,所以首先搞清楚遗传算法的流程。
遗传算法流程
一般遗传算法的流程:
- 种群初始化
- 计算每个个体的适应度
- 选择
- 交叉
- 变异
根据是否满足解的精度要求和迭代次数来判断是否进行下一轮的遗传进化。
NSGA算法存在的3个问题
- O(MN^3)计算时间复杂度(其中M代表目标个数,N代表种群个数)
- 非精英机制方法
- 需要指定一个共享参数
NSGA-II算法
NSGA-II算法主要由以下三个部分组成
A、快速非支配排序方法
B、拥挤比较算子
C、主程序
A、快速非支配排序方法
- 传统排序方法:时间复杂度O(MN3),M是目标个数,N是种群个数。为了计算第一非支配前沿面,需要判断每个解和种群中的其他解的支配关系。一个解和其他解的支配关系需要O(MN)复杂度,每个解和其他解的支配关系需要O(MN2)复杂度。最坏的情况,N个解各自构成一个前沿面,时间复杂度就是O(MN3)。
- 快速非支配排序方法:首先需要计算两个变量 1)支配计数np,即支配解p的解的数量。2)Sp,即解p支配的解的集合。

如上图所示,对解c而言,解c被a,b两个解支配,所以nc为2。对解b而言,解b支配c,d,e,所以Sb={c,d,e}。
算法流程如下:

首先计算每个解的np和Sp,这需要O(MN2)时间复杂度,同时得到了第一前沿面
F
1
\mathcal{F1}
F1。第二部分就是计算其余的前沿面,需要遍历前沿面中的每个解以及这个解的Sp集合,将对应解p的np值减一,如果np值减为0了,就加入下一前沿面集合,这部分需要O(N2)时间复杂度。由于第一部分和第二部分是分开的,所以总的时间复杂度是O(MN2)。
B、拥挤比较算子
密度估计:对同一前沿面的解按照目标函数值排序,计算每个解在该目标函数下两侧解的目标函数归一化差值,然后将所有目标函数下的分量累加作为拥挤系数。
具体算法流程如下:

如图所示,直观上看,解i的拥挤系数就是虚线矩形周长的一半。
C、主程序
- 种群初始化:随机创建父代种群P0 ,为父代种群利用快速非支配排序算法计算出非支配等级,并利用二进制锦标赛机制重组以及变异算子生成大小为N的子代种群。
- 子代个体选择

代码实现
参考博客链接
https://www.omegaxyz.com/2017/05/04/nsga2matlabzdt1/
主体代码是这篇博客的,仅将非支配排序部分修改为了原始论文中的
%% NSGA-II算法
clear; close all; clc; tic;
%% 初始化/参数设定
generations=100; %迭代次数
popnum=100; %种群大小(须为偶数)
poplength=30; %个体长度
minvalue=repmat(zeros(1,poplength),popnum,1); %个体最小值
maxvalue=repmat(ones(1,poplength),popnum,1); %个体最大值
%% 随机创建父代种群
population=rand(popnum,poplength).*(maxvalue-minvalue)+minvalue; %产生新的初始种群
%% 开始迭代进化
for gene=1:generations %开始迭代
%% 交叉
newpopulation=zeros(popnum,poplength); %子代种群
for i=1:popnum/2 %交叉产生子代
k=randperm(popnum); %从种群中随机选出两个父母,不采用二进制联赛方法
beta=(-1).^round(rand(1,poplength)).*abs(randn(1,poplength))*1.481; %采用正态分布交叉产生两个子代
newpopulation(i*2-1,:)=(population(k(1),:)+population(k(2),:))/2+beta.*(population(k(1),:)-population(k(2),:))./2; %产生第一个子代
newpopulation(i*2,:)=(population(k(1),:)+population(k(2),:))/2-beta.*(population(k(1),:)-population(k(2),:))./2; %产生第二个子代
end
%% 变异
k=rand(size(newpopulation)); %随机选定要变异的基因位
miu=rand(size(newpopulation)); %采用多项式变异
temp=k<1/poplength & miu<0.5; %要变异的基因位
newpopulation(temp)=newpopulation(temp)+(maxvalue(temp)-minvalue(temp)).*((2.*miu(temp)+(1-2.*miu(temp)).*(1-(newpopulation(temp)-minvalue(temp))./(maxvalue(temp)-minvalue(temp))).^21).^(1/21)-1); %变异情况一
newpopulation(temp)=newpopulation(temp)+(maxvalue(temp)-minvalue(temp)).*(1-(2.*(1-miu(temp))+2.*(miu(temp)-0.5).*(1-(maxvalue(temp)-newpopulation(temp))./(maxvalue(temp)-minvalue(temp))).^21).^(1/21)); %变异情况二
%% 越界处理/种群合并
newpopulation(newpopulation>maxvalue)=maxvalue(newpopulation>maxvalue); %子代越上界处理
newpopulation(newpopulation<minvalue)=minvalue(newpopulation<minvalue); %子代越下界处理
newpopulation=[population;newpopulation]; %合并父子种群
%% 计算目标函数值
functionvalue=zeros(size(newpopulation,1),2); %合并后种群的各目标函数值,这里的问题是ZDT1
functionvalue(:,1)=newpopulation(:,1); %计算第一维目标函数值
g=1+9*sum(newpopulation(:,2:poplength),2)./(poplength-1);
functionvalue(:,2)=g.*(1-(newpopulation(:,1)./g).^0.5); %计算第二维目标函数值
%% 非支配排序,NSGA-II论文中的算法
Sp = zeros(size(newpopulation,1)); %Sp解p支配的解的集合
np = zeros(size(newpopulation,1),1); %支配解p的解的数量
Prank = zeros(size(newpopulation,1),1); %每个解的前沿面编号
F = zeros(size(newpopulation,1)); %保存每层前沿面的个体
for p = 1:size(newpopulation,1)
for q = 1:size(newpopulation,1)
if isDomin(functionvalue,p,q)
Sp(p,sum(Sp(p,:)~=0)+1) = q;
elseif isDomin(functionvalue,q,p)
np(p) = np(p)+1;
end
end
if np(p)==0
Prank(p) = 1;
F(1,sum(F(1,:)~=0)+1) = p;
end
end
i = 1;
while sum(F(i,:)~=0)
Q = zeros(1,size(F,2));
for p = 1:sum(F(i,:)~=0)
for q = 1:sum(Sp(F(i,p),:)~=0)
np(Sp(F(i,p),q)) = np(Sp(F(i,p),q)) - 1;
if np(Sp(F(i,p),q))==0
Prank(Sp(F(i,p),q)) = i+1;
Q(sum(Q(:)~=0)+1) = Sp(F(i,p),q);
end
end
end
i = i+1;
F(i,:) = Q;
end
%% 计算拥挤距离/选出下一代个体
fnum = 0;
psum = 0;
while psum <= popnum %判断前多少个面的个体能完全放入下一代种群
fnum = fnum + 1;
psum = psum + sum(F(fnum,:)~=0);
end
num = popnum - (psum - sum(F(fnum,:)~=0)); %num表示在当前前沿面需要多少个个体
population(1:popnum-num,:) = newpopulation(Prank<fnum,:);
% 计算当前前沿面个体的拥挤距离
I = F(fnum,:);
l = sum(I(:)~=0);
I = I(1:l);
I_distance = zeros(l,1);
fmax = max(functionvalue(I,:),[],1);
fmin = min(functionvalue(I,:),[],1);
for m = 1:size(functionvalue,2)
[~,oldsite] = sortrows(functionvalue(I,m));
I_distance(oldsite(1)) = inf;
I_distance(oldsite(end)) = inf;
for i = 2:l-1
I_distance(oldsite(i)) = I_distance(oldsite(i)) + (functionvalue(I(oldsite(i+1)),m)-functionvalue(I(oldsite(i-1)),m))/(fmax(m)-fmin(m));
end
end
I = sortrows([I_distance,I'],1,'descend'); %按拥挤距离降序排序
population(popnum-num+1:popnum,:) = newpopulation(I(1:num,2),:); %将第fnum个面上拥挤距离较大的前num个个体复制入下一代
end
%% 程序输出
fprintf('已完成,耗时%4s秒\n',num2str(toc)); %程序最终耗时
output=sortrows(functionvalue(Prank==1,:)); %最终结果:种群中非支配解的函数值
plot(output(:,1),output(:,2),'*b'); %作图
axis([0,1,0,1]);xlabel('F_1');ylabel('F_2');title('ZDT1')
结果

多目标优化经典算法——NSGA-II的更多相关文章
- 目标检测 | 经典算法 Cascade R-CNN: Delving into High Quality Object Detection
作者从detector的overfitting at training/quality mismatch at inference问题入手,提出了基于multi-stage的Cascade R-CNN ...
- 多目标优化算法(一)NSGA-Ⅱ(NSGA2)(转载)
多目标优化算法(一)NSGA-Ⅱ(NSGA2) 本文链接:https://blog.csdn.net/qq_40434430/article/details/82876572多目标优化算法(一)NSG ...
- 从NSGA到 NSGA II
NSGA(非支配排序遗传算法).NSGAII(带精英策略的非支配排序的遗传算法),都是基于遗传算法的多目标优化算法,都是基于pareto最优解讨论的多目标优化,遗传算法已经做过笔记,下面介绍paret ...
- 机器学习经典算法详解及Python实现--基于SMO的SVM分类器
原文:http://blog.csdn.net/suipingsp/article/details/41645779 支持向量机基本上是最好的有监督学习算法,因其英文名为support vector ...
- 脑洞大开--一条项目中常用的linux命令引发的经典算法题
小时候家里定了<读者>的月刊,里面记录一个故事:说有有个偏僻的乡村一日突然来了一个美女,她携着万贯家财子女在当地安家落户,成了当地的乡绅.她让她的子女世世代代的保守这个秘密,直到这个秘密不 ...
- 一条项目中常用的linux命令引发的经典算法题
小时候家里定了<读者>的月刊,里面记录一个故事:说有有个偏僻的乡村一日突然来了一个美女,她携着万贯家财子女在当地安家落户,成了当地的乡绅.她让她的子女世世代代的保守这个秘密,直到这个秘密不 ...
- AI佳作解读系列(二)——目标检测AI算法集杂谈:R-CNN,faster R-CNN,yolo,SSD,yoloV2,yoloV3
1 引言 深度学习目前已经应用到了各个领域,应用场景大体分为三类:物体识别,目标检测,自然语言处理.本文着重与分析目标检测领域的深度学习方法,对其中的经典模型框架进行深入分析. 目标检测可以理解为是物 ...
- Java中的经典算法之选择排序(SelectionSort)
Java中的经典算法之选择排序(SelectionSort) 神话丿小王子的博客主页 a) 原理:每一趟从待排序的记录中选出最小的元素,顺序放在已排好序的序列最后,直到全部记录排序完毕.也就是:每一趟 ...
- JS的十大经典算法排序
引子 有句话怎么说来着: 雷锋推倒雷峰塔,Java implements JavaScript. 当年,想凭借抱Java大腿火一把而不惜把自己名字给改了的JavaScript(原名LiveScript ...
- 三白话经典算法系列 Shell排序实现
山是包插入的精髓排序排序,这种方法,也被称为窄增量排序.因为DL.Shell至1959提出命名. 该方法的基本思想是:先将整个待排元素序列切割成若干个子序列(由相隔某个"增量"的元 ...
随机推荐
- 基于YOLO和PSPNet的目标检测与语义分割系统(python)
基于YOLO和PSPNet的目标检测与语义分割系统 源代码地址 概述 这是我的本科毕业设计 它的主要功能是通过YOLOv5进行目标检测,并使用PSPNet进行语义分割. 本项目YOLOv5部分代码基于 ...
- 40.TokenAuthentication认证
TokenAuthentication认证介绍 TokenAuthentication是一种简单的基于令牌的HTTP认证 适用于CS架构,例如普通的桌面应用程序或移动客户端 TokenAuthen ...
- vue中动态引入图片为什么要是require, 你不知道的那些事
相信用过vue的小伙伴,肯定被面试官问过这样一个问题:在vue中动态的引入图片为什么要使用require 有些小伙伴,可能会轻蔑一笑:呵,就这,因为动态添加src被当做静态资源处理了,没有进行编译,所 ...
- Python基础部分:9、数据的类型和内置方法
目录 一.数据类型内置方法理论 1.什么是数据内置方法 2.如何调用数据内置方法 二.整型(int)内置方法与操作 1.类型转换 2.进制数转换 三.浮点型(float)内置方法与操作 1.类型转换 ...
- swap,传参实质
void swap(int a,int b){ int s=a; a=b; b=s; } int main(){ int x=1,y=2; swap(x,y); } 上面的函数并不能实现交换,因为传参 ...
- c++学习笔记(入门)
1 struct和class的区别 struct成员变量(成员函数)的访问属性缺省的情况下默认为public. class成员变量(成员函数)的访问属性缺省的情况下默认为private. 2 初始化列 ...
- Permanently added the RSA host key for IP address '192.30.253.113' to the list of known hosts.
$git push origin master 报错: Warning: Permanently added the RSA host key for IP address '192.30.253.1 ...
- 【SQL查询】必会的常用函数:条件函数、日期函数、文本函数、窗口函数
〇.概述 1.内容 选择(双分支.多分支) 一.条件函数 1.计算25岁以上和以下的用户数量 CASE THEN END多分支选择 SELECT (CASE WHEN age>=25 THEN ...
- Mybatis-Plus 对 json 的存储使用支持
Mybatis-Plus 对 json 的存储使用支持 场景分析: 随着数据库对字段类型支持的多元化,json 类型的存储已成为多场景高频使用的字段类型.而 MySql.postgrpSql 等都支持 ...
- JS中BOM与DOM操作
BOM操作 window对象 是与浏览器窗口做交互的语言 BOM = Browser Object Model 是指浏览器对象模型,它可以使Javascript 有能力和浏览器进行对话 window. ...