解题思路分享-2022年华中杯数模竞赛A题思路分析和完整代码分享

文摘   2024-08-26 09:00   中国澳门  
最近学校的校内赛快落下帷幕了,很多同学选择了真题进行解答练习,下面分享一些不错的解答,比如该团队的解答:

原题先奉上:

声明:题目仅作讲解展示使用。

这个题在当年的杀伤力还是很大的,这次模拟赛,有团队选择了这个题目作为了练习题,并且做完了,有关数据的问题(我后面看看能不能丢在百度网盘或者飞桨上)方便读者使用。

思路分析:对于问题一,为了研究出货架数量一定时批次最少的分批方案,首先我们应 该先把每一个订单中具有相同性质的订单分在一起,即把货物种类相似的订单放 到同一批次,对其进行聚类分析,再通过基于欧氏距离的聚类算法和基于余弦相似性的聚类算法,对其进行比较计算,找到邻近度系数进行求解,最后,得到最 优的分批策略。

对于问题二,为了研究出总拣选距离最小的货品摆放位置,可以考虑使用贪心算法来求解,即通过求局部最优解来求出全局最优解。依次处理每个订单,根 据订单中货品的位置调整货品的摆放位置,使得每个订单的拣选距离最小,首先 计算每个货品在所有订单中的出现频率,得到货品的优先级顺序,其次初始化货 架布局,将出现频率最高的货品放置在货架的中间,最后遍历所有的订单,依次将订单中的货品按照贪心策略放置在尽量靠近的货架上,调整货架的位置以最小 化当前订单的拣选距离。基于这种思路的贪心算法,可以将每个订单的拣选距离尽可能最小化,最终 使得总拣选距离最小,得到最后的全局最优解。
对于问题三,将给定的一批分拣任务分配给n名分拣工,要求分拣耗时最短, 且每名分拣工的运动距离尽可能均衡。基于题目所给的假设,可以发现与旅行商问题较为相似,将工作时间最小化且运动距离相对均衡设为目标函数,工人速度设为约束条件,每个订单的分配设为决策变量,所有分拣工从1号货架出发,开始当前批次的分拣任务,定义各订单间的距离,建立线性规划模型,最后采用模拟退火算法进行计算,得到最终结果。

接下来我们先放一下代码吧:

数据处理:

clc;clear;load Ding.matload OrderNo.matload ItemNo.matload Quantity.mat% 订 单 总 数Order = max(OrderNo);% 货 品 类 别 数Item = max(ItemNo);% 建 立 存 储 种 类 向 量% Order_Item(x,y)表示 订单x 含不含有 货品y 种类Order_Item = zeros(Order,Item); % 建 立 存 储 数 量 向 量% Order_Item_Quantity(x,y)表示 订单x 含有 货品y 的数量Order_Item_Quantity = zeros(Order,Item); len = length(OrderNo);
for i=1:len Order_Item(OrderNo(i),ItemNo(i)) = 1; Order_Item_Quantity(OrderNo(i),ItemNo(i)) = Quantity(i);end% 记 录 每 个 订 单 的 货 品 总 数Number = sum(Order_Item ,2);

hold on;xlabel('订 单 序 号');ylabel('订 单 中 包 含 的 货 品 种 类 数');title('所 有 订 单 中 的 货 品 种 类 数 量 分 布 示 意 图');New = sort(Number);bar(New);

save('Order_Item.mat','Order_Item');save('Order_Item_Quantity.mat','Order_Item_Quantity');save('Number.mat','Number');

通过余弦相似性的聚类算法进行分批

clc;clear;load OrderNo.matload ItemNo.matload Number.matload Order_Item.mat% N 为行数,含义为订 单 编 号N=max(OrderNo);% M 为列数,含义为种 类 编 号M=max(ItemNo);
L=size(ItemNo,1);% V 是特征向量,V(x,y)表示 订单x 是否含有 货品yV = Order_Item;
X=zeros(N,3); % 1 是第一个,2 是第二个,3 是差异值a=0; % a 是用于累计下标的
%计算临近度系数for i = 1 : N for j = i+1 : N a=a+1; X(a,1)=i; X(a,2)=j; % 用 余弦相识度 计算邻近度 X(a,3)=sum(V(i,:).*V(j,:))/(sqrt(sum(V(i,:).^2))*sqrt(sum(V(j,:).^2))); % 用 欧式距离 计算邻近度 %X(a,3)=1/(sqrt(sum((V(i,:)-V(j,:)).^2)));
endend

% 用于储存 订单批次l=zeros(N,1);for i=1:N l(i,1)=i;endwhile(1) X=sortrows(X,3,'descend'); ok=0; for K = 1 : a i=X(K,1); j=X(K,2); if(size(find(V(i,:)|V(j,:)),2)>200) continue; end ok=1; while(l(i,1)~=l(l(i,1),1)) l(i,1)=l(l(i,1),1); end while(l(j,1)~=l(l(j,1),1)) l(j,1)=l(l(j,1),1); end for kk = find(X(:,1)==i) X(kk,:)=[]; end for kk = find(X(:,2)==i) X(kk,:)=[]; end for kk = find(X(:,1)==j) X(kk,:)=[]; end for kk = find(X(:,2)==j) X(kk,:)=[]; end l(j,1)=l(i,1); V(i,:)=V(i,:)|V(j,:); V(j,1)=-1; a=size(X,1); for kk = 1 :size(V,1) if(kk==i || V(kk,1)==-1) continue; end a=a+1; X(a,1)=min([kk,i]); X(a,2)=max([kk,i]); % 用 余弦相识度 计算邻近度 X(a,3)=sum(V(i,:).*V(kk,:))/(sqrt(sum(V(i,:).^2))*sqrt(sum(V(kk,:).^2))); % 用 欧式距离 计算邻近度 %X(a,3)=1/(sqrt(sum((V(i,:)-V(kk,:)).^2)));

end break; end if(ok==0) break; endendfor i=1:N while(l(i,1)~=l(l(i,1),1)) l(i,1)=l(l(i,1),1); endend
num=0;%用于存储有几个批次index=ones(N,1).*(-1);%存储每一种标号在 cell 中哪个位置,还没放就是-1
%存储 每一订单批次 中所包含的 订单编号as=cell(1,N);for i = 1 : N if(index(l(i,1),1)==-1) num=num+1; index(l(i,1),1)=num; as{num}=[as{num} i]; continue; endas{index(l(i,1),1)}=[as{index(l(i,1),1)} i];pp=index(l(i,1),1);end
% 重新定义一个V,V(x,y)表示 订单x 是否含有 货品yV_2 = Order_Item;

Batch_total = [1,num];Batch_goods = [1,num];gg=0;for i = 1:num Batch_total(1,i) = length(as{i}); O_Bian = as{i}; for j = O_Bian gg=gg+1; cg(gg,:) = V_2(j,:); end gg=0; cg_1 = sum(cg,1); Batch_goods(1,i) = size(find(cg_1),2); cg = [];end

% 将 结 果 存 入 result1.csvresult(1,1)="OrderNo";result(1,2)="GroupNo";for i = 1: N result(i+1,1)="D"+num2str(i, '%04d'); result(i+1,2)=num2str(index(l(i,1),1));endwritematrix(result,'result1.csv');
figure(1);bar(Batch_total);hold on;xlabel('批 次 序 号');ylabel('批 次 中 的 订 单 数');title('订 单 分 批 结 果');
figure(2);bar(Batch_goods);hold on;xlabel('批 次 序 号');ylabel('批 次 中 的 货 架 数');title('批 次 货 架 利 用 情 况');
save('Batch_total.mat','Batch_total');save('Batch_goods.mat','Batch_goods');save('as.mat','as');

通过贪心算法求解最佳货品摆放位置

clc;clear;load as.matload Batch_total.matload Order_Item.mat

putin3 = cell(1,38);% 定义 每一批次 的 订单 中的 货品种类pi = cell(1,38);B = [];for i = 1:38 for j =as{1,i} B = [B;Order_Item(j,:)]; end pi{1,i} = B; B = [];end
aa = cell(1,size(Order_Item,2));
% 用于储存所有批次距离总和sum_zong=0;for k = 1:38 % 对每一批次分别求解 X=[]; Order_B = pi{1,k}; for i = 1 : size(Order_B,2) X(i,1)=i; X(i,2)=sum(Order_B(:,i)); end X=sortrows(X,2,'descend'); % 对该批次中的包含货品i的订单数量进行排序 num=0; B_1 = []; mark = ones(size(Order_B,2),1).*(-1); % 标记 货品 位置
while (1) num = num + 1; if(X(num,2)==0) break; end if(mod(num,2)~=0) B_1 = [B_1 Order_B(:,X(num,1))]; mark(X(num,1),1)=size(B_1,2); else mark(X(num,1),1)=1; mark(:,1)=mark(:,1)+1; B_1=[Order_B(:,X(num,1)) B_1]; end end
for ceng = 1 :size(B_1,1)

while(1) % 判断最左侧的货品 是否能往右移动 i = find(B_1(ceng,:),1); ok=0; for j = i+1 : size(B_1,2) % 计算原来的距离 sum_1=0; for z = 1 :size(B_1,1) de = find(B_1(z,:)); sum_1 = sum_1 + de(size(de,2)) - de(1); end % 交换位置 t=B_1(:,i); B_1(:,i)=B_1(:,j); B_1(:,j)=t;
% 计算交换位置后的距离 sum_2=0; for z = 1 :size(B_1,1) de = find(B_1(z,:)); sum_2 = sum_2 + de(size(de,2)) - de(1); end
% 比较交换前后的距离 if (sum_2 < sum_1) % 满足优化执行下一步 st = mark(i,1); mark(i,1) = mark(j,1); mark(j,1) = st; ok=1; break; end
%不满足优化 再次交换位置复原 t=B_1(:,i); B_1(:,i)=B_1(:,j); B_1(:,j)=t; end if(ok==0) break; end end


while(1) % 判断最右侧的货品 是否能往左移动 i=find(B_1(ceng,:),1,'last'); ok=0; for j = i-1 : -1: 1 sum_1=0; for z = 1 :size(B_1,1) de = find(B_1(z,:)); sum_1 = sum_1 + de(size(de,2)) - de(1); end % 交换位置 t=B_1(:,i); B_1(:,i)=B_1(:,j); B_1(:,j)=t;
% 计算交换位置后的距离 sum_2=0; for z = 1 :size(B_1,1) de = find(B_1(z,:)); sum_2 = sum_2 + de(size(de,2)) - de(1); end
% 比较交换前后的距离 if (sum_2 < sum_1) % 满足优化执行下一步 st = mark(i,1); mark(i,1) = mark(j,1); mark(j,1) = st; ok=1; break; end
%不满足优化 再次交换位置复原 t=B_1(:,i); B_1(:,i)=B_1(:,j); B_1(:,j)=t; end if(ok==0) break; end end end
% 每一批次的距离总和 sum_3(k,1) = 0; for z = 1 :size(B_1,1) de=find(B_1(z,:)); sum_3(k,1)=sum_3(k,1)+de(size(de,2))-de(1); end % 计算所有批次距离总和 sum_zong = sum_zong + sum_3(k,1); for i = 1 : size(Order_B,2) if(mark(i,1)<0) continue; end aa{i}=[aa{i} [k;mark(i,1)]]; end putin3{k} = B_1; end

% 保 存 结 果result(1,1)="ItemNo";result(1,2)="GroupNo";result(1,3)="ShelfNo";num_1 = 1;for i = 1:size(Order_B,2) kk = aa{i}; for j = 1 : size(kk,2) num_1 = num_1 + 1; result(num_1,1) = "P" + num2str(i, '%04d'); result(num_1,2) = num2str(kk(1,j)); result(num_1,3) = num2str(kk(2,j)); endendsum_3(39,1)=sum_zong;writematrix(result,'result2.csv');writematrix(sum_3,'sum.csv');save('sum_3.mat','sum_3');save('aa.mat','aa');save('putin3.mat','putin3');save('pi.mat','pi');
figure(1);bar(sum_3(1:38,1));hold on;xlabel('批 次 序 号');ylabel('的各批分拣任务拣选距离总和');title('各批分拣任务拣选距离示意图');



通过模拟退火算法分配订单任务

clc;clear;load putin3.matload as.mat
% 定义产生新路径的函数function [path1] = New_path(path0) n = length(path0); % 随机选择一种产生新路径的方法 F_p = 0.5; r = rand(1); % 交换两个订单的分配 if r< F_p idx1 = randi(n); idx2 = randi(n); while idx2 == idx1 idx2 = randi(n); end path1 = path0; path1(idx1) = path0(idx2); path1(idx2) = path0(idx1); % 调 整 路 径 顺 序 else idx1 = randi(n); idx2 = randi(n); while idx2 == idx1 idx2 = randi(n); end if idx1 > idx2 tem = idx2; idx2 = idx1; idx1 = tem; end path1 = path0; path1(idx1:idx2) = path0(idx2:-1:idx1); endend

% 定义计算分拣工运动距离的函数function [Len] = lenth(path,S,dd) % path路径(需要处理的订单),S两订单两端点间距离,dd端点位置 Fg_p = find(path(1,:) == 100); tem_z = cell(1,5); tem_z{1} = [100 path(1,1:Fg_p(1))]; tem_z{2} = path(1,Fg_p(1):Fg_p(2)); tem_z{3} = path(1,Fg_p(2):Fg_p(3)); tem_z{4} = path(1,Fg_p(3):Fg_p(4)); tem_z{5} = [path(1,Fg_p(4):size(path,2)) 100]; for k = 1: 5 % 对五个分拣工分别处理 X = tem_z{k}; dp = []; sum = 0; dp(1,1) = 0; dp(1,2) = 0;% 记录订单改变时分别从12端点进入的距离 for i = 1 : size(X(1,:),2)-1 for j = 1 : 2 a = X(1,i); b = X(1,i+1); if(j==1)% 从 端 点 1 dp(i+1,2) = min(dp(i,1)+S(a,1,b,1)+abs(dd(a,1)-dd(a,2)),dp(i,2)+S(a,2,b,1)+abs(dd(a,1)-dd(a,2))); else% 从 端 点 2 dp(i+1,1) = min(dp(i,1)+S(a,1,b,2)+abs(dd(a,1)-dd(a,2)),dp(i,2)+S(a,2,b,2)+abs(dd(a,1)-dd(a,2))); end end end sum = sum + min(dp(size(X(1,:),2),1),dp(size(X(1,:),2),2)); end Len = sum;end



Best_path_Zong = cell(1,38);Min_Len_Zong = zeros(1,38);tic;for k = 1:38 rng('shuffle'); % 控制随机数的生成 Item_p = putin3{k}; dd = [];
% 随机生成 lenth 中的初始 path patht = randperm(size(Item_p,1)); path0 = patht; fen = floor(size(Item_p,1)/5); path1 = [patht(1,1:fen*1) 100 patht(1,fen*1+1:fen*2) 100 patht(1,fen*2+1:fen*3) 100 patht(1,fen*3+1:fen*4) 100 patht(fen*4+1:end)]; % 获取 lenth 中的 dd for i = 1:size(Item_p,1)%得到端点值 v = find(Item_p(i,:)); dd(i,1) = v(1,1); dd(i,2) = v(1,size(v,2)); end dd(100,1)=0; dd(100,2)=0;
% 获取 lenth 中的 S for i=1:size(Item_p,1) for j = 1:size(Item_p,1) S(i,1,j,1)=abs(dd(i,1)-dd(j,1)); S(j,1,i,1)=abs(dd(i,1)-dd(j,1)); S(i,1,j,2)=abs(dd(i,1)-dd(j,2)); S(j,2,i,1)=abs(dd(i,1)-dd(j,2)); S(i,2,j,1)=abs(dd(i,2)-dd(j,1)); S(j,1,i,2)=abs(dd(i,2)-dd(j,1)); S(i,2,j,2)=abs(dd(i,2)-dd(j,2)); S(j,2,i,2)=abs(dd(i,2)-dd(j,2)); end S(i,1,100,1)=dd(i,1); S(i,1,100,2)=dd(i,1); S(i,2,100,1)=dd(i,2); S(i,2,100,2)=dd(i,2); S(100,1,i,1)=dd(i,1); S(100,1,i,2)=dd(i,1); S(100,2,i,1)=dd(i,2); S(100,2,i,2)=dd(i,2); end % 定义模拟退火算法的的一些参数 T0 = 10; % 初始温度 1 T = T0; % 迭代中温度会发生改变,第一次迭代时温度就是 T0 maxgen = 100; % 外层迭代次数 Lk = 1000; % 每个温度下的迭代次数 alpfa = 0.95; % 温度衰减系数 % 调用我们自己写的 lenth 函数计算初始路径的距离 Len0 = lenth(path1,S,dd); Min_Len = Len0; Best_path = path1; RESULT_Min_Len = zeros(maxgen,1); % 用于储存最小距离Min_Len for iter = 1 : maxgen % 外 循 环 for i = 1 : Lk % 内 循 环 patht = New_path(path0); path1 = [patht(1,1:fen*1) 100 patht(1,fen*1+1:fen*2) 100 patht(1,fen*2+1:fen*3) 100 patht(1,fen*3+1:fen*4) 100 patht(fen*4+1:end)]; Len1 = lenth(path1,S,dd); % 新 解 更 优 if Len1 < Len0 path0 = patht; Len0 = Len1; % 原 始 解 更 优,遵循一定概率接受 else p = exp(-(Len1 - Len0)/T); if rand(1) < p path0 = patht; Len0 = Len1; end end % 判断是否更新最优解 if Len0 < Min_Len Min_Len = Len0; Best_path = path1; end end RESULT_Min_Len(iter) = Min_Len; % 保存本轮外循环结束后找到的最小距离 T = alpfa*T; end Min_Len_Zong(1,k) = Min_Len; % 保存循环结束后找到的最小距离 Best_path_Zong{1,k} = Best_path; % 保存循环结束后找到的最优路径endtime = toc;


% 保 存 数 据result3(1,1)="OrderNo";result3(1,2)="GroupNo";result3(1,3)="WorkerNo";result3(1,4)="TaskNo";num=1;for k = 1:38 Best_path = Best_path_Zong{k}; Ding_h = as{k}; Fg_p_1 = find(Best_path(1,:) == 100); tem_z = cell(1,5); tem_z{1} = Best_path(1,1:Fg_p_1(1)-1); tem_z{2} = Best_path(1,Fg_p_1(1)+1:Fg_p_1(2)-1); tem_z{3} = Best_path(1,Fg_p_1(2)+1:Fg_p_1(3)-1); tem_z{4} = Best_path(1,Fg_p_1(3)+1:Fg_p_1(4)-1); tem_z{5} = Best_path(1,Fg_p_1(4)+1:end); for i =1:5 x = tem_z{i}; for j = 1:size(x,2) num = num+1; result3(num,1) = "D"+num2str(Ding_h(1,x(j)), '%04d'); result3(num,2) = num2str(k); result3(num,3) = num2str(i); result3(num,4) = num2str(j); end endend
writematrix(result3,'result3.csv');writematrix(Min_Len_Zong,'Min_Len_Zong.csv');
save('Best_path_Zong.mat','Best_path_Zong');save('Min_Len_Zong.mat','Min_Len_Zong');

% Lk =25,maxgen =100时的迭代数据figure(1);plot(RESULT_Min_Len);hold on;xlabel('一定内层迭代次数下的外层迭代次数');ylabel('第38批次的最小分拣距离');title('优化目标值随迭代次数变化表');

figure(2);bar(Min_Len_Zong);hold on;xlabel('订单批次数');ylabel('最小分拣距离');title('每一批次最小分拣距离表');

完整源代码见上。

如下展示效果图:

第一问

第三问效果图。

代码分享就到这里吧。

感谢:2022级数学专业 费紫洛,肖烨,杨沛源(均为本科生)供稿。

师苑数模
发布数模协会培训推文,讲解数模算法。赛题讲解及比赛通知。学校竞赛结果及学校竞赛成绩发布等文章。
 最新文章