原题先奉上:
声明:题目仅作讲解展示使用。
思路分析:对于问题一,为了研究出货架数量一定时批次最少的分批方案,首先我们应 该先把每一个订单中具有相同性质的订单分在一起,即把货物种类相似的订单放 到同一批次,对其进行聚类分析,再通过基于欧氏距离的聚类算法和基于余弦相似性的聚类算法,对其进行比较计算,找到邻近度系数进行求解,最后,得到最 优的分批策略。
接下来我们先放一下代码吧:
数据处理:
clc;clear;
load Ding.mat
load OrderNo.mat
load ItemNo.mat
load 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.mat
load ItemNo.mat
load Number.mat
load Order_Item.mat
% N 为行数,含义为订 单 编 号
N=max(OrderNo);
% M 为列数,含义为种 类 编 号
M=max(ItemNo);
L=size(ItemNo,1);
% V 是特征向量,V(x,y)表示 订单x 是否含有 货品y
V = 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)));
end
end
% 用于储存 订单批次
l=zeros(N,1);
for i=1:N
l(i,1)=i;
end
while(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;
end
end
for i=1:N
while(l(i,1)~=l(l(i,1),1))
l(i,1)=l(l(i,1),1);
end
end
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;
end
as{index(l(i,1),1)}=[as{index(l(i,1),1)} i];
pp=index(l(i,1),1);
end
% 重新定义一个V,V(x,y)表示 订单x 是否含有 货品y
V_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.csv
result(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));
end
writematrix(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.mat
load Batch_total.mat
load 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));
end
end
sum_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.mat
load 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);
end
end
% 定义计算分拣工运动距离的函数
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;% 记录订单改变时分别从1,2端点进入的距离
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; % 保存循环结束后找到的最优路径
end
time = 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
end
end
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级数学专业 费紫洛,肖烨,杨沛源(均为本科生)供稿。