在城市规划、物流配送、公共服务设施布局等领域,如何在有限的资源下实现最大的效益是一个关键问题。例如,确定多少个供给点(如超市、医院、消防站)既能覆盖尽可能多的需求点(居民区、商业区等),又能控制建设和运营成本。今天,我们通过一段 MATLAB 代码,探索如何利用多目标优化寻找选址问题中的最优解,揭示帕累托前沿在资源分配中的智慧。
一、数据生成:构建虚拟的需求与供给场景 🏙️
n1=100;%需求点个数
Map_max=35;%地图大小
xy1=rand(n1,2)*Map_max;%需求点坐标
n2=100;%供给点个数
xy2=rand(n2,2)*Map_max;%供给点坐标
pop=500+randi(20000,n1,1);%随机生成人口
pop_all=sum(pop);
R=5;%覆盖范围
代码首先设定了需求点(\(n_1 = 100\))和供给点(\(n_2 = 100\))的数量,在大小为 \(\text{Map\_max} = 35\) 的地图上随机生成它们的坐标(\(\text{xy}_1, \text{xy}_2\))。每个需求点的人口数量 \(\text{pop}\) 随机生成(500 到 20000 之间),总人 \(\text{pop\_all}\) 为所有需求点人口之和。覆盖范围 R 设为 5,表示供给点能服务周围 5 单位距离内的需求点。
二、可视化呈现:直观展示站点分布 🌍
figure;%绘图
scatter(xy1(:,1),xy1(:,2),5+pop.^(1/2),pop,'filled')
txt1=1:n1;
text(xy1(:,1)+Map_max/50,xy1(:,2),strcat("需",string(num2str(txt1'))),"FontSize",7);
hold on
scatter(xy2(:,1),xy2(:,2),40,'r^')
text(xy2(:,1)+Map_max/50,xy2(:,2),strcat("供",string(num2str(txt1'))),"FontSize",7);
c1=colorbar;
set(get(c1,'title'),'string','人口');
legend('需求点','供给点');
title('站点位置示意图')
这部分代码绘制了需求点和供给点的分布图。需求点用散点表示,大小与人口数量相关(人口越多点越大),并标注 “需 + 编号”;供给点用红色三角形表示,标注 “供 + 编号”。颜色条显示人口规模,直观呈现了各需求点的重要性。
三、距离计算与初始解生成:为优化做准备 🧭
D = pdist2(xy1,xy2);%计算需求点到供给点的距离
G_item=10000;%生成n个解
k=randi(n1,G_item,1);%选取供给点的个数
f2=zeros(G_item,1);
r1cell=cell(G_item,1);
for i=1:G_item%求每个解的覆盖率
r1 = randperm(n1,k(i));%提取所选供给站
dtemp=D(:,r1);%提取距离
bw1=any(dtemp<R,2);%判断哪些需求点被覆盖到
f2(i)=sum(pop(bw1))/pop_all;%计算覆盖率
r1cell{i}=r1;
end
D = pdist2(xy1,xy2)
计算每个需求点到所有供给点的距离。然后生成 \(G\_item = 10000\) 个初始解,每个解随机选择 \(k(i)\) 个供给点(\(k(i)\) 随机在 1 到 \(n_1\) 之间)。通过判断需求点到所选供给点的距离是否小于 R,计算每个解的覆盖率 f2(覆盖人口与总人口的比值)。
四、多目标优化:寻找帕累托前沿 🎯
data=[k,-f2];
s=size(data);
options = optimoptions('gamultiobj','PlotFcn',@gaplotpareto,'PopulationSize',s(1),'InitialScores',data,'Generations',1);
[x,fval,exitflag,output,population,scores] = gamultiobj(@(x) x,2,[],[],[],[],[],[],options);
将选点个数 k 和负的覆盖率(因为 gamultiobj
默认最小化,这里要最大化覆盖率,所以取负)组成数据 data
。利用 gamultiobj
进行多目标优化,目标是最小化选点个数(k)和最大化覆盖率(\(-f2\))。options
设置绘图函数(绘制帕累托前沿)、种群大小、初始分数和代数。
五、结果展示:帕累托前沿的意义与解读 📈
clf;
plot(fval(:,1),-fval(:,2),'rp','MarkerSize',9)
xlabel('选点个数');ylabel('覆盖率')
title('帕累托前沿')
set(gca,'FontSize',12);
grid on
hold on
plot(data(:,1),-data(:,2),'k.')
legend('帕累托解集','解空间')
绘制帕累托前沿(\(fval(:,1)\) 为选点个数,\(-fval(:,2)\) 为覆盖率),红色圆点表示帕累托解集,黑色点表示所有初始解。帕累托前沿上的点表示在选点个数和覆盖率之间的最优权衡,比如选点个数少则覆盖率低,选点多则覆盖率高,用户可根据实际需求(如成本限制)选择合适的点。
ind1=zeros(1,size(fval,1));%编号转换
for i=1:size(fval,1)
ind1(i)=find(all(fval(i,:)==data,2),1);
end
ind1=unique(ind1);
for i=1:size(ind1,2)
str='--------------------------------';
disp(str);
str=['第',num2str(i),'个帕累托解:'];
disp(str);
str=['选址个数:',num2str(k(ind1(i)))];
disp(str);
str=['选址供给点编号:',num2str(r1cell{ind1(i)})];
disp(str);
str=['人口覆盖率:',num2str(f2(ind1(i)))];
disp(str);
end
最后输出每个帕累托解的详细信息,包括选址个数、供给点编号和覆盖率,方便用户根据具体情况做出决策。
六、总结:帕累托前沿的价值与应用拓展 💡
通过这段代码,我们展示了如何利用多目标优化解决选址问题,帕累托前沿为我们提供了一系列最优权衡的选择。在实际应用中,这种方法可用于:
- 物流配送:确定最佳配送中心数量,平衡建设成本和服务范围。
- 公共服务:规划医院、学校数量,确保资源高效利用。
- 通信网络:布置基站数量,优化信号覆盖与建设成本。
帕累托前沿的思想不仅仅适用于选址问题,还可拓展到更多多目标优化场景。它教会我们在资源有限的情况下,如何找到最优的平衡,让每一份投入都发挥最大的价值。下次遇到类似的权衡问题,不妨想想帕累托前沿,让数据为决策指引方向~🌟
完整代码(省流版)
clc;clear;close all;
n1=100;%需求点个数
Map_max=35;%地图大小
xy1=rand(n1,2)*Map_max;%需求点坐标
n2=100;%需求点个数
xy2=rand(n2,2)*Map_max;%供给点坐标
pop=500+randi(20000,n1,1);%随机生成人口
pop_all=sum(pop);
R=5;%覆盖范围
figure;%绘图
scatter(xy1(:,1),xy1(:,2),5+pop.^(1/2),pop,'filled')
txt1=1:n1;
text(xy1(:,1)+Map_max/50,xy1(:,2),strcat("需",string(num2str(txt1'))),"FontSize",7);
hold on
scatter(xy2(:,1),xy2(:,2),40,'r^')
text(xy2(:,1)+Map_max/50,xy2(:,2),strcat("供",string(num2str(txt1'))),"FontSize",7);
c1=colorbar;
set(get(c1,'title'),'string','人口');
legend('需求点','供给点');
title('站点位置示意图')
D = pdist2(xy1,xy2);%计算需求点到供给点的距离
G_item=100000;%生成n个解
k=randi(n1,G_item,1);%选取供给点的个数
f2=zeros(G_item,1);
r1cell=cell(G_item,1);
for i=1:G_item%求每个解的覆盖率
r1 = randperm(n1,k(i));%提取所选供给站
dtemp=D(:,r1);%提取距离
bw1=any(dtemp<R,2);%判断哪些需求点被覆盖到
f2(i)=sum(pop(bw1))/pop_all;%计算覆盖率
r1cell{i}=r1;
end
data=[k,-f2];
s=size(data);
% data=-data;
options = optimoptions('gamultiobj','PlotFcn',@gaplotpareto,'PopulationSize',s(1),'InitialScores',data,'Generations',1);
[x,fval,exitflag,output,population,scores] = gamultiobj(@(x) x,2,[],[],[],[],[],[],options);
%fval即为pareto前沿数据
%利用fval数据重新绘制图形灵活度更高
clf;
plot(fval(:,1),-fval(:,2),'rp','MarkerSize',9)
% axis([-1,-0,-1,0])
% set(gca,'XTick',-1:0.1:0,'YTick',-1:0.1:0)
xlabel('选点个数');ylabel('覆盖率')
title('帕累托前沿')
set(gca,'FontSize',12);
grid on
%将pareto前沿与原始数据绘制在同一张图上
hold on
plot(data(:,1),-data(:,2),'k.')
legend('帕累托解集','解空间')
ind1=zeros(1,size(fval,1));%编号转换
for i=1:size(fval,1)
ind1(i)=find(all(fval(i,:)==data,2),1);
end
ind1=unique(ind1);
for i=1:size(ind1,2)
str='--------------------------------';
disp(str);
str=['第',num2str(i),'个帕累托解:'];
disp(str);
str=['选址个数:',num2str(k(ind1(i)))];
disp(str);
str=['选址供给点编号:',num2str(r1cell{ind1(i)})];
disp(str);
str=['人口覆盖率:',num2str(f2(ind1(i)))];
disp(str);
end