package com.zy.asrs.utils;
|
|
import com.core.common.SnowflakeIdWorker;
|
import com.core.common.SpringUtils;
|
|
import java.util.*;
|
|
public class OptimizedLockerPacking3Utils {
|
|
public static class Item {
|
String name;
|
double unitSpace;
|
int maxCapacity;
|
int stock;
|
int allocated; // 改为已分配数量,避免直接修改remaining导致的混乱
|
|
public Item(String name, int maxCapacity, int stock) {
|
this.name = name;
|
this.maxCapacity = maxCapacity;
|
this.unitSpace = 1.0 / maxCapacity;
|
this.stock = stock;
|
this.allocated = 0;
|
}
|
|
public int getRemaining() {
|
return stock - allocated;
|
}
|
|
public boolean allocate(int quantity) {
|
if (allocated + quantity <= stock) {
|
allocated += quantity;
|
return true;
|
}
|
return false;
|
}
|
|
@Override
|
public String toString() {
|
return name + "(单放" + maxCapacity + "个, 库存" + stock + "个)";
|
}
|
}
|
|
static class Locker {
|
int id;
|
double remainingSpace;
|
long bindingTags;
|
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<>();
|
SnowflakeIdWorker snowflakeIdWorker = SpringUtils.getBean(SnowflakeIdWorker.class);
|
this.bindingTags = snowflakeIdWorker.nextId();
|
}
|
|
public boolean canAdd(Item item, int quantity) {
|
return remainingSpace >= quantity * item.unitSpace;
|
}
|
|
public boolean addItem(Item item, int quantity) {
|
// 验证分配数量
|
if (!item.allocate(quantity)) {
|
return false;
|
}
|
|
double spaceUsed = quantity * item.unitSpace;
|
if (spaceUsed > remainingSpace + 1e-6) {
|
// 回滚分配
|
item.allocated -= quantity;
|
return false;
|
}
|
|
remainingSpace -= spaceUsed;
|
contents.put(item.name, contents.getOrDefault(item.name, 0) + quantity);
|
itemTypes.add(item.name);
|
return true;
|
}
|
|
public boolean containsItemType(String itemName) {
|
return itemTypes.contains(itemName);
|
}
|
|
@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;
|
int pureLockers;
|
|
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;
|
pureLockers = 0;
|
String lastItemType = null;
|
|
for (Locker locker : lockers) {
|
totalUsedSpace += (1.0 - locker.remainingSpace);
|
|
if (locker.itemTypes.size() == 1) {
|
pureLockers++;
|
}
|
|
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(pureLockers).append("个\n");
|
sb.append("平均空间利用率: ").append(String.format("%.2f", overallUtilization * 100)).append("%\n");
|
sb.append("物品种类切换次数: ").append(itemTypeChanges).append("次\n\n");
|
|
List<Locker> pureLockersList = new ArrayList<>();
|
List<Locker> mixedLockersList = new ArrayList<>();
|
|
for (Locker locker : lockers) {
|
if (locker.itemTypes.size() == 1) {
|
pureLockersList.add(locker);
|
} else {
|
mixedLockersList.add(locker);
|
}
|
}
|
|
sb.append("【纯种类储物柜】:\n");
|
for (Locker locker : pureLockersList) {
|
sb.append(locker.toString()).append("\n");
|
}
|
|
sb.append("【混合储物柜】:\n");
|
for (Locker locker : mixedLockersList) {
|
sb.append(locker.toString()).append("\n");
|
}
|
|
return sb.toString();
|
}
|
}
|
|
/**
|
* 余料物品辅助类
|
*/
|
private static class RemainderItem {
|
Item item;
|
int quantity;
|
|
RemainderItem(Item item, int quantity) {
|
this.item = item;
|
this.quantity = quantity;
|
}
|
|
@Override
|
public String toString() {
|
return item.name + "(" + quantity + "个)";
|
}
|
|
@Override
|
public boolean equals(Object obj) {
|
if (this == obj) return true;
|
if (obj == null || getClass() != obj.getClass()) return false;
|
RemainderItem that = (RemainderItem) obj;
|
return quantity == that.quantity && item.name.equals(that.item.name);
|
}
|
|
@Override
|
public int hashCode() {
|
return Objects.hash(item.name, quantity);
|
}
|
}
|
|
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 = optimizedPacking(items);
|
System.out.println("\n" + solution);
|
|
scanner.close();
|
}
|
|
/**
|
* 优化装箱算法:修复数量管理问题
|
*/
|
public static PackingSolution optimizedPacking(List<Item> items) {
|
// 验证输入
|
if (items == null || items.isEmpty()) {
|
throw new IllegalArgumentException("物品列表不能为空");
|
}
|
|
// 创建物品副本用于处理
|
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;
|
|
// 第一阶段:对每种物品进行集中处理,分配完整储物柜
|
Map<String, Integer> remainders = new HashMap<>();
|
|
for (Item item : itemsToPack) {
|
if (item.getRemaining() == 0) continue;
|
|
int fullLockersNeeded = item.getRemaining() / item.maxCapacity;
|
int remainder = item.getRemaining() % item.maxCapacity;
|
|
// 分配完整储物柜
|
for (int i = 0; i < fullLockersNeeded; i++) {
|
Locker locker = new Locker(lockerId++);
|
if (locker.addItem(item, item.maxCapacity)) {
|
solution.addLocker(locker);
|
} else {
|
System.out.println("警告: 无法分配完整储物柜给物品 " + item.name);
|
}
|
}
|
|
if (remainder > 0) {
|
remainders.put(item.name, remainder);
|
}
|
}
|
|
// 第二阶段:优化余料分配
|
if (!remainders.isEmpty()) {
|
uniformRemainderProcessing(remainders, itemsToPack, solution, lockerId);
|
}
|
|
// 验证结果
|
if (!validateSolution(items, solution)) {
|
System.out.println("警告: 数量分配验证失败");
|
}
|
|
return solution;
|
}
|
|
/**
|
* 修复后的均匀余料处理算法
|
*/
|
private static void uniformRemainderProcessing(Map<String, Integer> remainders,
|
List<Item> items,
|
PackingSolution solution,
|
int startLockerId) {
|
int lockerId = startLockerId;
|
|
// 将余料转换为可处理的物品列表
|
List<RemainderItem> remainderItems = new ArrayList<>();
|
for (Item item : items) {
|
Integer remainder = remainders.get(item.name);
|
if (remainder != null && remainder > 0 && item.getRemaining() > 0) {
|
remainderItems.add(new RemainderItem(item, Math.min(remainder, item.getRemaining())));
|
}
|
}
|
|
// 按物品单位空间从大到小排序
|
remainderItems.sort((a, b) -> Double.compare(b.item.unitSpace, a.item.unitSpace));
|
|
// 使用修复后的余料处理算法
|
List<Locker> remainderLockers = fixedRemainderArrangement(remainderItems, solution, lockerId);
|
|
// 将最优解添加到解决方案中
|
for (Locker locker : remainderLockers) {
|
solution.addLocker(locker);
|
}
|
}
|
|
/**
|
* 修复后的余料排列方案
|
*/
|
private static List<Locker> fixedRemainderArrangement(List<RemainderItem> remainderItems,
|
PackingSolution solution,
|
int startLockerId) {
|
if (remainderItems.isEmpty()) {
|
return new ArrayList<>();
|
}
|
|
// 创建副本避免修改原始数据
|
List<RemainderItem> remaining = new ArrayList<>();
|
for (RemainderItem ri : remainderItems) {
|
if (ri.quantity > 0) {
|
remaining.add(new RemainderItem(ri.item, ri.quantity));
|
}
|
}
|
|
List<Locker> lockers = new ArrayList<>();
|
int lockerId = startLockerId;
|
|
// 主要策略:创建高利用率储物柜
|
while (!remaining.isEmpty()) {
|
Locker locker = createOptimizedLocker(remaining, lockerId);
|
if (locker != null) {
|
lockers.add(locker);
|
lockerId++;
|
|
// 移除已处理完的物品
|
Iterator<RemainderItem> iterator = remaining.iterator();
|
while (iterator.hasNext()) {
|
RemainderItem ri = iterator.next();
|
if (ri.quantity == 0) {
|
iterator.remove();
|
}
|
}
|
|
double utilization = (1.0 - locker.remainingSpace) * 100;
|
System.out.println("创建储物柜,利用率: " + String.format("%.2f", utilization) + "%");
|
} else {
|
break;
|
}
|
}
|
|
// 处理剩余物品(如果有)
|
if (!remaining.isEmpty()) {
|
System.out.println("使用标准算法处理剩余 " + remaining.size() + " 种物品");
|
List<Locker> standardLockers = standardBestFit(remaining, lockerId);
|
lockers.addAll(standardLockers);
|
}
|
|
return lockers;
|
}
|
|
/**
|
* 创建优化储物柜(修复数量验证)
|
*/
|
private static Locker createOptimizedLocker(List<RemainderItem> remaining, int lockerId) {
|
if (remaining.isEmpty()) {
|
return null;
|
}
|
|
Locker locker = new Locker(lockerId);
|
double targetUtilization = 0.85;
|
double minUtilization = 0.3; // 最低利用率阈值
|
|
// 寻找最佳组合
|
List<RemainderItem> bestCombination = findValidCombination(remaining, targetUtilization);
|
|
if (bestCombination.isEmpty()) {
|
// 尝试达到最低利用率
|
bestCombination = findMinUtilizationCombination(remaining, minUtilization);
|
}
|
|
if (bestCombination.isEmpty()) {
|
return null;
|
}
|
|
// 分配组合到储物柜
|
boolean success = allocateValidCombination(remaining, locker, bestCombination);
|
|
if (!success || locker.contents.isEmpty()) {
|
return null;
|
}
|
|
// 检查利用率是否可接受
|
double utilization = 1.0 - locker.remainingSpace;
|
if (utilization < minUtilization) {
|
// 回滚分配
|
rollbackAllocation(remaining, locker);
|
return null;
|
}
|
|
return locker;
|
}
|
|
/**
|
* 寻找有效组合(修复数量验证)
|
*/
|
private static List<RemainderItem> findValidCombination(List<RemainderItem> items, double targetUtilization) {
|
List<RemainderItem> bestCombination = new ArrayList<>();
|
double[] bestDiff = {Double.MAX_VALUE};
|
|
// 过滤有效物品
|
List<RemainderItem> availableItems = new ArrayList<>();
|
for (RemainderItem ri : items) {
|
if (ri.quantity > 0 && ri.item.getRemaining() > 0) {
|
availableItems.add(ri);
|
}
|
}
|
|
if (availableItems.isEmpty()) {
|
return bestCombination;
|
}
|
|
// 使用DFS搜索有效组合
|
validDfsCombination(availableItems, 0, 0.0, targetUtilization,
|
new ArrayList<>(), bestCombination, bestDiff);
|
|
return bestCombination;
|
}
|
|
/**
|
* 有效的DFS组合搜索
|
*/
|
private static void validDfsCombination(List<RemainderItem> items, int index, double currentSpace,
|
double targetUtilization,
|
List<RemainderItem> current,
|
List<RemainderItem> bestCombination, double[] bestDiff) {
|
// 检查当前组合的有效性
|
if (!current.isEmpty()) {
|
double diff = Math.abs(currentSpace - targetUtilization);
|
if (currentSpace <= 1.0 + 1e-6 && diff < bestDiff[0] && isCombinationValid(current)) {
|
bestCombination.clear();
|
for (RemainderItem ri : current) {
|
bestCombination.add(new RemainderItem(ri.item, ri.quantity));
|
}
|
bestDiff[0] = diff;
|
|
if (diff < 0.01) {
|
return;
|
}
|
}
|
}
|
|
if (index >= items.size() || currentSpace > 1.0 + 1e-6) {
|
return;
|
}
|
|
RemainderItem currentItem = items.get(index);
|
|
// 不选择当前物品
|
validDfsCombination(items, index + 1, currentSpace, targetUtilization,
|
current, bestCombination, bestDiff);
|
|
// 选择当前物品
|
if (currentItem.quantity > 0) {
|
int maxCanAdd = Math.min(currentItem.quantity,
|
(int) Math.floor((1.0 - currentSpace) / currentItem.item.unitSpace));
|
maxCanAdd = Math.min(maxCanAdd, currentItem.item.getRemaining()); // 重要修复:不超过剩余数量
|
|
for (int qty = 1; qty <= maxCanAdd; qty++) {
|
current.add(new RemainderItem(currentItem.item, qty));
|
validDfsCombination(items, index + 1, currentSpace + qty * currentItem.item.unitSpace,
|
targetUtilization, current, bestCombination, bestDiff);
|
current.remove(current.size() - 1);
|
}
|
}
|
}
|
|
/**
|
* 检查组合是否有效(修复数量验证)
|
*/
|
private static boolean isCombinationValid(List<RemainderItem> combination) {
|
Map<String, Integer> totalQuantities = new HashMap<>();
|
|
for (RemainderItem ri : combination) {
|
int currentTotal = totalQuantities.getOrDefault(ri.item.name, 0);
|
totalQuantities.put(ri.item.name, currentTotal + ri.quantity);
|
|
// 检查是否超过物品剩余数量
|
if (currentTotal + ri.quantity > getRemainingQuantity(ri.item)) {
|
return false;
|
}
|
}
|
|
return true;
|
}
|
|
/**
|
* 获取物品剩余数量
|
*/
|
private static int getRemainingQuantity(Item item) {
|
return item.stock - item.allocated;
|
}
|
|
/**
|
* 寻找达到最低利用率的组合
|
*/
|
private static List<RemainderItem> findMinUtilizationCombination(List<RemainderItem> items, double minUtilization) {
|
List<RemainderItem> combination = new ArrayList<>();
|
double currentSpace = 0.0;
|
|
// 按单位空间排序
|
List<RemainderItem> sortedItems = new ArrayList<>(items);
|
sortedItems.sort((a, b) -> Double.compare(b.item.unitSpace, a.item.unitSpace));
|
|
for (RemainderItem ri : sortedItems) {
|
if (ri.quantity > 0 && currentSpace < minUtilization) {
|
int maxCanAdd = Math.min(ri.quantity,
|
(int) Math.floor((1.0 - currentSpace) / ri.item.unitSpace));
|
maxCanAdd = Math.min(maxCanAdd, ri.item.getRemaining()); // 重要修复
|
|
if (maxCanAdd > 0) {
|
combination.add(new RemainderItem(ri.item, maxCanAdd));
|
currentSpace += maxCanAdd * ri.item.unitSpace;
|
}
|
}
|
}
|
|
if (currentSpace >= minUtilization) {
|
return combination;
|
}
|
|
return new ArrayList<>();
|
}
|
|
/**
|
* 分配有效组合到储物柜(修复数量验证)
|
*/
|
private static boolean allocateValidCombination(List<RemainderItem> remaining, Locker locker,
|
List<RemainderItem> combination) {
|
// 验证组合中的所有物品都有足够的数量
|
for (RemainderItem combItem : combination) {
|
boolean found = false;
|
for (RemainderItem remItem : remaining) {
|
if (remItem.item.name.equals(combItem.item.name)) {
|
found = true;
|
if (combItem.quantity > remItem.quantity) {
|
return false;
|
}
|
if (combItem.quantity > remItem.item.getRemaining()) {
|
return false;
|
}
|
break;
|
}
|
}
|
if (!found) {
|
return false;
|
}
|
}
|
|
// 实际分配
|
for (RemainderItem combItem : combination) {
|
for (RemainderItem remItem : remaining) {
|
if (remItem.item.name.equals(combItem.item.name)) {
|
if (locker.addItem(combItem.item, combItem.quantity)) {
|
remItem.quantity -= combItem.quantity;
|
} else {
|
return false;
|
}
|
break;
|
}
|
}
|
}
|
|
return true;
|
}
|
|
/**
|
* 回滚分配
|
*/
|
private static void rollbackAllocation(List<RemainderItem> remaining, Locker locker) {
|
for (Map.Entry<String, Integer> entry : locker.contents.entrySet()) {
|
String itemName = entry.getKey();
|
int quantity = entry.getValue();
|
|
for (RemainderItem ri : remaining) {
|
if (ri.item.name.equals(itemName)) {
|
ri.quantity += quantity;
|
ri.item.allocated -= quantity; // 回滚分配数量
|
break;
|
}
|
}
|
}
|
}
|
|
/**
|
* 标准最佳适应算法
|
*/
|
private static List<Locker> standardBestFit(List<RemainderItem> remainderItems, int startLockerId) {
|
List<Locker> lockers = new ArrayList<>();
|
int lockerId = startLockerId;
|
|
// 创建副本
|
List<RemainderItem> remaining = new ArrayList<>();
|
for (RemainderItem ri : remainderItems) {
|
if (ri.quantity > 0) {
|
remaining.add(new RemainderItem(ri.item, ri.quantity));
|
}
|
}
|
|
// 按单位空间排序
|
remaining.sort((a, b) -> Double.compare(b.item.unitSpace, a.item.unitSpace));
|
|
for (RemainderItem ri : remaining) {
|
while (ri.quantity > 0 && ri.item.getRemaining() > 0) {
|
Locker bestLocker = null;
|
double bestRemainingSpace = Double.MAX_VALUE;
|
|
// 寻找最佳储物柜
|
for (Locker locker : lockers) {
|
if (locker.canAdd(ri.item, 1) && locker.remainingSpace < bestRemainingSpace) {
|
bestLocker = locker;
|
bestRemainingSpace = locker.remainingSpace;
|
}
|
}
|
|
if (bestLocker != null) {
|
int maxCanAdd = (int) Math.floor(bestLocker.remainingSpace / ri.item.unitSpace);
|
int actualAdd = Math.min(ri.quantity, maxCanAdd);
|
actualAdd = Math.min(actualAdd, ri.item.getRemaining()); // 重要修复
|
|
if (actualAdd > 0 && bestLocker.addItem(ri.item, actualAdd)) {
|
ri.quantity -= actualAdd;
|
} else {
|
break;
|
}
|
} else {
|
// 创建新储物柜
|
Locker newLocker = new Locker(lockerId++);
|
int maxCanAdd = (int) Math.floor(1.0 / ri.item.unitSpace);
|
int actualAdd = Math.min(ri.quantity, maxCanAdd);
|
actualAdd = Math.min(actualAdd, ri.item.getRemaining()); // 重要修复
|
|
if (actualAdd > 0 && newLocker.addItem(ri.item, actualAdd)) {
|
lockers.add(newLocker);
|
ri.quantity -= actualAdd;
|
} else {
|
break;
|
}
|
}
|
}
|
}
|
|
return lockers;
|
}
|
|
/**
|
* 验证解决方案(修复验证逻辑)
|
*/
|
private static boolean validateSolution(List<Item> originalItems, PackingSolution solution) {
|
Map<String, Integer> allocatedQuantities = new HashMap<>();
|
Map<String, Integer> originalQuantities = new HashMap<>();
|
|
// 记录原始数量
|
for (Item item : originalItems) {
|
originalQuantities.put(item.name, item.stock);
|
}
|
|
// 计算分配数量
|
for (Locker locker : solution.lockers) {
|
for (Map.Entry<String, Integer> entry : locker.contents.entrySet()) {
|
String itemName = entry.getKey();
|
int quantity = entry.getValue();
|
allocatedQuantities.put(itemName, allocatedQuantities.getOrDefault(itemName, 0) + quantity);
|
}
|
}
|
|
boolean valid = true;
|
for (Map.Entry<String, Integer> entry : originalQuantities.entrySet()) {
|
String itemName = entry.getKey();
|
int originalQty = entry.getValue();
|
int allocatedQty = allocatedQuantities.getOrDefault(itemName, 0);
|
|
if (originalQty != allocatedQty) {
|
System.out.println("验证失败: " + itemName + " 原始数量=" + originalQty +
|
", 分配数量=" + allocatedQty);
|
valid = false;
|
}
|
}
|
|
if (valid) {
|
System.out.println("数量验证通过");
|
}
|
|
return valid;
|
}
|
|
/**
|
* 性能测试和比较
|
*/
|
public static void performanceTest() {
|
System.out.println("=== 性能测试 ===");
|
|
// 测试数据
|
List<Item> testItems = Arrays.asList(
|
new Item("X", 20, 87),
|
new Item("Y", 15, 63),
|
new Item("Z", 34, 125),
|
new Item("T", 25, 48),
|
new Item("U", 40, 92)
|
);
|
|
System.out.println("测试物品数量: " + testItems.size());
|
int totalItems = testItems.stream().mapToInt(item -> item.stock).sum();
|
System.out.println("总物品数量: " + totalItems);
|
|
long startTime = System.currentTimeMillis();
|
PackingSolution solution = optimizedPacking(testItems);
|
long endTime = System.currentTimeMillis();
|
|
System.out.println("\n优化算法结果:");
|
System.out.println("处理时间: " + (endTime - startTime) + "ms");
|
System.out.println("储物柜数量: " + solution.totalLockersUsed);
|
System.out.println("纯种类储物柜: " + solution.pureLockers);
|
System.out.println("空间利用率: " + String.format("%.2f", solution.overallUtilization * 100) + "%");
|
}
|
}
|