src/main/java/com/zy/asrs/task/OrderToSortLineScheduler.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/zy/asrs/utils/GroupedLockerOptimizerUtils.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/zy/asrs/utils/MultiLockerOptimizerUtils.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main/java/com/zy/asrs/utils/ToSortLineUtils.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/main/java/com/zy/asrs/task/OrderToSortLineScheduler.java
@@ -12,6 +12,8 @@ import com.zy.asrs.service.impl.OrderDetlServiceImpl; import com.zy.asrs.task.core.ReturnT; import com.zy.asrs.task.handler.OrderToLineHandler; import com.zy.asrs.utils.GroupedLockerOptimizerUtils; import com.zy.asrs.utils.ToSortLineUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; @@ -35,7 +37,6 @@ @Autowired private BasArmMastSignService basArmMastSignService; @Scheduled(cron = "0/3 * * * * ? ") private void orderToSortLine() { //获取未下发单据 @@ -47,94 +48,60 @@ //遍历单据 for (String orderNo : orderNos) { List<OrderDetl> orderDetls = orderDetlService.selectByOrderNo(orderNo); OrderToLine orderToLine = new OrderToLine(); orderToLine.setOrderNo(orderNo); //单据编号 orderToLine.setCreateTime(System.currentTimeMillis()); //创建时间 Long bindingTag = System.currentTimeMillis();//混搭标记 List<OrderToLine.MatList> matLists = new ArrayList<>(); List<OrderToLine.MatList> matListsRemainder = new ArrayList<>(); List<OrderDetl> orderDetlsRemainder = new ArrayList<>(); for (OrderDetl orderDetl : orderDetls) { Integer number = basArmRulesService.getNumber(orderDetl.getWeight(),orderDetl.getVolume(),orderDetl.getManLength(),orderDetl.getWidth(),orderDetl.getHeight()); if (number == null) { BasArmRules basArmRules = new BasArmRules(); basArmRules.setMaterialHeight(orderDetl.getHeight()); basArmRules.setMaterialWeight(orderDetl.getWeight()); basArmRules.setMaterialLength(orderDetl.getManLength()); basArmRules.setMaterialWidth(orderDetl.getWidth()); basArmRulesService.insert(basArmRules); return; } else if (number == 0){ return; } Double anfme = orderDetl.getAnfme(); Double remainder = anfme % number; //取余 余数混搭 // remainder = 0D; if(!remainder.equals(0D)){ // 组装物料信息 OrderToLine.MatList matMix = new OrderToLine.MatList( orderDetl.getMatnr(), // matnr -> sku orderDetl.getStandby1(), // supp -> po orderDetl.getAnfme(), // 余料 -> 剩余余料统一时间戳标记 orderDetl.getStandby2(), //barcode -> upc 条码 1, null, //origin -> supplier 货源 bindingTag //余料标记相同 ); // matListsRemainder.add(matMix); // orderDetlsRemainder.add(orderDetl); matLists.add(matMix); } else { Double ctns = anfme - remainder; // 组装物料信息 OrderToLine.MatList mat = new OrderToLine.MatList( orderDetl.getMatnr(), // matnr -> sku orderDetl.getStandby1(), // supp -> po orderDetl.getAnfme(), //整料 orderDetl.getStandby2(), //barcode -> upc 1, null, //origin -> supplier 货源 System.currentTimeMillis() ); matLists.add(mat); } } // // 智能分组余料 // if (matListsRemainder.size() > 0) { // groupRemainders(orderDetlsRemainder, orderDetlsRemainder.size(), orderDetls.size(), matLists); // } orderToLine.setMatList(matLists); try{ ReturnT<String> returnT = orderToLineHandler.start(orderToLine); if (!returnT.isSuccess()) { log.error("下发单据失败===>"+ JSON.toJSON(orderToLine)); } try{ for (OrderToLine.MatList matList:orderToLine.getMatList()){ BasArmMastSign basArmMastSign = new BasArmMastSign(); basArmMastSign.setMatnr(matList.getSku()); basArmMastSign.setOrderNo(orderToLine.getOrderNo()); basArmMastSign.setSku(matList.getSku()); basArmMastSign.setPo(matList.getPo()); basArmMastSign.setUpc(matList.getUpc()); basArmMastSign.setSupplier(matList.getSupplier()); basArmMastSign.setStatus(0); basArmMastSign.setCreateTime(matList.getBindingTags()); basArmMastSignService.insert(basArmMastSign); List<OrderDetl> orderDetls = orderDetlService.selectByOrderNo(orderNo); List<GroupedLockerOptimizerUtils.Item> items = new ArrayList<>(); for (OrderDetl orderDetl:orderDetls){ Integer number = basArmRulesService.getNumber(orderDetl.getWeight(),orderDetl.getVolume(),orderDetl.getManLength(),orderDetl.getWidth(),orderDetl.getHeight()); if (number == null) { BasArmRules basArmRules = new BasArmRules(); basArmRules.setMaterialHeight(orderDetl.getHeight()); basArmRules.setMaterialWeight(orderDetl.getWeight()); basArmRules.setMaterialLength(orderDetl.getManLength()); basArmRules.setMaterialWidth(orderDetl.getWidth()); basArmRulesService.insert(basArmRules); return; } else if (number == 0){ return; } }catch (Exception e){ String name = ToSortLineUtils.MergerParameter(orderDetl.getMatnr(),orderDetl.getStandby1(),orderDetl.getStandby2()); int maxCapacity = number; int stock = orderDetl.getAnfme().intValue(); items.add(new GroupedLockerOptimizerUtils.Item(name, maxCapacity, stock)); } OrderToLine orderToLine = new OrderToLine(); orderToLine.setOrderNo(orderNo); //单据编号 orderToLine.setCreateTime(System.currentTimeMillis()); //创建时间 OrderToLine orderToLineR = ToSortLineUtils.GetOrderToLine(items, orderToLine); try{ ReturnT<String> returnT = orderToLineHandler.start(orderToLineR); if (!returnT.isSuccess()) { log.error("下发单据失败===>"+ JSON.toJSON(orderToLineR)); } else { try{ for (OrderToLine.MatList matList:orderToLineR.getMatList()){ BasArmMastSign basArmMastSign = new BasArmMastSign(); basArmMastSign.setMatnr(matList.getSku()); basArmMastSign.setOrderNo(orderToLineR.getOrderNo()); basArmMastSign.setSku(matList.getSku()); basArmMastSign.setPo(matList.getPo()); basArmMastSign.setUpc(matList.getUpc()); basArmMastSign.setSupplier(matList.getSupplier()); basArmMastSign.setStatus(0); basArmMastSign.setCreateTime(matList.getBindingTags()); basArmMastSignService.insert(basArmMastSign); } }catch (Exception e){} } } catch (Exception e){ log.error("下发单据异常===>"+e.getMessage()); } } catch (Exception e){ log.error("下发单据异常===>"+e.getMessage()); log.error("下发单据异常,跳转下一个订单===>"+e.getMessage()); } } } src/main/java/com/zy/asrs/utils/GroupedLockerOptimizerUtils.java
New file @@ -0,0 +1,466 @@ package com.zy.asrs.utils; import java.util.*; public class GroupedLockerOptimizerUtils { public static class Item { String name; double unitSpace; int maxCapacity; int stock; int remaining; public Item(String name, int maxCapacity, int stock) { this.name = name; this.maxCapacity = maxCapacity; this.unitSpace = 1.0 / maxCapacity; this.stock = stock; this.remaining = stock; } @Override public String toString() { return name + "(单放" + maxCapacity + "个, 库存" + stock + "个)"; } } static class Locker { int id; double remainingSpace; Map<String, Integer> contents; Set<String> itemTypes; // 当前储物柜中的物品种类 public Locker(int id) { this.id = id; this.remainingSpace = 1.0; this.contents = new HashMap<>(); this.itemTypes = new HashSet<>(); } public boolean canAdd(Item item, int quantity) { return remainingSpace >= quantity * item.unitSpace; } public void addItem(Item item, int quantity) { double spaceUsed = quantity * item.unitSpace; remainingSpace -= spaceUsed; contents.put(item.name, contents.getOrDefault(item.name, 0) + quantity); itemTypes.add(item.name); item.remaining -= quantity; } // 检查是否已经包含该种类的物品 public boolean containsItemType(String itemName) { return itemTypes.contains(itemName); } // 检查是否可以优先放置同种物品(已有同种物品且还有空间) public boolean canAddSameType(Item item) { return containsItemType(item.name) && canAdd(item, 1); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("储物柜").append(id).append(": "); sb.append(String.format("剩余空间%.4f", remainingSpace)).append("\n"); for (Map.Entry<String, Integer> entry : contents.entrySet()) { sb.append(" ").append(entry.getKey()).append(": ").append(entry.getValue()).append("个\n"); } return sb.toString(); } } static class PackingSolution { List<Locker> lockers; int totalLockersUsed; double overallUtilization; int itemTypeChanges; // 物品种类切换次数(衡量集中度) public PackingSolution() { this.lockers = new ArrayList<>(); } public void addLocker(Locker locker) { lockers.add(locker); totalLockersUsed = lockers.size(); } public void calculateMetrics() { double totalUsedSpace = 0; itemTypeChanges = 0; String lastItemType = null; for (Locker locker : lockers) { totalUsedSpace += (1.0 - locker.remainingSpace); for (String itemType : locker.itemTypes) { if (lastItemType != null && !lastItemType.equals(itemType)) { itemTypeChanges++; } lastItemType = itemType; } } overallUtilization = totalUsedSpace / totalLockersUsed; } @Override public String toString() { calculateMetrics(); StringBuilder sb = new StringBuilder(); sb.append("=== 分组优化摆放方案 ===\n"); sb.append("使用的储物柜数量: ").append(totalLockersUsed).append("个\n"); sb.append("平均空间利用率: ").append(String.format("%.2f", overallUtilization * 100)).append("%\n"); sb.append("物品种类切换次数: ").append(itemTypeChanges).append("次(越少越集中)\n\n"); for (Locker locker : lockers) { sb.append(locker.toString()).append("\n"); } return sb.toString(); } } // public static void main(String[] args) { // Scanner scanner = new Scanner(System.in); // // System.out.println("请输入物品种类数量:"); // int itemTypes = scanner.nextInt(); // scanner.nextLine(); // // List<Item> items = new ArrayList<>(); // // for (int i = 0; i < itemTypes; i++) { // System.out.println("\n请输入第" + (i + 1) + "种物品的信息:"); // System.out.print("物品名称: "); // String name = scanner.nextLine(); // // System.out.print("单种存放最大数量: "); // int maxCapacity = scanner.nextInt(); // // System.out.print("库存数量: "); // int stock = scanner.nextInt(); // scanner.nextLine(); // // items.add(new Item(name, maxCapacity, stock)); // } // // // 使用分组优先算法 // PackingSolution solution = packItemsWithGrouping(items); // // System.out.println("\n" + solution); // // scanner.close(); // } public static void main(String[] args) { int itemTypes = 5; List<Item> items = new ArrayList<>(); for (int i = 0; i < itemTypes; i++) { String name = i+"a"; int maxCapacity = i*10; int stock = i*11; items.add(new Item(name, maxCapacity, stock)); } // 使用分组优先算法 PackingSolution solution = packItemsWithGrouping(items); for (Locker locker:solution.lockers) { for (String mantnr : locker.contents.keySet()){ System.out.println(mantnr+"<===>"+locker.contents.get(mantnr)); } } System.out.println("\n" + solution); // scanner.close(); } /** * 分组优先装箱算法 * 优先将同种物品放在一起,只有在不得已时才分开放置 */ public static PackingSolution packItemsWithGrouping(List<Item> items) { List<Item> itemsToPack = new ArrayList<>(); for (Item item : items) { itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock)); } PackingSolution solution = new PackingSolution(); int lockerId = 1; // 第一阶段:优先尝试将每种物品完整放入一个储物柜 for (Item item : itemsToPack) { if (item.remaining == 0) continue; double totalSpaceNeeded = item.remaining * item.unitSpace; // 如果所有物品能放入一个储物柜 if (totalSpaceNeeded <= 1.0) { boolean placedInExisting = false; // 寻找已有储物柜(优先选择空的) for (Locker locker : solution.lockers) { if (locker.contents.isEmpty() && locker.canAdd(item, item.remaining)) { locker.addItem(item, item.remaining); placedInExisting = true; break; } } // 如果没有空储物柜,创建新的 if (!placedInExisting) { Locker newLocker = new Locker(lockerId++); newLocker.addItem(item, item.remaining); solution.addLocker(newLocker); } } } // 第二阶段:处理剩余物品(无法完整放入一个储物柜的) for (Item item : itemsToPack) { while (item.remaining > 0) { // 优先尝试放入已有同种物品的储物柜 Locker bestSameTypeLocker = null; double bestSameTypeSpace = -1; for (Locker locker : solution.lockers) { if (locker.containsItemType(item.name) && locker.canAdd(item, 1)) { if (locker.remainingSpace > bestSameTypeSpace) { bestSameTypeLocker = locker; bestSameTypeSpace = locker.remainingSpace; } } } if (bestSameTypeLocker != null) { int maxCanAdd = (int) Math.floor(bestSameTypeLocker.remainingSpace / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); bestSameTypeLocker.addItem(item, actualAdd); continue; } // 其次尝试放入空储物柜(优先集中放置) Locker emptyLocker = null; for (Locker locker : solution.lockers) { if (locker.contents.isEmpty() && locker.canAdd(item, 1)) { emptyLocker = locker; break; } } if (emptyLocker != null) { int maxCanAdd = (int) Math.floor(emptyLocker.remainingSpace / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); emptyLocker.addItem(item, actualAdd); continue; } // 最后尝试放入任何有空间的储物柜 Locker bestLocker = null; double bestRemaining = Double.MAX_VALUE; for (Locker locker : solution.lockers) { if (locker.canAdd(item, 1) && locker.remainingSpace < bestRemaining) { bestLocker = locker; bestRemaining = locker.remainingSpace; } } if (bestLocker != null) { int maxCanAdd = (int) Math.floor(bestLocker.remainingSpace / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); bestLocker.addItem(item, actualAdd); } else { // 创建新储物柜 Locker newLocker = new Locker(lockerId++); int maxCanAdd = (int) Math.floor(1.0 / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); newLocker.addItem(item, actualAdd); solution.addLocker(newLocker); } } } return solution; } /** * 按种类顺序处理算法 * 先处理一种物品,再处理下一种 */ public static PackingSolution packItemsByType(List<Item> items) { List<Item> itemsToPack = new ArrayList<>(); for (Item item : items) { itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock)); } PackingSolution solution = new PackingSolution(); int lockerId = 1; // 按种类顺序处理 for (Item item : itemsToPack) { while (item.remaining > 0) { // 优先使用当前正在处理该类物品的储物柜 Locker currentTypeLocker = null; for (Locker locker : solution.lockers) { if (locker.containsItemType(item.name) && locker.canAdd(item, 1)) { currentTypeLocker = locker; break; } } if (currentTypeLocker != null) { int maxCanAdd = (int) Math.floor(currentTypeLocker.remainingSpace / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); currentTypeLocker.addItem(item, actualAdd); continue; } // 如果没有同种物品的储物柜,找空储物柜 Locker emptyLocker = null; for (Locker locker : solution.lockers) { if (locker.contents.isEmpty()) { emptyLocker = locker; break; } } if (emptyLocker != null) { int maxCanAdd = (int) Math.floor(emptyLocker.remainingSpace / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); emptyLocker.addItem(item, actualAdd); continue; } // 创建新储物柜专门放这种物品 Locker newLocker = new Locker(lockerId++); int maxCanAdd = (int) Math.floor(1.0 / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); newLocker.addItem(item, actualAdd); solution.addLocker(newLocker); } } return solution; } /** * 比较不同算法的效果 */ public static void compareAlgorithms() { System.out.println("=== 算法比较 ==="); List<Item> testItems = Arrays.asList( new Item("X", 20, 35), new Item("Y", 15, 28), new Item("Z", 34, 45), new Item("T", 25, 18) ); System.out.println("测试物品: " + testItems); // 分组优先算法 PackingSolution groupedSolution = packItemsWithGrouping(testItems); System.out.println("\n分组优先算法:"); System.out.println("储物柜数量: " + groupedSolution.totalLockersUsed); System.out.println("种类切换次数: " + groupedSolution.itemTypeChanges); // 按种类顺序算法 PackingSolution typeOrderSolution = packItemsByType(testItems); System.out.println("\n按种类顺序算法:"); System.out.println("储物柜数量: " + typeOrderSolution.totalLockersUsed); System.out.println("种类切换次数: " + typeOrderSolution.itemTypeChanges); // 原始最佳适应算法(不分组) PackingSolution originalSolution = packItemsOriginal(testItems); System.out.println("\n原始最佳适应算法:"); System.out.println("储物柜数量: " + originalSolution.totalLockersUsed); System.out.println("种类切换次数: " + originalSolution.itemTypeChanges); } /** * 原始最佳适应算法(不分组,作为对比) */ public static PackingSolution packItemsOriginal(List<Item> items) { List<Item> itemsToPack = new ArrayList<>(); for (Item item : items) { itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock)); } // 按空间占用从大到小排序 itemsToPack.sort((a, b) -> Double.compare(b.unitSpace, a.unitSpace)); PackingSolution solution = new PackingSolution(); int lockerId = 1; for (Item item : itemsToPack) { while (item.remaining > 0) { Locker bestLocker = null; double bestRemaining = Double.MAX_VALUE; for (Locker locker : solution.lockers) { if (locker.canAdd(item, 1) && locker.remainingSpace < bestRemaining) { bestLocker = locker; bestRemaining = locker.remainingSpace; } } if (bestLocker != null) { int maxCanAdd = (int) Math.floor(bestLocker.remainingSpace / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); bestLocker.addItem(item, actualAdd); } else { Locker newLocker = new Locker(lockerId++); int maxCanAdd = (int) Math.floor(1.0 / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); newLocker.addItem(item, actualAdd); solution.addLocker(newLocker); } } } return solution; } /** * 可视化展示储物柜使用情况 */ // public static void visualizeSolution(PackingSolution solution) { // System.out.println("=== 储物柜使用情况可视化 ==="); // // for (Locker locker : solution.lockers) { // System.out.println("储物柜 " + locker.id + ":"); // for (Map.Entry<String, Integer> entry : locker.contents.entrySet()) { // int barLength = (int) (entry.getValue() * 50 / (1.0 / 0.05)); // 简化可视化 // String bar = "=".repeat(barLength); // System.out.printf(" %s: %s %d个\n", entry.getKey(), bar, entry.getValue()); // } // System.out.printf(" 剩余空间: %.2f%%\n", locker.remainingSpace * 100); // System.out.println(); // } // } public static void visualizeSolution(PackingSolution solution) { System.out.println("=== 储物柜使用情况可视化 ==="); for (Locker locker : solution.lockers) { System.out.println("储物柜 " + locker.id + ":"); for (Map.Entry<String, Integer> entry : locker.contents.entrySet()) { int barLength = (int) (entry.getValue() * 50 / (1.0 / 0.05)); // 替代 String.repeat() StringBuilder barBuilder = new StringBuilder(); for (int i = 0; i < barLength; i++) { barBuilder.append("="); } String bar = barBuilder.toString(); System.out.printf(" %s: %s %d个\n", entry.getKey(), bar, entry.getValue()); } System.out.printf(" 剩余空间: %.2f%%\n", locker.remainingSpace * 100); System.out.println(); } } } src/main/java/com/zy/asrs/utils/MultiLockerOptimizerUtils.java
New file @@ -0,0 +1,286 @@ package com.zy.asrs.utils; import java.util.*; public class MultiLockerOptimizerUtils { static class Item implements Comparable<Item> { String name; double unitSpace; int maxCapacity; int stock; int remaining; // 剩余未摆放的数量 public Item(String name, int maxCapacity, int stock) { this.name = name; this.maxCapacity = maxCapacity; this.unitSpace = 1.0 / maxCapacity; this.stock = stock; this.remaining = stock; } // 按空间占用从大到小排序(优先放占用大的) @Override public int compareTo(Item other) { return Double.compare(other.unitSpace, this.unitSpace); } @Override public String toString() { return name + "(单放" + maxCapacity + "个, 库存" + stock + "个)"; } } static class Locker { int id; double remainingSpace; Map<String, Integer> contents; public Locker(int id) { this.id = id; this.remainingSpace = 1.0; this.contents = new HashMap<>(); } // 尝试放入物品 public boolean canAdd(Item item, int quantity) { return remainingSpace >= quantity * item.unitSpace; } // 放入物品 public void addItem(Item item, int quantity) { double spaceUsed = quantity * item.unitSpace; remainingSpace -= spaceUsed; contents.put(item.name, contents.getOrDefault(item.name, 0) + quantity); item.remaining -= quantity; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("储物柜").append(id).append(": "); sb.append(String.format("剩余空间%.4f", remainingSpace)).append("\n"); for (Map.Entry<String, Integer> entry : contents.entrySet()) { sb.append(" ").append(entry.getKey()).append(": ").append(entry.getValue()).append("个\n"); } return sb.toString(); } } static class PackingSolution { List<Locker> lockers; int totalLockersUsed; double overallUtilization; public PackingSolution() { this.lockers = new ArrayList<>(); } public void addLocker(Locker locker) { lockers.add(locker); totalLockersUsed = lockers.size(); } public void calculateOverallUtilization() { double totalUsedSpace = 0; for (Locker locker : lockers) { totalUsedSpace += (1.0 - locker.remainingSpace); } overallUtilization = totalUsedSpace / totalLockersUsed; } @Override public String toString() { calculateOverallUtilization(); StringBuilder sb = new StringBuilder(); sb.append("=== 多储物柜摆放方案 ===\n"); sb.append("使用的储物柜数量: ").append(totalLockersUsed).append("个\n"); sb.append("平均空间利用率: ").append(String.format("%.2f", overallUtilization * 100)).append("%\n\n"); for (Locker locker : lockers) { sb.append(locker.toString()).append("\n"); } return sb.toString(); } } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("请输入物品种类数量:"); int itemTypes = scanner.nextInt(); scanner.nextLine(); List<Item> items = new ArrayList<>(); for (int i = 0; i < itemTypes; i++) { System.out.println("\n请输入第" + (i + 1) + "种物品的信息:"); System.out.print("物品名称: "); String name = scanner.nextLine(); System.out.print("单种存放最大数量: "); int maxCapacity = scanner.nextInt(); System.out.print("库存数量: "); int stock = scanner.nextInt(); scanner.nextLine(); items.add(new Item(name, maxCapacity, stock)); } // 使用最佳适应递减算法 PackingSolution solution = packItems(items); System.out.println("\n" + solution); scanner.close(); } /** * 装箱算法:最佳适应递减(Best Fit Decreasing) * 这是装箱问题的经典启发式算法 */ public static PackingSolution packItems(List<Item> items) { // 复制物品列表,避免修改原始数据 List<Item> itemsToPack = new ArrayList<>(); for (Item item : items) { itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock)); } // 按物品大小从大到小排序(优先处理大物品) Collections.sort(itemsToPack); PackingSolution solution = new PackingSolution(); int lockerId = 1; // 处理每种物品 for (Item item : itemsToPack) { while (item.remaining > 0) { // 寻找最适合的现有储物柜 Locker bestLocker = null; double bestRemaining = Double.MAX_VALUE; for (Locker locker : solution.lockers) { if (locker.canAdd(item, 1) && locker.remainingSpace < bestRemaining) { bestLocker = locker; bestRemaining = locker.remainingSpace; } } // 如果找到合适的储物柜 if (bestLocker != null) { int maxCanAdd = (int) Math.floor(bestLocker.remainingSpace / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); bestLocker.addItem(item, actualAdd); } else { // 创建新储物柜 Locker newLocker = new Locker(lockerId++); int maxCanAdd = (int) Math.floor(1.0 / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); newLocker.addItem(item, actualAdd); solution.addLocker(newLocker); } } } return solution; } /** * 替代算法:首次适应递减(First Fit Decreasing) * 另一种常用的装箱启发式算法 */ public static PackingSolution packItemsFirstFit(List<Item> items) { List<Item> itemsToPack = new ArrayList<>(); for (Item item : items) { itemsToPack.add(new Item(item.name, item.maxCapacity, item.stock)); } Collections.sort(itemsToPack); PackingSolution solution = new PackingSolution(); int lockerId = 1; for (Item item : itemsToPack) { while (item.remaining > 0) { boolean placed = false; // 尝试放入现有储物柜 for (Locker locker : solution.lockers) { if (locker.canAdd(item, 1)) { int maxCanAdd = (int) Math.floor(locker.remainingSpace / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); locker.addItem(item, actualAdd); placed = true; break; } } // 如果没找到合适的储物柜,创建新的 if (!placed) { Locker newLocker = new Locker(lockerId++); int maxCanAdd = (int) Math.floor(1.0 / item.unitSpace); int actualAdd = Math.min(item.remaining, maxCanAdd); newLocker.addItem(item, actualAdd); solution.addLocker(newLocker); } } } return solution; } /** * 测试不同的装箱算法 */ public static void testDifferentAlgorithms() { System.out.println("=== 算法比较测试 ==="); List<Item> testItems = Arrays.asList( new Item("X", 20, 30), new Item("Y", 15, 25), new Item("Z", 34, 40), new Item("T", 25, 15) ); System.out.println("测试物品: " + testItems); // 测试最佳适应算法 PackingSolution bestFitSolution = packItems(testItems); System.out.println("\n最佳适应算法结果:"); System.out.println("使用储物柜: " + bestFitSolution.totalLockersUsed + "个"); // 测试首次适应算法 PackingSolution firstFitSolution = packItemsFirstFit(testItems); System.out.println("首次适应算法结果:"); System.out.println("使用储物柜: " + firstFitSolution.totalLockersUsed + "个"); // 显示最佳算法的详细方案 System.out.println("\n最佳方案详情:"); System.out.println(bestFitSolution); } /** * 验证是否所有物品都已摆放 */ public static boolean validateSolution(List<Item> originalItems, PackingSolution solution) { Map<String, Integer> totalPlaced = new HashMap<>(); for (Locker locker : solution.lockers) { for (Map.Entry<String, Integer> entry : locker.contents.entrySet()) { totalPlaced.put(entry.getKey(), totalPlaced.getOrDefault(entry.getKey(), 0) + entry.getValue()); } } for (Item item : originalItems) { if (totalPlaced.getOrDefault(item.name, 0) != item.stock) { System.out.println("验证失败: " + item.name + " 应放" + item.stock + "个,实放" + totalPlaced.getOrDefault(item.name, 0) + "个"); return false; } } System.out.println("验证成功: 所有物品都已正确摆放"); return true; } } src/main/java/com/zy/asrs/utils/ToSortLineUtils.java
New file @@ -0,0 +1,64 @@ package com.zy.asrs.utils; import com.zy.asrs.entity.OrderDetl; import com.zy.asrs.entity.param.OrderToLine; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; public class ToSortLineUtils { private static final String sign_F = "|F|LABEL_"; public static void main(String[] args) { int itemTypes = 5; List<GroupedLockerOptimizerUtils.Item> items = new ArrayList<>(); for (int i = 0; i < itemTypes; i++) { String name = i+"a"; int maxCapacity = i*10; int stock = i*11; items.add(new GroupedLockerOptimizerUtils.Item(name, maxCapacity, stock)); } // 使用分组优先算法 GroupedLockerOptimizerUtils.PackingSolution solution = GroupedLockerOptimizerUtils.packItemsWithGrouping(items); for (GroupedLockerOptimizerUtils.Locker locker:solution.lockers) { for (String mantnr : locker.contents.keySet()){ System.out.println(mantnr+"<===>"+locker.contents.get(mantnr)); } } System.out.println("\n" + solution); } public static String MergerParameter(String sku,String po,String upc){ return sku+sign_F+po+sign_F+upc; } public static OrderToLine GetOrderToLine(List<GroupedLockerOptimizerUtils.Item> items,OrderToLine orderToLine){ Long bindingTag = System.currentTimeMillis();//混搭标记 List<OrderToLine.MatList> matLists = new ArrayList<>(); // 使用分组优先算法 GroupedLockerOptimizerUtils.PackingSolution solution = GroupedLockerOptimizerUtils.packItemsWithGrouping(items); for (GroupedLockerOptimizerUtils.Locker locker:solution.lockers) { for (String mantnr : locker.contents.keySet()){ System.out.println(mantnr+"<===>"+locker.contents.get(mantnr)); String[] split = mantnr.split(Pattern.quote(sign_F)); OrderToLine.MatList mat = new OrderToLine.MatList( split[0], // matnr -> sku split[1], // supp -> po -> s1 locker.contents.get(mantnr).doubleValue(), //整料 split[2], //barcode -> upc -> s2 1, null, //origin -> supplier 货源 bindingTag ); matLists.add(mat); } bindingTag++; } orderToLine.setMatList(matLists); return orderToLine; } }