粒子群算法运行时间太长怎么办?—教你一招降低94%的运行时间

news2024/11/23 11:39:44

        不管是初学者还是精通智能优化算法(粒子群算法,遗传算法等)的朋友,相信你们都对智能优化算法运行之慢深有体会,对于比较复杂的问题,经常出现运行一次几小时,调试一次几小时的情况。调试了这么多年代码,智能优化算法对我来说算是老朋友了,平时也积累了一些提高智能优化算法运行效率的办法,在此分享给大家。

1.基本粒子群算法和matlab实现

        这篇博客以粒子群算法为例,说明使用matlab编程时,如何减少粒子群算法运行时间。要解决的目标函数为:

        其中,变量x和y都属于[-10,10]的区间,要求出f(x)的最大值。

        matlab绘图代码和图像如下:

[x,y]=meshgrid(-1.2:0.01:1.2);
z=sin( sqrt(x.^2+y.^2) )./sqrt(x.^2+y.^2)+exp((cos(2*pi*x)+cos(2*pi*y))/2)-2.71289;
mesh(x,y,z)

        从函数图形可以看出,该函数有很多局部极大值点,而极值位置为(0,0),在(0,0)附近取得极大值。

        粒子群算法(particle swarm optimization,PSO)最早由Kennedy和Eberhart在1995年提出的,源于对鸟类捕食行为的研究,算法中每个粒子都代表问题的一个潜在解,每个粒子对应一个由适应度函数决定的适应度值。粒子的速度决定了粒子移动的方向和距离,速度随自身及其他粒子的移动经验进行动态调整,从而实现个体在可解空间中的寻优。

        PSO算法首先在可行解空间中初始化一群粒子,每个粒子都代表极值优化问题的一个潜在最优解,用位置、速度和适应度值三项指标表示该粒子特征,适应度值由适应度函数计算得到,其值的好坏表示粒子的优劣。粒子在解空间中运动,通过跟踪个体极值Pbest和群体极值Gbest更新个体位置。个体极值Pbest是指个体所经历位置中计算得到的适应度值最优位置,群体极值Gbest是指种群中的所有粒子搜索到的适应度最优位置。粒子每更新一次位置,就计算一次适应度值,并且通过比较新粒子的适应度值和个体极值、群体极值的适应度值更新个体极值Pbest和群体极值Gbest位置。

        实现粒子群算法求解上述优化问题的原始代码如下:

%% 清除变量
clc
clear
close all
warning off

tic
%% 设置种群参数
sizepop = 500;                      % 初始种群个数
dim = 2;                            % 空间维数
ger = 500;                          % 最大迭代次数    
x_max = 10*ones(1,dim);             % 位置上限
x_min = -10*ones(1,dim);            % 位置下限
v_max = 5*ones(1,dim);              % 速度上限
v_min = -5*ones(1,dim);             % 速度下限
w = 0.9;                            % 惯性权重
c_1 = 1.5;                          % 自我学习因子
c_2 = 1.5;                          % 群体学习因子 
%% 种群初始化
pop = x_min + rand(sizepop,dim).*(x_max-x_min);     % 初始化种群
pop_v = v_min + rand(sizepop,dim).*(v_max-v_min);   % 初始化种群速度        
pop_zbest = pop(1,:);                               % 初始化群体最优位置
pop_gbest = pop;                                    % 初始化个体最优位置
fitness = zeros(1,sizepop);                         % 所有个体的适应度
fitness_zbest = -inf;                               % 初始化群体最优适应度
fitness_gbest = -inf*ones(1,sizepop);               % 初始化个体最优适应度
%% 初始的适应度
for k = 1:sizepop
    % 计算适应度值
    fitness(k) = fun(pop(k,:));
    if fitness(k) > fitness_zbest
        fitness_zbest = fitness(k);
        pop_zbest = pop(k,:);
    end
end
history_pso = zeros(1,ger);            % 粒子群历史最优适应度值
%% 迭代求最优解
iter = 1;
while iter <= ger
    for k = 1:sizepop
        % 更新速度并对速度进行边界处理 
        pop_v(k,:)= w * pop_v(k,:) + c_1*rand*(pop_gbest(k,:) - pop(k,:)) + c_2*rand*(pop_zbest - pop(k,:));
        for kk = 1:dim
            if  pop_v(k,kk) > v_max(kk)
                pop_v(k,kk) = v_max(kk);
            end
            if  pop_v(k,kk) < v_min(kk)
                pop_v(k,kk) = v_min(kk);
            end
        end
        % 更新位置并对位置进行边界处理
        pop(k,:) = pop(k,:) + pop_v(k,:);
        for kk = 1:dim
            if  pop(k,kk) > x_max(kk)
                pop(k,kk) = x_max(kk);
            end
            if  pop(k,kk) < x_min(kk)
                pop(k,kk) = x_min(kk);
            end
        end
        % 更新适应度值
        fitness(k) = fun(pop(k,:));
        if fitness(k) > fitness_zbest
            fitness_zbest = fitness(k);
            pop_zbest = pop(k,:);
        end
        if fitness(k) > fitness_gbest(k)
            fitness_gbest(k) = fitness(k);
            pop_gbest(k,:) = pop(k,:);
        end
    end
    history_pso(iter) = fitness_zbest;
%     disp(['PSO第',num2str(iter),'次迭代最优适应度=',num2str(fitness_zbest)])
    iter = iter+1;
end
time0 = toc;
disp(['运行时间为:',num2str(time0) , '秒'])
disp(['最优解:x=',num2str(pop_zbest)])
disp(['最优函数值=',num2str(fitness_zbest)])
% plot(history_pso,'linewidth',1)
% ylabel('最优适应度值')
% xlabel('迭代次数')

function fitness = fun(pop)
x = pop(1);
y = pop(2);
fitness = sin( sqrt(x.^2+y.^2) )./sqrt(x.^2+y.^2)+exp((cos(2*pi*x)+cos(2*pi*y))/2)-2.71289;
end

        当种群规模为500,最大迭代次数为500时,粒子群算法某次运行结果如下:

        我们可以看到,大约1秒的时间,粒子群算法可以求出最优解为[-1.4828e-9,-5.222e-10],非常接近最优解[0,0]。

2.该写代码以减少运行时间

        其实这份代码在运行时间上还有很大的改进空间,我们可以分块来看。%% 清除变量、%% 设置种群参数与%% 种群初始化这几步基本上都是最优的写法。主要是求适应度和迭代求最优解这里存在循环语句,可以改进写法。

2.1 初始的适应度部分的改写

        首先来看%% 初始的适应度这块代码:

%% 初始的适应度
for k = 1:sizepop
    % 计算适应度值
    fitness(k) = fun(pop(k,:));
    if fitness(k) > fitness_zbest
        fitness_zbest = fitness(k);
        pop_zbest = pop(k,:);
    end
end

        这里用到的循环语句,运行时间肯定会偏长。那么我们可不可以改写这部分代码,去掉循环语句,同时保持效果不变?当然是可以的,但首先需要改写一下fun函数,初始的fun函数是这样的:

function fitness = fun(pop)
x = pop(1);
y = pop(2);
fitness = sin( sqrt(x.^2+y.^2) )./sqrt(x.^2+y.^2)+exp((cos(2*pi*x)+cos(2*pi*y))/2)-2.71289;
end

        由于我们最开始默认传入fun函数的变量pop只代表一个粒子,所以是一个1×2的变量,就可以令x = pop(1),令y = pop(2),计算得到的输出变量fitness也是一个1×1的标量。如果输入变量pop是一个n×2的变量,就不能这样写了,需要把fun函数改写成:

function fitness = fun(pop)
x = pop(:,1);
y = pop(:,2);
fitness = sin( sqrt(x.^2+y.^2) )./sqrt(x.^2+y.^2)+exp((cos(2*pi*x)+cos(2*pi*y))/2)-2.71289;
end

        这样就可以输入一个n×2的变量pop,输出一个n×1变量fitness,直接计算所有粒子的适应度。同时在主函数部分也需要把代码修改为:

%% 初始的适应度
fitness = fun(pop);                                 % 所有个体的适应度
fitness_gbest = fitness;                            % 初始化个体最优适应度
[fitness_zbest,zbest_index] = max(fitness);
pop_zbest = pop(zbest_index,:);                     % 初始化群体最优位置
history_pso = zeros(1,ger);                         % 粒子群历史最优适应度值

        同时也省去了前面初始化的一些代码。

2.2 迭代求最优解部分的改写

        再来看迭代求最优解的部分。首先这里也包括了求适应度的部分,可以直接沿用上面的成果。对于速度和位置的更新,还有进一步改进的空间。

    for k = 1:sizepop
        % 更新速度并对速度进行边界处理 
        pop_v(k,:)= w * pop_v(k,:) + c_1*rand*(pop_gbest(k,:) - pop(k,:)) + c_2*rand*(pop_zbest - pop(k,:));
        for kk = 1:dim
            if  pop_v(k,kk) > v_max(kk)
                pop_v(k,kk) = v_max(kk);
            end
            if  pop_v(k,kk) < v_min(kk)
                pop_v(k,kk) = v_min(kk);
            end
        end
        % 更新位置并对位置进行边界处理
        pop(k,:) = pop(k,:) + pop_v(k,:);
        for kk = 1:dim
            if  pop(k,kk) > x_max(kk)
                pop(k,kk) = x_max(kk);
            end
            if  pop(k,kk) < x_min(kk)
                pop(k,kk) = x_min(kk);
            end
        end
    end

        这部分速度慢的原因同样也是因为有一个for循环,使用matlab中向量化的运算可以避免使用for循环,同时保持代码的效果不变,具体如下:

    % 更新速度并对速度进行边界处理 
    r1 = rand(sizepop , 1)*ones(1 , dim);
    r2 = rand(sizepop , 1)*ones(1 , dim);
    pop_v = w * pop_v + c_1*r1.*(pop_gbest - pop) + c_2*r2.*(pop_zbest - pop);
    pop_v(pop_v > v_max) = v_max(pop_v > v_max);
    pop_v(pop_v < v_min) = v_min(pop_v < v_min);
    % 更新位置并对位置进行边界处理
    pop = pop + pop_v;
    pop(pop > x_max) = x_max(pop > x_max);
    pop(pop < x_min) = x_min(pop < x_min);
    % 更新适应度值
    fitness = fun(pop); 
    if max(fitness) > fitness_zbest
        [fitness_zbest,zbest_index] = max(fitness);
        pop_zbest = pop(zbest_index,:);
    end
    fitness_gbest(fitness_gbest < fitness) = fitness(fitness_gbest < fitness);
    pop_gbest(fitness_gbest < fitness , :) = pop(fitness_gbest < fitness , :);
    history_pso(iter) = fitness_zbest;

        1)速度的更新。在更新速度时,可以采用矢量化的方式。由于对每个粒子都需要生成随机数r1和r2,所以r1和r2的维度应该都是sizepop×1,另外为了可以使用.*运算使r1和pop_v每个元素可以对应相乘,还将其乘上一个全为1的1×dim向量ones(1,dim)。

        而对越限速度的处理方式用到了matlab矩阵中的逻辑索引方式,这里不再赘述,具体可以参考官方文档(查找符合条件的数组元素 - MATLAB & Simulink - MathWorks 中国)

        2)位置的更新,位置的更新直接使用矩阵加法,对位置越限粒子的处理和速度越限时的处理一致。

        3)更新群体所有的适应度的方法和上面提到的一样,而更新群体最优和个体最优时则需要使用到if语句和逻辑索引。

2.3 两份代码运行时间对比

        经过我们的处理,除了迭代求最优解,其他所有的循环语句都被消除了,修改后完整的代码如下:

%% 清除变量
clc
clear
close all
warning off

tic
%% 设置种群参数
sizepop = 500;                      % 初始种群个数
dim = 2;                            % 空间维数
ger = 500;                          % 最大迭代次数    
x_max = 10*ones(sizepop,dim);       % 位置上限
x_min = -10*ones(sizepop,dim);      % 位置下限
v_max = 5*ones(sizepop,dim);        % 速度上限
v_min = -5*ones(sizepop,dim);       % 速度下限
w = 0.9;                            % 惯性权重
c_1 = 1.5;                          % 自我学习因子
c_2 = 1.5;                          % 群体学习因子 
%% 种群初始化
pop = x_min + rand(sizepop,dim).*(x_max-x_min);     % 初始化种群
pop_v = v_min + rand(sizepop,dim).*(v_max-v_min);   % 初始化种群速度        
pop_gbest = pop;                                    % 初始化个体最优位置
%% 初始的适应度
fitness = fun(pop);                                 % 所有个体的适应度
fitness_gbest = fitness;                            % 初始化个体最优适应度
[fitness_zbest,zbest_index] = max(fitness);
pop_zbest = pop(zbest_index,:);                     % 初始化群体最优位置
history_pso = zeros(1,ger);                         % 粒子群历史最优适应度值
%% 迭代求最优解
iter = 1;
while iter <= ger
    % 更新速度并对速度进行边界处理 
    r1 = rand(sizepop , 1)*ones(1 , dim);
    r2 = rand(sizepop , 1)*ones(1 , dim);
    pop_v = w * pop_v + c_1*r1.*(pop_gbest - pop) + c_2*r2.*(pop_zbest - pop);
    pop_v(pop_v > v_max) = v_max(pop_v > v_max);
    pop_v(pop_v < v_min) = v_min(pop_v < v_min);
    % 更新位置并对位置进行边界处理
    pop = pop + pop_v;
    pop(pop > x_max) = x_max(pop > x_max);
    pop(pop < x_min) = x_min(pop < x_min);
    % 更新适应度值
    fitness = fun(pop); 
    if max(fitness) > fitness_zbest
        [fitness_zbest,zbest_index] = max(fitness);
        pop_zbest = pop(zbest_index,:);
    end
    fitness_gbest(fitness_gbest < fitness) = fitness(fitness_gbest < fitness);
    pop_gbest(fitness_gbest < fitness , :) = pop(fitness_gbest < fitness , :);
    history_pso(iter) = fitness_zbest;
%     disp(['PSO第',num2str(iter),'次迭代最优适应度=',num2str(fitness_zbest)])
    iter = iter+1;
end
time0 = toc;
disp(['运行时间为:',num2str(time0) , '秒'])
disp(['最优解:x=',num2str(pop_zbest)])
disp(['最优函数值=',num2str(fitness_zbest)])
% plot(history_pso,'linewidth',1)
% ylabel('最优适应度值')
% xlabel('迭代次数')

function fitness = fun(pop)
x = pop(:,1);
y = pop(:,2);
fitness = sin( sqrt(x.^2+y.^2) )./sqrt(x.^2+y.^2)+exp((cos(2*pi*x)+cos(2*pi*y))/2)-2.71289;
end

        运行结果:

         求出的最优解也很接近实际最优解[0,0],同时求解时间从接近1秒大幅减小到0.06秒,减小的比例达到了惊人的94%!!

        当然,由于粒子群算法是一个随机搜索,时间也具有偶然性,我们多试几次,求平均值,结果如表1所示: 

表1 优化前和优化后代码运行时间对比

次数优化前/秒优化后/秒
10.972730.06014
20.948610.058207
31.05260.056668
40.970830.056835
51.07510.058857
60.95470.055463
70.984520.060969
81.19330.057191
90.982350.060772
101.04970.05734
平均值1.0184440.0582442

        即使是平均值,也相差了94.28%,和我们之前对比的结果相差不大。

        这是一个二维小规模的优化问题,从时间的减少上可能看不出很大的效果,但如果问题的规模很大,算法运行时动不动就要好几个小时,即使能提升50%的运行效率,也能大大节省我们的时间。

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

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

相关文章

数仓架构模型设计参考

1、数据技术架构 1.1、技术架构 1.2、数据分层 将数据仓库分为三层&#xff0c;自下而上为&#xff1a;数据引入层&#xff08;ODS&#xff0c;Operation Data Store&#xff09;、数据公共层&#xff08;CDM&#xff0c;Common Data Model&#xff09;和数据应用层&#xff…

IoTDB原理剖析

一、介绍 IoTDB&#xff08;物联网数据库&#xff09;是一体化收集、存储、管理与分析物联网时序数据的软件系统。 Apache IoTDB采用轻量式架构&#xff0c;具有高性能和丰富的功能。 IoTDB从存储上对时间序列进行排序&#xff0c;索引和chunk块存储&#xff0c;大大的提升时序…

wireshark 安装和使用

wireshark&#xff0c;世界上最受欢迎的网络协议分析器。是一个网络流量分析器&#xff0c;或“嗅探器”&#xff0c;适用于Linux、macOS、*BSD和其他Unix和类Unix操作系统以及Windows。它使用图形用户界面库Qt以及libpcap和npcap作为数据包捕获和过滤库。 wireshark&#xff…

MyBatis 缓存机制复习及项目中的应用经历

背景 想起前两年工作中因为二级缓存默认开启导致的问题&#xff0c;完整的看了一个介绍 MyBatis 缓存机制的视频《MyBatis 缓存基础知识讲解》。 总计知识点&#xff1a; 缓存的类型及开关这是个形同虚设的功能&#xff0c;线上环境应该禁用缓存 MyBatis 缓存分类 MyBasit…

AWD攻防学习总结(草稿状态,待陆续补充)

AWD攻防学习总结 防守端1、修改密码2、备份网站3、备份数据库4、部署WAF5、部署文件监控脚本6、部署流量监控脚本/工具7、D盾扫描&#xff0c;删除预留webshell8、代码审计&#xff0c;seay/fortify扫描&#xff0c;漏洞修复及利用9、时刻关注流量和积分信息&#xff0c;掉分时…

yolov2检测网数据集标注_labelme使用_json2txt格式转换

yolov2检测网数据集标注_labelme使用_json2txt格式转换 一、安装Anaconda二、创建labelme虚拟环境三、使用labelme标注健康非健康猫狗数据3.1 打开数据集所在文件夹3.2 进行标注数据集3.3 json2txt3.4 按文件目录和训练测试数据集重分配 四、数据喂给服务器网络参考链接 一、安…

容器安装Nginx

文章目录 容器安装nginx下载安装容器1、安装docker容器2、安装nginx3、容器运行nginx结果 容器安装nginx 下载安装容器 1、安装docker容器 yum makecache fast # 更新yum缓存 yum-config-manager \--add-repo \http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.…

Rookit系列一 【隐藏网络端口】【支持Win7 x32/x64 ~ Win10 x32/x64】

文章目录 Rookit系列一 【隐藏网络端口】【支持Win7 x32/x64 ~ Win10 x32/x64】前言探究隐藏网络端口netstat分析隐藏网络端口的原理关键数据结构隐藏网络端口源码 效果演示 Rookit系列一 【隐藏网络端口】【支持Win7 x32/x64 ~ Win10 x32/x64】 前言 Rookit是个老生常谈的话…

微服务服务拆分和远程调用

一、服务架构比较 单体架构&#xff1a;简单方便&#xff0c;高度耦合&#xff0c;扩展性差&#xff0c;适合小型项目。例如&#xff1a;学生管理系统 分布式架构&#xff1a;松耦合&#xff0c;扩展性好&#xff0c;但架构复杂&#xff0c;难度大。适合大型互联网项目&#x…

鉴源实验室丨汽车网络安全运营

作者 | 苏少博 上海控安可信软件创新研究院汽车网络安全组 来源 | 鉴源实验室 社群 | 添加微信号“TICPShanghai”加入“上海控安51fusa安全社区” 01 概 述 1.1 背景 随着车辆技术的不断进步和智能化水平的提升&#xff0c;车辆行业正经历着快速的变革和技术进步。智能化…

C/C++内存管理:解析分配、释放与优化

目录 引言 一、栈与堆内存 1.1 栈内存 1.2 堆内存 1.3 示例 C示例 C示例 二 、C语言内存管理方式 2.1 malloc函数 介绍 用法示例 原理剖析 2.2 calloc函数 介绍 用法示例 原理剖析 2.3 realloc函数 介绍 解释 作用 用法示例 原理剖析 2.4 free函数 介…

tidevice+appium在windows系统实施iOS自动化

之前使用iOS手机做UI自动化都是在Mac电脑上进行的&#xff0c;但是比较麻烦&#xff0c;后来看到由阿里开源的tidevice工具可以实现在windows上启动WDA&#xff0c;就准备试一下&#xff0c;记录一下过程。 tidevice的具体介绍可以参考一下这篇文章&#xff1a;tidevice 开源&…

上传图片视频

分布式文件系统MinIo MinIO提供多个语言版本SDK的支持&#xff0c;下边找到java版本的文档&#xff1a; 地址&#xff1a;https://docs.min.io/docs/java-client-quickstart-guide.html MinIO测试&#xff08;上传、删除、下载&#xff09; public class MinioTest {MinioC…

JavaScript中的交互的方式alert,prompt,confirm的用法

一.alert的用法 1.alert 它会显示一条信息,弹出的这个带有信息的小窗口被称为模态窗。“modal” 意味着用户不能与页面的其他部分&#xff08;例如点击其他按钮等&#xff09;进行交互&#xff0c;直到他们处理完窗口。在上面示例这种情况下 —— 直到用户点击“确定”按钮。 …

【二叉树】105. 从前序与中序遍历序列构造二叉树

链接: 105. 从前序与中序遍历序列构造二叉树 先序 能够确定谁是根 中序 知道根之后&#xff0c;能够确定左子树和右子树的范围 例子 根据先序的性质&#xff08;根左右&#xff09;&#xff0c;能够确定根&#xff0c;我们就能够从总序中找出根节点&#xff08;rooti所在…

OpenLayers实战,OpenLayers画线测量距离和画多边形测量区域面积

专栏目录: OpenLayers实战进阶专栏目录 前言 本章使用OpenLayers实现画线测量距离和画多边形测量区域面积这两个功能。 本章代码就是通过OpenLayers的图形绘制功能,通过监听绘制事件获取绘制的图形,并进行计算,就可以得到长度和面积。日常开发中比较常用,所以不废话,立…

Maven构建项目失败 Non-resolvable import POM

Maven构建项目失败 Non-resolvable import POM Non-resolvable import POM: XXX:pom:4.2.0 was not found in https://repo.maven.apache.org/maven2 during a previous attempt.项目结构定位错误解决问题打包碰到另外的问题Failed to execute goal org.springframework.boot:s…

GCC编译过程:预处理->编译->汇编->链接

目录 引言 概括介绍 一、预处理 二、编译 三、汇编 四、链接 总结 引言 当使用集成开发环境&#xff08;IDE&#xff09;进行C语言编程时&#xff0c;点击"编译"按钮后&#xff0c;整个C程序从源代码到可执行文件的生成过程会自动完成。IDE会在后台为我们执行C…

CAD练习——绘制冲压件三视图

首先还是先设置咱们的绘图模板&#xff1a; 这是图层划分&#xff1a; 文字样式设置&#xff1a; 标注样式&#xff1a; 从主视图开始&#xff0c;首先绘制如下图形 用到的快捷指令&#xff1a; L&#xff1a;直线 O&#xff1a;偏移 TR&#xff1a;修剪 效果&#xff1a;…

实现跨域的几种方式

原理 前后端的分离导致了跨域的产生 跨域的三要素&#xff1a;协议 域名 端口 三者有一个不同即产生跨域 例如&#xff1a; http://www.csdn.com https://www.csdn.com 由于协议不同&#xff0c;端口不同而产生跨域 注&#xff1a;http的默认端口80&#xff0c;https的默…