新闻详情

新闻详情

首页 / 资讯中心 / 详情

麻雀搜索算法改进:反向学习初始化与柯西变异跳出局部最优的Matlab实现

发布时间:2026/9/29 17:38:38来源:尧图网络
麻雀搜索算法改进:反向学习初始化与柯西变异跳出局部最优的Matlab实现
如果你在Matlab里写过群体智能算法大概率绕不开麻雀搜索算法SSA。它2020年提出之后因为结构直观、参数少、收敛快很快在我手里顶替了粒子群和差分进化成了解决调度、路径规划这类问题的默认选项。但用久了你会发现一个规律SSA前期冲得很快后期却容易一窝蜂扎在某个局部最优点边上怎么都出不来。这篇文章记录的就是我给SSA做的一次小手术——用反向学习改善初始种群质量再用柯西变异去踢一脚陷入局部最优的最优个体完整Matlab代码在文内测试函数、收敛曲线和踩坑经验一起给你。先说结论这套组合拳不需要额外工具箱代码量增加不到三十行但对多峰测试函数的改善通常很直观。下面先把SSA的三个角色讲清楚再讲两个改进点各自的原理最后给完整实现和实验设计。1. 麻雀算法本身在解决什么问题三类角色的分工与短板1.1 发现者、加入者、侦察者的位置更新麻雀算法模拟的是麻雀觅食和躲避危险的行为。种群被分成三类角色发现者、加入者、侦察者。每轮迭代开始前先按适应度排序适应度好的个体排在前面然后按比例分配角色。常见的配置是发现者占70%、侦察者占10%到20%不过我见过不少复现代码把发现者改成20%这两种都有道理后面我会专门聊参数问题。发现者的职责是大范围找食物位置更新分两种情况。当预警值R2小于安全阈值ST时说明周围暂时安全发现者按指数收缩的方式在当前区域精细搜索用Matlab写就是X(i, :) X(i, :) .* exp(-i / (alpha * T));alpha是(0,1]之间的随机数。注意这个公式的实际效果排名越靠前的个体收缩幅度越小相当于在以自身为中心逐渐收拢活动半径排名靠后的发现者收缩更剧烈等于快速放弃原位置、向原点方向靠拢。当R2大于等于ST时说明有危险发现者会飞到一个随机位置X(i, :) X(i, :) randn(1, dim);加入者的策略更直接。排在种群前一半的加入者会向当前最优个体靠拢用的公式里带一个A矩阵作用是在个体飞向最优位置时保留一定方向随机性排在后一半的加入者已经离群体太远干脆飞到更远的地方重新觅食X(i, :) randn * exp((worstX - X(i, :)) / i^2);侦察者是从整个种群中随机抽取的少数个体。如果侦察者发现自己不是最优个体就向最优个体靠近如果它自己就是最优个体说明它正处在危险位置必须用一个小幅随机扰动逃开。这个扰动幅度K限制在[-1,1]之间所以侦察者的逃离能力其实很有限。整个算法的执行顺序是排序、发现者更新、加入者更新、侦察者更新、重算适应度、记录历史最优。结构非常简单这也是它受欢迎的原因。1.2 为什么说SSA后期容易一窝蜂卡在局部最优SSA最大的问题是勘探和开发的节奏不可控。发现者比例如果取0.7大部分个体一开始就在向最优区域收缩种群多样性下降得非常快。加入者中的前一半个体又不断被拉向当前最优位置等于在强化收敛。到了迭代中后期几乎所有个体都挤在同一个吸引域里。这时候侦察者的作用就尴尬了。它的逃离步长K只有[-1,1]对于搜索范围动辄[-100,100]的决策变量来说这个小扰动根本不足以把种群推出当前局部最优的吸引域。如果某次运行运气不好初始种群恰好落在某个多峰函数的错误山峰上后期就很难翻身。这也是为什么针对SSA的改进研究几乎都集中在两个方向一是提高初始种群质量让算法一开始就离全局最优更近二是增强跳出局部最优的能力给陷入僵局的最优个体一个外部扰动。本文的两个改进点刚好分别对应这两个方向前者的实现是反向学习OBL后者是柯西变异。2. 反向学习一种几乎没有额外成本的种群初始化方案2.1 反向解的定义与初始化流程反向学习Opposition-Based Learning的核心思想很朴素对一个候选解同时考虑它的镜像解谁更好就用谁。假设搜索空间是[lb, ub]个体是X那么它的反向解定义为X* lb ub - X举个例子如果搜索空间是[0,1]一个个体是0.2反向解就是0.8如果个体是0.9反向解就是0.1。也就是说反向解把每个个体镜像到了搜索空间的对侧。用在初始化阶段的具体流程是先随机生成N个体再生成这N个体对应的反向解然后把2N个体都做适应度评估排序后取前N个作为初始种群。这一步的实际效果是即使随机初始化分布不均匀反向解也能保证搜索空间两侧都被覆盖到初始种群里的最优个体质量通常会比纯随机初始化更好。这个操作的额外成本是N次适应度评估。对Sphere、Rastrigin这类数学测试函数来说可以忽略不计但如果目标函数要跑一次仿真、单次评估要花几秒甚至几分钟就需要权衡了。2.2 反向学习初始化的Matlab实现在Matlab里实现反向学习初始化非常短我习惯单独写成一个函数方便在多个实验里复用function [X, fit] initialize_with_obl(N, dim, lb, ub, fobj) X rand(N, dim) .* (ub - lb) lb; XOpp lb ub - X; both [X; XOpp]; fit arrayfun((i) fobj(both(i, :)), (1:2*N)); [~, idx] sort(fit); X both(idx(1:N), :); fit fit(idx(1:N)); end这里有几个细节值得注意。一是lb、ub可以是向量这样每个维度都能单独设置边界二是lb ub - X是矩阵运算Matlab会自动对每个维度做镜像不需要循环三是排序后只保留前N个个体这一步等于让初始种群里的优秀个体参与竞争不是简单地把反向解塞进去。我自己测试下来的感受是反向学习初始化的收益在高维多峰函数上最明显。维度30的Rastrigin函数纯随机初始化经常让整个种群一开始就偏到某个错误区域做一轮反向竞争后初始最优值往往能低一个数量级。这个差距会在后续迭代中被不断放大。2.3 和混沌映射初始化相比它赢在简单也许你会问初始化阶段不是还有更常用的混沌映射吗Logistic映射、Tent映射也经常被用来生成均匀分布的初始种群。混沌映射的优点是序列分布更均匀、没有随机聚堆的问题但它需要额外生成混沌序列、还要处理序列周期带来的潜在风险而且它只改进了分布均匀性没有引入竞争择优的机制。反向学习的做法是让原始解和镜像解打一架谁好留谁本质上是一种带反馈的初始化信息利用率更高。从工程角度看反向学习还有两个更实际的优点代码量只有两三行不需要引入任何额外的随机数生成器和后续的迭代逻辑完全解耦你想去掉随时可以去掉不会干扰主算法结构。所以我在这个项目里选了反向学习作为初始种群手段而不是更花哨的混沌初始化。3. 柯西变异扰乱最优个体的厚尾跳跃3.1 柯西分布的厚尾直觉先看标准柯西分布的概率密度函数f(x) 1 / (pi * (1 x^2))它和高斯分布最本质的区别在尾部高斯分布的抽样值大概率集中在均值附近出现大值的概率很低柯西分布的尾巴衰减得非常慢虽然大部分抽样也落在零附近但时不时会蹦出一个绝对值很大的值。这种特性在统计学上叫厚尾。用生活化的方式理解高斯变异像在平静的湖面投石子波纹范围稳定可控柯西变异像湖底偶尔爆炸的水雷大部分时间安静但真炸一次动静非常大。对群体智能算法来说这种偶尔的大动静恰恰是跳出局部最优需要的。我在原始的SSA主循环里试过直接把最优个体加上一个小幅高斯扰动效果很差——高斯扰动只会让最优个体在原地微调根本跳不出当前山峰。换成柯西扰动之后最优个体偶尔会被弹到很远的区域一旦那个区域适应度更好贪心逻辑会立刻接受它收敛曲线就会呈现出一次明显的下降台阶。3.2 变异公式、尺度衰减与贪心接受用Matlab生成标准柯西随机数不需要专门的工具箱利用逆变换采样一行代码搞定cauchy tan(pi * (rand(1, dim) - 0.5));这是整个改进里最值钱的一行代码。有了随机序列对历史最优个体做变异newBest bestX cauchy .* scale;这里的scale是变异尺度我建议让它随着迭代次数衰减前期大尺度探索后期小尺度精细调整。scale 0.1 * (ub - lb) * (1 - t / T);为什么一定要衰减如果全程用固定的大尺度到迭代后期种群已经收敛得很好了一个柯西大跳跃很可能把好不容易找到的好解直接弹飞。虽然我们有贪心接受兜底不至于让历史最优变差但这种无意义的反复大跳跃会浪费迭代次数导致收敛曲线末端不断波动。贪心接受策略也很关键。计算变异后个体的适应度只有比当前历史最优更好才更新bestX和bestFif fobj(newBest) bestF bestX newBest; bestF fobj(newBest); end这个策略保证柯西变异只会让算法变得更好永远不产生劣化。即使某次变异完全失败也就是多算了一次适应度没有任何损失。3.3 高斯变异和柯西变异的行为差异把两种变异放在一起对比会更清楚变异方式扰动分布特征适合场景典型后果高斯变异扰动集中在均值附近方差有限同一吸引域内的精细搜索打破局部最优的能力弱柯西变异厚尾偶发大跨度跳跃从一个吸引域跳向另一个跳出局部最优能力强但需贪心接受兜底这让我想起一个更通用的思路如果你的算法整体收敛过快、容易早熟最优个体上挂一个柯西变异往往比调整侦察者参数更有效因为侦察者数量少、扰动幅度又受限而柯西变异是直接对最关键的个体下手效果更直接。如果目标是最优个体在收敛末期的高精度微调那就该用高斯变异或者尺度缩得很小的柯西变异。本文这套ISSA设计里因为柯西变异加了衰减因子前期跳跃、后期微调两种需求其实算兼顾了。4. 改进版麻雀算法的Matlab完整实现与代码走读4.1 完整代码把反向学习初始化和柯西变异嵌入原始SSA主循环后完整函数如下。这个函数可以当作独立脚本跑也可以打包成函数供其他脚本调用。function [bestX, bestF, conv] ISSA(N, T, lb, ub, dim, fobj) % ISSA: 融合反向学习初始化与柯西变异扰动的改进麻雀搜索算法 % 输入: % N 种群规模 % T 最大迭代次数 % lb 决策变量下界, 1*dim 向量 % ub 决策变量上界, 1*dim 向量 % dim 决策变量维度 % fobj 目标函数句柄, 输入1*dim向量, 输出标量适应度(最小化问题) % 输出: % bestX 全局最优位置 % bestF 全局最优适应度 % conv 收敛曲线, 1*T pd 0.7; % 发现者比例 sd 0.2; % 侦察者比例 ST 0.8; % 安全阈值 pNum round(N * pd); sNum round(N * sd); % ---------- 反向学习初始化 ---------- [X, fit] initialize_with_obl(N, dim, lb, ub, fobj); [bestF, bestIdx] min(fit); bestX X(bestIdx, :); conv zeros(1, T); for t 1:T % 每轮迭代开始前按适应度排序 [fit, sortIdx] sort(fit); X X(sortIdx, :); bestX X(1, :); worstX X(end, :); R2 rand; % ---------- 发现者更新 ---------- for i 1:pNum if R2 ST alpha rand; X(i, :) X(i, :) .* exp(-i / (alpha * T)); else X(i, :) X(i, :) randn(1, dim); end end % ---------- 加入者更新 ---------- for i pNum1:N if i N/2 A ones(1, dim); A(rand(1, dim) 0.5) -1; A_plus A * inv(A * A); L ones(1, dim); X(i, :) bestX abs(X(i, :) - bestX) * (A_plus * L); else Q randn; X(i, :) Q * exp((worstX - X(i, :)) / i^2); end end % ---------- 侦察者更新 ---------- for i 1:sNum idx randi(N); if fit(idx) bestF X(idx, :) bestX randn(1, dim) .* abs(X(idx, :) - bestX); else K -1 2 * rand; X(idx, :) X(idx, :) K .* abs(X(idx, :) - worstX) / (fit(idx) - fit(end) eps); end end % ---------- 边界处理 ---------- X max(X, lb); X min(X, ub); % ---------- 柯西变异最优个体 ---------- cauchy tan(pi * (rand(1, dim) - 0.5)); scale 0.1 * (ub - lb) * (1 - t / T); newBest bestX cauchy .* scale; newBest max(newBest, lb); newBest min(newBest, ub); newFit fobj(newBest); if newFit bestF bestX newBest; bestF newFit; end % ---------- 重算种群适应度 ---------- fit arrayfun((i) fobj(X(i, :)), (1:N)); [minFit, minIdx] min(fit); if minFit bestF bestF minFit; bestX X(minIdx, :); end conv(t) bestF; end end配合前面的initialize_with_obl函数这套代码可以直接复制到Matlab里执行。4.2 主循环里容易写错的顺序问题第一次复现改进SSA时我在执行顺序上踩过坑这里专门说一下。第一迭代开始必须先排序再更新发现者。发现者的身份是从排序后的种群头部取出来的如果不排序直接更新某一轮里发现者可能不是适应度最好的个体算法的收敛方向就偏了。第二历史最优bestX要独立维护不能简单地用排序后的X(1,:)代替。柯西变异产生的新解可能比当前种群所有个体都好此时bestX已经被更新了但X(1,:)还是旧值。如果后续的加入者更新仍以X(1,:)为目标等于在向一个已知更差的个体靠拢白白损失改进效果。第三适应度重算要放在三类角色全部更新完之后。不要在发现者更新完就去重算并重新排序那样会打乱后续加入者和侦察者依赖的种群排列信息。一轮迭代内排序后的fit和X结构要维持到三类更新全部结束。4.3 边界处理我建议的越界策略最初我图省事越界个体直接截断到边界X max(X, lb); X min(X, ub);。后来观察种群分布才发现这种策略会让大量个体堆积在边界上边界附近成为停车场多样性流失很严重。尤其是反向学习初始化阶段镜像解原本应该落在搜索空间对侧截断之后很多被拉回边界反向学习的价值打了折扣。我的建议是对越界个体在越界维度上做随机重生for j 1:dim outIdx X(:, j) lb(j) | X(:, j) ub(j); X(outIdx, j) lb(j) rand(sum(outIdx), 1) .* (ub(j) - lb(j)); end这样处理会保留种群多样性代价是某些越界个体携带的探索方向信息被丢弃了。不过在群体智能算法里越界个体基本意味着无效探索与其截断后在边界扎堆不如重新撒种子。如果你对收敛速度更敏感也可以用镜像映射但我不推荐纯粹截断。5. 用Sphere和Rastrigin验证改进效果收敛曲线怎么看5.1 测试函数与统一实验设置验证改进算法最少要有单峰函数和多峰函数两类测试。我常用的三个函数是% Sphere 单峰函数理论最优0搜索范围[-100, 100] fobjSphere (x) sum(x.^2); % Rastrigin 强多峰函数大量局部最优理论最优0搜索范围[-5.12, 5.12] fobjRastrigin (x) sum(x.^2 - 10 * cos(2 * pi * x) 10); % Ackley 多峰且地形复杂理论最优0搜索范围[-32, 32] fobjAckley (x) -20 * exp(-0.2 * sqrt(sum(x.^2) / numel(x))) ... - exp(sum(cos(2 * pi * x)) / numel(x)) 20 exp(1);统一实验设置是维度dim30种群规模N30最大迭代T500每个算法独立运行30次。原版SSA使用相同的发现者比例、侦察者比例、排序逻辑和边界处理方式唯一区别是没有反向学习初始化和柯西变异。这样才能保证对比结果只反映两个改进点的贡献。跑实验的脚本骨架如下dim 30; N 30; T 500; lb -100 * ones(1, dim); ub 100 * ones(1, dim); fobj (x) sum(x.^2); [~, bestF1, conv1] ISSA(N, T, lb, ub, dim, fobj); % 同时跑原始SSA得到conv2 semilogy(1:T, conv1 eps, b-, 1:T, conv2 eps, r-); legend(ISSA, SSA);注意这里绘图时加了eps。如果最优适应度在某些迭代恰好降到0semilogy会因log(0)产生-Inf导致曲线画不出来这是画收敛曲线时最容易被忽略的细节。5.2 收敛曲线的三种典型读法我自己的经验是看收敛曲线要看三个特征不要只盯着最终值。第一是初始位置。由于反向学习初始化ISSA在t1时的最优值通常会比原始SSA低一个档次。Rastrigin函数上这个特征非常明显纯随机初始化可能初始最优值在50左右反向学习竞争一轮后可能在20左右。这个优势会直接影响后续所有迭代的搜索基础。第二是曲线是否出现台阶式下降。原始SSA在Rastrigin上经常跑几十代曲线纹丝不动说明种群被困在某个吸引域ISSA因为有柯西变异后期经常突然掉下去一截这个台阶就是最优个体被柯西扰动弹出新区域的信号。看到这种台阶说明跳出机制在工作。第三是末端的平滑度。如果柯西变异尺度没有衰减到位收敛曲线末端会不停波动甚至偶尔上跳衰减合理的话末端曲线应该平稳微降。调参时我优先看这个特征来判断尺度衰减系数是否合适。5.3 用多次独立重复而不是单次运气说话单次运行结果很容易骗人。群体智能算法带随机性跑一次的最好值可能有运气成分尤其Rastrigin这种大量局部最优的函数跑30次里偶尔能撞上一次全局最优附近但平均值和标准差才能反映算法的稳定水平。我建议表格至少记录三列平均最优值、标准差、平均运行时间。标准差特别重要它反映算法稳定性。改进算法如果只有均值低但方差大那说明它还是依赖运气只是运气比原版好一点均值低且方差小才是真正更可靠的改进。我实际测试的典型情况是Sphere函数上ISSA的均值略有优势、方差更小Rastrigin函数上ISSA的均值优势明显方差也会小不少因为反向学习初始化让每轮运行的起点都更稳定减少了坏运气开局。6. 调参过程中的常见误区与我的处理方式6.1 柯西变异尺度太大震荡太小无效柯西变异尺度是最容易调崩的参数。我最初直接把scale设成固定值0.1*(ub-lb)结果Rastrigin函数在快收敛时突然又剧烈波动收敛曲线末端像心电图。原因就是后期的大尺度扰动把种群附近好不容易积累的搜索成果打散了。后来改成线性衰减(1 - t/T)末端波动立刻消失。还有一个问题是系数0.1是不是最优。对不同搜索空间可能需要微调搜索范围特别大的问题0.1倍往往偏保守可以试试0.2或0.3高维问题上可以改用基于欧氏距离的归一化尺度scale 0.1 * sqrt(sum((ub - lb).^2) / dim) * (1 - t / T);这种写法把尺度从每个维度独立放大改成整体扰动半径高维时稳定性更好。我自己的经验是先跑一个便宜的多峰函数观察收敛曲线末端是否出现异常波动再决定把系数往大调还是往小调。6.2 反向学习是初始化够用还是迭代叠加严格说融合反向学习可以出现在三个位置初始化阶段、迭代过程中每隔G代生成一次反向解、对最优个体生成反向解。我在这套ISSA里只用了初始化阶段原因有两个。第一初始化阶段做反向学习的收益是最稳定的。这个阶段种群还没有成形反向竞争能提高初始解质量几乎不会破坏任何已积累的信息。第二迭代过程中周期性做反向学习相当于每G代给整个种群来一次镜像换血如果种群已经收敛到某个小区域反向解会全部落在搜索空间另一侧人为注入巨大扰动很可能导致明明已经收敛又硬生生搞发散。这个操作只有在满足替换条件时才值得用例如反向解中至少有一个优于当前最差个体才执行替换否则跳过。如果目标函数是便宜数学函数可以试试每10到20代做一次反向竞争如果目标函数单次评估要跑仿真我建议只保留初始化阶段的反向学习否则额外2N次评估会显著增加实验总时长。6.3 公平对比的几个纪律最后聊几点实验纪律这些都是我自己走过弯路后总结出来的。一是不要顺手改多个因素。很多改进文章把发现者比例也从0.7改成0.2还把侦察者比例调大最后实验效果确实提升了但你根本说不清是哪个策略起的作用。我在对比实验里的原则是原版SSA和ISSA除了反向学习初始化、柯西变异这两个改动外其余参数、排序方式、边界处理完全一致。二是至少跑30次独立重复。只跑一次给个最好值或者只贴一次收敛曲线的做法说服力很差。用平均值加标准差说话必要时加一个Wilcoxon秩和检验看两个算法在统计意义上是否有显著差异。三是注意边界处理和排序逻辑的一致性。如果原版SSA用截断边界ISSA用随机重生边界那么两者的对比就混入了边界策略的影响。这种低级错误我在早期犯过导致某一版实验结果异常漂亮仔细排查才发现是边界处理差异带来的虚假提升。四是要记录运行时间。改进算法如果每轮多了一次柯西变异适应度评估和初始化阶段的N次额外评估运行时间必然略高于原版。在多峰函数上多花的时间通常换来更优的结果这是值得的但如果某个改动让运行时间翻倍、精度只提升一点点就要考虑性价比了。如果你只是给课程作业配一个基线条目或者想给自己的项目选一个默认智能优化算法这套改进SSA直接跑起来是很省心的。多峰函数先运行上面三个测试函数观察收敛曲线形态再决定要不要调整柯西变异尺度。我自己后来做调度类问题也经常把这套ISSA当作与其他算法对比的主力基线——它比原始SSA稳又不像某些复杂改进算法那样代码冗长、参数一堆适合作为长期保留的Matlab工具箱函数。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

STM32 CAN邮箱机制与可靠发送轮询法详解 2026/9/29 18:35:52

STM32 CAN邮箱机制与可靠发送轮询法详解

1. 为什么CAN发送丢包不是“玄学”,而是邮箱机制没吃透 STM32的CAN控制器丢包问题,在调试现场常被归为“运气不好”“总线干扰大”“波特率设错了”——但我在带三个工业客户做CAN通信模块时发现, 超过70%的所谓“丢包”,根本不是…

阅读更多 →
IP5356 Type-C快充芯片硬件配置避坑指南 2026/9/29 18:35:52

IP5356 Type-C快充芯片硬件配置避坑指南

1. 这不是普通充电宝芯片,而是Type-C快充系统的“神经中枢”你手里的Type-C快充移动电源,插上就能亮灯、握手、升压、降压、协商电压——背后真正指挥这一切的,不是那块锂电池,也不是外壳上的LED灯,而是那颗指甲盖大小…

阅读更多 →
PyTorch AMP混合精度训练实战:显存减半与训练吞吐提升指南 2026/9/29 18:35:52

PyTorch AMP混合精度训练实战:显存减半与训练吞吐提升指南

先说个我前阵子遇到的事儿:一个朋友在做LoRA微调,显卡是8G显存,模型刚加载完就报CUDA out of memory,他第一个反应是把batch size从4降到2,结果还是炸,最后来找我问有没有什么办法能在不大改代码的前提下把…

阅读更多 →
Java MES源码落地实战:从解压到产线稳定运行 2026/9/29 18:35:51

Java MES源码落地实战:从解压到产线稳定运行

简介:本资源是一套基于Java开发的MES(制造执行系统)生产管理平台完整源码,面向计算机专业本科生、毕业设计学生及制造业信息化初学者,旨在帮助理解生产计划调度、车间实时监控、质量追溯等核心业务逻辑,并支…

阅读更多 →
YOLOv8s剪枝源码实战:通道剪枝与推理加速 2026/9/29 18:35:51

YOLOv8s剪枝源码实战:通道剪枝与推理加速

模型体积大、推理慢,部署到边缘设备总被嫌弃,这是我在跑YOLOv8s项目时最头疼的问题。后来靠剪枝解决了,实测大概能砍掉30%-50%的参数,推理速度提升明显,精度还能维持在可接受范围。这篇就围绕yolov8s模型剪枝的源码实现…

阅读更多 →
YOLOv8s结构化剪枝实战:从稀疏化训练到边缘部署加速 2026/9/29 18:35:44

YOLOv8s结构化剪枝实战:从稀疏化训练到边缘部署加速

如果你已经在用yolov8s做工业缺陷检测、车流统计或者安防巡检,大概率会遇到同一个尴尬:模型精度能满足要求,但部署到Jetson Nano、RK3588这类边缘设备上时,帧率就是上不去,显存和内存双双告急。换yolov8n又嫌弃精度掉太…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉