Commit b9d3b3e5 authored by Tong Li's avatar Tong Li

策略最小化换线:小于10全部遍历,大于10用变邻域搜索方法

parent 9a0c5df9
package com.aps.service.Algorithm; package com.aps.service.Algorithm;
import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.OrderSortRule; import com.aps.entity.Algorithm.OrderSortRule;
import com.aps.entity.basic.Order; import com.aps.entity.basic.Order;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
...@@ -14,6 +15,7 @@ import java.time.OffsetDateTime; ...@@ -14,6 +15,7 @@ import java.time.OffsetDateTime;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream;
/** /**
* 增强版订单排序服务(最终版:严格匹配条件数格式) * 增强版订单排序服务(最终版:严格匹配条件数格式)
...@@ -120,90 +122,275 @@ public class OrderSortService { ...@@ -120,90 +122,275 @@ public class OrderSortService {
/** /**
* 基于最小化换线成本的排序 * 基于最小化换线成本的排序
*/ */
private List<Order> sortByMinimizeChangeover(List<Order> orders) {
if (CollectionUtils.isEmpty(orders)) {
return new ArrayList<>();
}
// 定义阈值:当订单数量小于等于这个值时,遍历所有起始点
int threshold = 5;
if (orders.size() <= threshold) {
// 订单数量较少,遍历所有起始点
return sortByMinimizeChangeoverWithAllStarts(orders);
} else {
// 订单数量较多,使用局部搜索
return sortByMinimizeChangeoverWithLocalSearch(orders);
}
}
/** /**
* 基于最小化换线成本的排序,并返回带有优先级信息的订单列表 * 基于遍历所有起始点的最小化换线排序
*/ */
private List<Order> sortByMinimizeChangeover(List<Order> orders) { private List<Order> sortByMinimizeChangeoverWithAllStarts(List<Order> orders) {
if (CollectionUtils.isEmpty(orders)) { if (CollectionUtils.isEmpty(orders)) {
return new ArrayList<>(); return new ArrayList<>();
} }
List<Order> sortedOrders = new ArrayList<>();
List<Order> remainingOrders = new ArrayList<>(orders);
List<Map.Entry<List<Order>, Double>> results = IntStream.range(0, orders.size())
// 选择第一个订单作为起始点 .parallel()
Order currentOrder = remainingOrders.remove(0); .mapToObj(i -> {
currentOrder.setChangeoverPriority(1); List<Order> sortedOrders= generateInitialSolution(ProductionDeepCopyUtil.deepCopyList(orders) ,i);
sortedOrders.add(currentOrder); // 计算总换线成本
int priority=2; double totalCost = calculateTotalChangeoverCost(sortedOrders);
// 每次选择与当前最后一个订单换线成本最低的订单 return new AbstractMap.SimpleEntry<>(sortedOrders, totalCost);
while (!remainingOrders.isEmpty()) { })
double minCost = Double.MAX_VALUE; .collect(Collectors.toList());
List<Order> bestOrders = new ArrayList<>();
List<Order> bestSortedOrders = null;
// 找到换线成本最低的所有订单 double minTotalCost = Double.MAX_VALUE;
for (Order order : remainingOrders) {
double cost = calculateChangeoverCost(currentOrder, order); for (Map.Entry<List<Order>, Double> entry : results) {
if (entry.getValue() < minTotalCost) {
if (cost < minCost) { minTotalCost = entry.getValue();
minCost = cost; bestSortedOrders = entry.getKey();
order.setChangeoverCost(cost);
if(minCost==0){
order.setChangeoverPriority(currentOrder.getPriority());
}else {
order.setChangeoverPriority(priority);
}
bestOrders.clear();
bestOrders.add(order);
} else if (cost == minCost) {
order.setChangeoverCost(cost);
if(minCost==0){
order.setChangeoverPriority(currentOrder.getPriority());
}else {
order.setChangeoverPriority(priority);
}
bestOrders.add(order);
}
} }
}
return bestSortedOrders;
}
// 如果有多个成本相同的订单,将它们作为一组添加
if (!bestOrders.isEmpty()) {
sortedOrders.addAll(bestOrders); /**
remainingOrders.removeAll(bestOrders); * 基于局部搜索的最小化换线排序
// 更新当前订单为最后添加的订单 */
currentOrder = bestOrders.get(bestOrders.size() - 1); private List<Order> sortByMinimizeChangeoverWithLocalSearch(List<Order> orders) {
if(minCost!=0) { if (CollectionUtils.isEmpty(orders)) {
priority++; return new ArrayList<>();
}
// 1. 生成多个初始解,选择最优的一个
List<Order> bestInitialSolution = null;
double bestInitialCost = Double.MAX_VALUE;
// 尝试多个随机起始点
Set<Integer> selectedIndices = new HashSet<>();
Random random = new Random();
int maxAttempts = Math.min(5, orders.size());
while (selectedIndices.size() < maxAttempts) {
int randomIndex = random.nextInt(orders.size());
if (selectedIndices.add(randomIndex)) {
List<Order> initialSolution= generateInitialSolution(ProductionDeepCopyUtil.deepCopyList(orders) ,randomIndex);
double initialCost = calculateTotalChangeoverCost(initialSolution);
if (initialCost < bestInitialCost) {
bestInitialCost = initialCost;
bestInitialSolution = initialSolution;
} }
}
}
List<Order> currentSolution = bestInitialSolution != null ? bestInitialSolution : generateInitialSolution(orders,0);
double currentCost = bestInitialCost;
// 2. 局部搜索(允许所有相邻订单交换)
boolean improved;
int maxIterations = 50;
int iteration = 0;
do {
improved = false;
List<Order> bestNeighbor = new ArrayList<>(currentSolution);
double bestNeighborCost = currentCost;
// 遍历所有相邻订单对,尝试交换
for (int i = 0; i < currentSolution.size() - 1; i++) {
// 交换相邻订单
List<Order> neighbor = new ArrayList<>(currentSolution);
Collections.swap(neighbor, i, i + 1);
double neighborCost = calculateTotalChangeoverCost2(neighbor);
} else { // 如果交换后成本更低,更新最佳邻居
// 以防万一,添加剩余的第一个订单 if (neighborCost < bestNeighborCost) {
Order nextOrder = remainingOrders.remove(0); bestNeighbor = neighbor;
sortedOrders.add(nextOrder); bestNeighborCost = neighborCost;
currentOrder = nextOrder; improved = true;
if(minCost!=0) {
priority++;
} }
} }
// 如果找到更优解,更新当前解
if (improved) {
currentSolution = bestNeighbor;
// assignChangeoverPriority(currentSolution);
currentCost = bestNeighborCost;
}
iteration++;
} while (improved && iteration < maxIterations);
return currentSolution;
}
/**
* 计算订单序列的总换线成本
*/
/**
* 计算订单序列的总换线成本(考虑ChangeoverPriority分组)
*/
private double calculateTotalChangeoverCost2(List<Order> orders) {
if (CollectionUtils.isEmpty(orders) || orders.size() <= 1) {
return 0.0;
} }
return sortedOrders; double totalCost = 0.0;
// 首先为订单分配ChangeoverPriority
assignChangeoverPriority(orders);
totalCost=calculateTotalChangeoverCost(orders);
return totalCost;
} }
public void sortByMinimizeChangeover1(List<Order> orders,Map<Integer, List<Integer>> priorityPaths) /**
{ * 生成初始解(使用贪心算法)
List<Order> optimizedOrders = sortByMinimizeChangeover(orders); */
private List<Order> generateInitialSolution(List<Order> orders,int i) {
if (CollectionUtils.isEmpty(orders)) {
return new ArrayList<>();
}
List<Order> sortedOrders = new ArrayList<>();
List<Order> remainingOrders = new ArrayList<>(orders);
// 选择第一个订单作为起始点
Order currentOrder = remainingOrders.remove(i);
currentOrder.setChangeoverPriority(1);
sortedOrders.add(currentOrder);
int priority=2;
// 每次选择与当前最后一个订单换线成本最低的订单
while (!remainingOrders.isEmpty()) {
double minCost = Double.MAX_VALUE;
List<Order> bestOrders = new ArrayList<>();
// 找到换线成本最低的所有订单
for (Order order : remainingOrders) {
double cost = calculateChangeoverCost(currentOrder, order);
if (cost < minCost) {
minCost = cost;
order.setChangeoverCost(cost);
if(minCost==0){
order.setChangeoverPriority(currentOrder.getChangeoverPriority());
}else {
order.setChangeoverPriority(priority);
}
bestOrders.clear();
bestOrders.add(order);
} else if (cost == minCost) {
order.setChangeoverCost(cost);
if(minCost==0){
order.setChangeoverPriority(currentOrder.getChangeoverPriority());
}else {
order.setChangeoverPriority(priority);
}
bestOrders.add(order);
}
}
// 如果有多个成本相同的订单,将它们作为一组添加
if (!bestOrders.isEmpty()) {
sortedOrders.addAll(bestOrders);
remainingOrders.removeAll(bestOrders);
// 更新当前订单为最后添加的订单
currentOrder = bestOrders.get(bestOrders.size() - 1);
if(minCost!=0) {
priority++;
}
} else {
// 以防万一,添加剩余的第一个订单
Order nextOrder = remainingOrders.remove(0);
sortedOrders.add(nextOrder);
currentOrder = nextOrder;
if(minCost!=0) {
priority++;
}
}
}
return sortedOrders;
}
/**
* 为排序后的订单分配ChangeoverPriority
*/
private void assignChangeoverPriority(List<Order> orders) {
if (CollectionUtils.isEmpty(orders)) {
return;
}
int priority = 1;
orders.get(0).setChangeoverPriority(priority);
for (Order order : optimizedOrders) { for (int i = 1; i < orders.size(); i++) {
double cost = calculateChangeoverCost(orders.get(i - 1), orders.get(i));
priorityPaths.get(order.getId()).add(order.getChangeoverPriority()); if (cost > 0) {//成本为0 ,则在同一级
priority++;
orders.get(i).setChangeoverCost(cost);
}else {
orders.get(i).setChangeoverCost(orders.get(i - 1).getChangeoverCost());
}
orders.get(i).setChangeoverPriority(priority);
}
}
/**
* 计算订单序列的总换线成本
*/
private double calculateTotalChangeoverCost(List<Order> orders) {
if (CollectionUtils.isEmpty(orders) || orders.size() <= 1) {
return 0.0;
} }
// convertPriorityPathsToNumeric(optimizedOrders, priorityPaths);
int i=0; double totalCost = 0.0;
// 001>002 2 cost 75
// 001>003 2 cost 75
//有一点问题,相同ChangeoverPriority 直接换型应该是0,002>003, 然而对象中存的是,
// 001>002,001>003 ,所以相同ChangeoverPriority 不加成本了
// 只计算不同优先级组之间的换线成本
for (int i = 0; i < orders.size() - 1; i++) {
Order currentOrder = orders.get(i);
Order nextOrder = orders.get(i + 1);
// 只有当两个订单的ChangeoverPriority不同时,才计算换线成本
if (currentOrder.getChangeoverPriority() != nextOrder.getChangeoverPriority()) {
totalCost += orders.get(i + 1).getChangeoverCost();
}
}
return totalCost;
} }
/** /**
* 处理排序条件,支持在任意位置应用最小化换线策略 * 处理排序条件,支持在任意位置应用最小化换线策略
*/ */
......
...@@ -53,8 +53,6 @@ class OrderSortServiceTest { ...@@ -53,8 +53,6 @@ class OrderSortServiceTest {
orders.add(order); orders.add(order);
Order order2=new Order(); Order order2=new Order();
order2.setId(2); order2.setId(2);
order2.setOrderId("002"); order2.setOrderId("002");
...@@ -89,7 +87,7 @@ class OrderSortServiceTest { ...@@ -89,7 +87,7 @@ class OrderSortServiceTest {
Order order6=new Order(); Order order6=new Order();
order6.setId(6); order6.setId(6);
order6.setOrderId("005"); order6.setOrderId("006");
order6.setRoutingId(2); order6.setRoutingId(2);
order6.setMaterialCode("M002"); order6.setMaterialCode("M002");
order6.setSerie("S003"); order6.setSerie("S003");
...@@ -161,7 +159,7 @@ class OrderSortServiceTest { ...@@ -161,7 +159,7 @@ class OrderSortServiceTest {
condition3.setSequence(3); condition3.setSequence(3);
condition3.setFieldName("materialCode"); condition3.setFieldName("materialCode");
condition3.setReverse(true); // 高优先级在前 condition3.setReverse(true); // 高优先级在前
conditions.add(condition3); // conditions.add(condition3);
rule.setConditions(conditions); rule.setConditions(conditions);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment