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 contents; Set 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 entry : contents.entrySet()) { sb.append(" ").append(entry.getKey()).append(": ").append(entry.getValue()).append("个\n"); } return sb.toString(); } } static class PackingSolution { List 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 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 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 items) { List 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 items) { List 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 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 items) { List 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 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 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(); } } }