package org.springblade.schedulemanagement.utils;
import org.apache.commons.collections4.CollectionUtils;
import org.springblade.scheduleManagementservice.entity.SteelOrder;
import org.springblade.scheduleManagementservice.entity.CuttingSchedulePlan;
import org.springblade.scheduleManagementservice.entity.CuttingSchedulePlanRef;
import org.springblade.scheduleManagementservice.vo.CuttingPatternVO;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class CuttingPatternByUtils {
private static final int MAX_TYPES_PER_CUT = 3; // 每种方案最多3种类型
private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP;
private static final int MAX_CUTTING_ORDERS = 3; // 最大切割状态订单数
private static final int BATCH_SIZE = 10; // 每批次处理的订单数量
public static void main(String[] args) {
Map<String, SteelOrder> orders = new LinkedHashMap<>();
orders.put("X19260061", new SteelOrder("X19260061", new BigDecimal("474.5"), 102, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X19260021", new SteelOrder("X19260021", new BigDecimal("407"), 200, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X25250841", new SteelOrder("X25250841", new BigDecimal("396"), 345, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X20200921", new SteelOrder("X20200921", new BigDecimal("438.5"), 243, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X20201321", new SteelOrder("X20201321", new BigDecimal("538"), 202, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X26264181", new SteelOrder("X26264181", new BigDecimal("487"), 345, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X20200861", new SteelOrder("X20200861", new BigDecimal("474.5"), 102, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X20201061", new SteelOrder("X20201061", new BigDecimal("407"), 200, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X18180191", new SteelOrder("X18180191", new BigDecimal("396"), 345, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X20200811", new SteelOrder("X20200811", new BigDecimal("438.5"), 243, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X20201021", new SteelOrder("X20201021", new BigDecimal("538"), 202, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X19190071", new SteelOrder("X19190071", new BigDecimal("487"), 345, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X19192041", new SteelOrder("X19192041", new BigDecimal("474.5"), 102, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X20201011", new SteelOrder("X20201011", new BigDecimal("407"), 200, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X20190121", new SteelOrder("X20190121", new BigDecimal("396"), 345, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X20201761", new SteelOrder("X20201761", new BigDecimal("438.5"), 243, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X20201001", new SteelOrder("X20201001", new BigDecimal("538"), 202, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X19191791", new SteelOrder("X19191791", new BigDecimal("487"), 345, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X20202141", new SteelOrder("X20202141", new BigDecimal("474.5"), 102, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X20200891", new SteelOrder("X20200891", new BigDecimal("407"), 200, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
orders.put("X19260031", new SteelOrder("X19260031", new BigDecimal("396"), 345, "", "精拉料/GA2040222/Φ22/40Cr", "22"));
List<CuttingPatternVO> cuttingPattern = getCuttingPattern(new BigDecimal("6000"), new BigDecimal("3"),
new BigDecimal("0"), 0,
orders);
System.out.println("----------------------------------------");
}
public static List<CuttingPatternVO> getCuttingPattern(BigDecimal STANDARD_RAW_MATERIAL_LENGTH, BigDecimal CUTTING_LOSS,
BigDecimal REMAINING_RAW_MATERIAL_LENGTH, int MAX_REMAINING_MATERIALS,
Map<String, org.springblade.scheduleManagementservice.entity.SteelOrder> orders) {
List<CuttingPatternVO> cuttingPatternVOS = new ArrayList<>();
List<SteelOrder> remainingOrders = new ArrayList<>(orders.values()); // 复制原始订单列表
List<SteelOrder> matchedOrders = new ArrayList<>();// 存储匹配到的订单
//剩余余料
int remainMaterials = MAX_REMAINING_MATERIALS;
while (!remainingOrders.isEmpty()) {
// 取出当前批次的订单(10个或剩余的所有订单)
int batchSize = Math.min(BATCH_SIZE, remainingOrders.size());
List<SteelOrder> currentBatch = new ArrayList<>(remainingOrders.subList(0, batchSize));
remainingOrders = remainingOrders.subList(batchSize, remainingOrders.size()); // 更新剩余订单列表
// 将之前匹配的订单添加到当前批次的最前面
if (!matchedOrders.isEmpty()) {
currentBatch.addAll(0, matchedOrders);
matchedOrders.clear();
}
// Map<String, SteelOrder> maps2 = currentBatch.stream().collect
//
// (Collectors.toMap(SteelOrder::getType, Function.identity()));
//
Map<String, SteelOrder> maps2 = currentBatch.stream()
.collect(Collectors.toMap(
SteelOrder::getType, // key生成器
Function.identity(), // value生成器
(existing, replacement) -> existing, // 解决key冲突的策略(保留原有值)
LinkedHashMap::new // 指定Map实现类为LinkedHashMap,保持插入顺序
));
// 处理当前批次,生成切割方案
List<CuttingPatternVO> batchPatterns = processBatch(STANDARD_RAW_MATERIAL_LENGTH, CUTTING_LOSS,
REMAINING_RAW_MATERIAL_LENGTH, remainMaterials, maps2);
// 检查每个切割方案的废料,尝试从剩余订单中匹配
for (CuttingPatternVO pattern : batchPatterns) {
BigDecimal remainder = pattern.getCuttingSchedulePlan().getWasteLength();
if (remainder.compareTo(BigDecimal.ZERO) > 0) {
// 从剩余订单中查找可匹配的订单
if (CollectionUtils.isNotEmpty(matchedOrders)) {
for (Iterator<SteelOrder> it = matchedOrders.iterator(); it.hasNext(); ) {
SteelOrder order = it.next();
if (order.getQuantity() > 0 &&
order.getLength().add(CUTTING_LOSS).compareTo(remainder) <= 0) {
// 计算可以添加的数量
int addableQuantity = Math.min(
order.getQuantity(),
remainder.divide(order.getLength().add(CUTTING_LOSS), 0, RoundingMode.FLOOR).intValue()
);
if (addableQuantity > 0) {
// 更新切割方案
CuttingSchedulePlanRef cuttingSchedulePlanRef = new CuttingSchedulePlanRef();
cuttingSchedulePlanRef.setModel(order.getType());
cuttingSchedulePlanRef.setSingleNum(addableQuantity);
cuttingSchedulePlanRef.setLength(order.getLength());
cuttingSchedulePlanRef.setTotalNum(pattern.getCuttingSchedulePlan().getNum() * addableQuantity);
List<CuttingSchedulePlanRef> cuttingSchedulePlanRefs = pattern.getCuttingSchedulePlanRefs();
cuttingSchedulePlanRefs.add(cuttingSchedulePlanRef);
pattern.setCuttingSchedulePlanRefs(cuttingSchedulePlanRefs);
// 更新剩余废料
remainder = remainder.subtract(
order.getLength().multiply(BigDecimal.valueOf(addableQuantity))
.add(CUTTING_LOSS.multiply(BigDecimal.valueOf(addableQuantity)))
);
pattern.getCuttingSchedulePlan().setWasteLength(remainder);
// 更新订单数量
order.setQuantity(order.getQuantity() - pattern.getCuttingSchedulePlan().getNum() * addableQuantity);
if (order.getQuantity() == 0) {
remainingOrders.remove(order);
}
// 如果废料已用完,退出循环
if (remainder.compareTo(BigDecimal.ZERO) <= 0) {
break;
}
}
}
}
} else {
for (Iterator<SteelOrder> it = remainingOrders.iterator(); it.hasNext(); ) {
SteelOrder order = it.next();
if (order.getQuantity() > 0 &&
order.getLength().add(CUTTING_LOSS).compareTo(remainder) <= 0) {
// 计算可以添加的数量
int addableQuantity = Math.min(
order.getQuantity(),
remainder.divide(order.getLength().add(CUTTING_LOSS), 0, RoundingMode.FLOOR).intValue()
);
if (addableQuantity > 0) {
// 更新切割方案
CuttingSchedulePlanRef cuttingSchedulePlanRef = new CuttingSchedulePlanRef();
cuttingSchedulePlanRef.setModel(order.getType());
cuttingSchedulePlanRef.setSingleNum(addableQuantity);
cuttingSchedulePlanRef.setLength(order.getLength());
cuttingSchedulePlanRef.setTotalNum(pattern.getCuttingSchedulePlan().getNum() * addableQuantity);
List<CuttingSchedulePlanRef> cuttingSchedulePlanRefs = pattern.getCuttingSchedulePlanRefs();
cuttingSchedulePlanRefs.add(cuttingSchedulePlanRef);
pattern.setCuttingSchedulePlanRefs(cuttingSchedulePlanRefs);
// 更新剩余废料
remainder = remainder.subtract(
order.getLength().multiply(BigDecimal.valueOf(addableQuantity))
.add(CUTTING_LOSS.multiply(BigDecimal.valueOf(addableQuantity)))
);
pattern.getCuttingSchedulePlan().setWasteLength(remainder);
// 更新订单数量
order.setQuantity(order.getQuantity() - pattern.getCuttingSchedulePlan().getNum() * addableQuantity);
// 记录匹配到的订单
if (order.getQuantity() > 0) {
matchedOrders.add(order);
}
// 从剩余订单列表中移除(无论数量是否为0)
it.remove();
// 如果废料已用完,退出循环
if (remainder.compareTo(BigDecimal.ZERO) <= 0) {
break;
}
}
}
}
}
}
if (pattern.getCuttingSchedulePlan().getMaterialType().equals("余料")) {
remainMaterials = remainMaterials - pattern.getCuttingSchedulePlan().getNum();
}
}
cuttingPatternVOS.addAll(batchPatterns);
// 将匹配的订单添加到剩余订单列表的最前面
if (!matchedOrders.isEmpty()) {
remainingOrders.addAll(0, matchedOrders);
matchedOrders.clear();
}
}
return cuttingPatternVOS;
}
private static List<CuttingPatternVO> processBatch(BigDecimal STANDARD_RAW_MATERIAL_LENGTH, BigDecimal CUTTING_LOSS,
BigDecimal REMAINING_RAW_MATERIAL_LENGTH, int MAX_REMAINING_MATERIALS,
Map<String, org.springblade.scheduleManagementservice.entity.SteelOrder> orders) {
List<CuttingPatternVO> cuttingPatternVOS = new ArrayList<>();
Map<String, Integer> completionPatterns = new HashMap<>();
// 生成所有可能的切割方案(针对两种原材料长度)
List<CuttingPattern> remainingPatterns = generateAllPatterns(orders, REMAINING_RAW_MATERIAL_LENGTH, CUTTING_LOSS, STANDARD_RAW_MATERIAL_LENGTH);
List<CuttingPattern> standardPatterns = generateAllPatterns(orders, STANDARD_RAW_MATERIAL_LENGTH, CUTTING_LOSS, STANDARD_RAW_MATERIAL_LENGTH);
// 优先使用剩余原材料,再使用标准原材料
List<UsedPattern> usedPatterns = solveByPriorityGreedy(
remainingPatterns, standardPatterns, orders, completionPatterns, MAX_REMAINING_MATERIALS, STANDARD_RAW_MATERIAL_LENGTH,
REMAINING_RAW_MATERIAL_LENGTH, CUTTING_LOSS);
// 输出结果
printResults(usedPatterns, orders, completionPatterns, cuttingPatternVOS);
// 释放不再使用的对象
remainingPatterns = null;
standardPatterns = null;
usedPatterns = null;
completionPatterns = null;
return cuttingPatternVOS;
}
// 生成所有可能的切割方案,考虑切割损耗
private static List<CuttingPattern> generateAllPatterns(Map<String, SteelOrder> orders, BigDecimal materialLength,
BigDecimal CUTTING_LOSS, BigDecimal STANDARD_RAW_MATERIAL_LENGTH) {
List<CuttingPattern> patterns = new ArrayList<>();
List<String> keys = new ArrayList<>(orders.keySet());
for (int r = 1; r <= MAX_TYPES_PER_CUT; r++) {
generateCombinations(keys, r, combination -> {
// 提前剪枝:计算组合的最小总长度,如果超过原材料长度,直接跳过
BigDecimal minTotalLength = calculateMinTotalLength(combination, orders, CUTTING_LOSS);
if (minTotalLength.compareTo(materialLength) > 0) {
return;
}
if (!materialLength.equals(STANDARD_RAW_MATERIAL_LENGTH)) {
// 处理剩余原材料,检查减去切割损耗后是否足够
boolean canUse = false;
for (String key : combination) {
SteelOrder order = orders.get(key);
if (materialLength.subtract(CUTTING_LOSS).compareTo(order.length) >= 0) {
canUse = true;
break;
}
}
if (canUse) {
generatePatternsForCombination(combination, orders, patterns, materialLength, CUTTING_LOSS, STANDARD_RAW_MATERIAL_LENGTH);
}
} else {
// 处理标准原材料,正常生成方案
generatePatternsForCombination(combination, orders, patterns, materialLength, CUTTING_LOSS, STANDARD_RAW_MATERIAL_LENGTH);
}
});
}
// 新增:按废料量升序,再按利用率((材料长度-废料)/材料长度)降序排序
patterns.sort(Comparator.comparing((CuttingPattern p) -> p.remainder)
.thenComparing(p -> {
BigDecimal usedLength = materialLength.subtract(p.remainder);
return usedLength.divide(materialLength, 4, ROUNDING_MODE).negate(); // 利用率高的排前面
}));
return patterns;
}
// 计算组合的最小总长度
private static BigDecimal calculateMinTotalLength(List<String> combination, Map<String, SteelOrder> orders, BigDecimal CUTTING_LOSS) {
// BigDecimal minTotalLength = BigDecimal.ZERO;
// for (String key : combination) {
// SteelOrder order = orders.get(key);
// minTotalLength = minTotalLength.add(order.length);
// }
// if (combination.size() > 0) {
// minTotalLength = minTotalLength.add(CUTTING_LOSS.multiply(BigDecimal.valueOf(combination.size() - 1)));
// }
// return minTotalLength;
BigDecimal minTotalLength = BigDecimal.ZERO;
for (String key : combination) {
SteelOrder order = orders.get(key);
minTotalLength = minTotalLength.add(order.length);
}
// 原逻辑:仅计算1次切割损耗(组合大小-1)
// 优化:考虑最少1段的情况(避免组合中单个长零件被误判)
int cutTimes = Math.max(combination.size() - 1, 1);
minTotalLength = minTotalLength.add(CUTTING_LOSS.multiply(BigDecimal.valueOf(cutTimes)));
return minTotalLength;
}
// 生成指定组合的所有可能切割方案,考虑切割损耗
private static void generatePatternsForCombination(
List<String> combination,
Map<String, SteelOrder> orders,
List<CuttingPattern> patterns,
BigDecimal materialLength, BigDecimal CUTTING_LOSS, BigDecimal STANDARD_RAW_MATERIAL_LENGTH) {
List<BigDecimal> lengths = new ArrayList<>();
for (String key : combination) {
lengths.add(orders.get(key).length);
}
// 计算每种类型的最大可能数量,考虑切割损耗
List<Integer> maxCounts = new ArrayList<>();
for (BigDecimal length : lengths) {
int maxCount = 0;
BigDecimal totalLength = BigDecimal.ZERO;
while (true) {
maxCount++;
// 总长度 = 钢材长度 * 数量 + 切割损耗 * (数量 - 1)
totalLength = length.multiply(BigDecimal.valueOf(maxCount))
.add(CUTTING_LOSS.multiply(BigDecimal.valueOf(maxCount - 1)));
if (totalLength.compareTo(materialLength) > 0) {
maxCount--;
break;
}
}
maxCounts.add(maxCount);
}
// 生成所有可能的数量组合
List<int[]> countCombinations = new ArrayList<>();
generateCountCombinations(maxCounts, countCombinations);
// 检查每个组合的总长度是否不超过原材料长度,考虑切割损耗
for (int[] counts : countCombinations) {
int totalPieces = 0;
for (int count : counts) {
totalPieces += count;
}
if (totalPieces == 0) continue; // 跳过没有钢材的组合
BigDecimal totalLength = BigDecimal.ZERO;
for (int i = 0; i < counts.length; i++) {
totalLength = totalLength.add(lengths.get(i).multiply(BigDecimal.valueOf(counts[i])));
}
// 添加切割损耗
totalLength = totalLength.add(CUTTING_LOSS.multiply(BigDecimal.valueOf(totalPieces - 1)));
if (totalLength.compareTo(materialLength) <= 0) {
Map<String, Integer> patternMap = new LinkedHashMap<>();
for (int i = 0; i < combination.size(); i++) {
if (counts[i] > 0) {
patternMap.put(combination.get(i), counts[i]);
}
}
patterns.add(new CuttingPattern(
patternMap,
materialLength.subtract(totalLength),
materialLength.equals(STANDARD_RAW_MATERIAL_LENGTH)));
}
}
}
// 生成所有可能的数量组合
private static void generateCountCombinations(List<Integer> maxCounts, List<int[]> result) {
int[] current = new int[maxCounts.size()];
generateCountCombinationsRecursive(maxCounts, current, 0, result);
}
private static void generateCountCombinationsRecursive(
List<Integer> maxCounts, int[] current, int index, List<int[]> result) {
if (index == maxCounts.size()) {
boolean hasPieces = false;
for (int count : current) {
if (count > 0) {
hasPieces = true;
break;
}
}
if (hasPieces) {
result.add(Arrays.copyOf(current, current.length));
}
return;
}
// for (int i = 0; i <= maxCounts.get(index); i++) {
// current[index] = i;
// generateCountCombinationsRecursive(maxCounts, current, index + 1, result);
// }
// 原有是从0到max,改为从max到0,优先生成大数量组合
for (int i = maxCounts.get(index); i >= 0; i--) {
current[index] = i;
generateCountCombinationsRecursive(maxCounts, current, index + 1, result);
}
}
// 生成组合的辅助方法
private static void generateCombinations(List<String> list, int r, CombinationCallback callback) {
if (r == 0) {
callback.process(Collections.emptyList()); // 生成空组合
return;
}
if (list.size() < r) {
// 无法生成组合,直接返回或抛出异常
return;
}
int[] indices = new int[r];
for (int i = 0; i < r; i++) {
indices[i] = i;
}
callback.process(getCombination(list, indices));
while (true) {
int i = r - 1;
while (i >= 0 && indices[i] == list.size() - r + i) {
i--;
}
if (i < 0) {
break;
}
indices[i]++;
for (int j = i + 1; j < r; j++) {
indices[j] = indices[j - 1] + 1;
}
callback.process(getCombination(list, indices));
}
}
private static List<String> getCombination(List<String> list, int[] indices) {
List<String> combination = new ArrayList<>();
for (int index : indices) {
if (index >= 0 && index < list.size()) {
combination.add(list.get(index));
}
}
return combination;
}
// 优先级贪心算法求解,优先处理已在切割状态的订单,优先使用剩余原材料
private static List<UsedPattern> solveByPriorityGreedy(
List<CuttingPattern> remainingPatterns,
List<CuttingPattern> standardPatterns,
Map<String, SteelOrder> orders,
Map<String, Integer> completionPatterns, int MAX_REMAINING_MATERIALS, BigDecimal STANDARD_RAW_MATERIAL_LENGTH,
BigDecimal REMAINING_RAW_MATERIAL_LENGTH, BigDecimal CUTTING_LOSS) {
// 检查剩余原材料长度减去切割损耗后是否比所有订单长度都小
boolean canUseRemaining = false;
for (SteelOrder order : orders.values()) {
if (REMAINING_RAW_MATERIAL_LENGTH.subtract(CUTTING_LOSS).compareTo(order.length) >= 0) {
canUseRemaining = true;
break;
}
}
if (!canUseRemaining) {
MAX_REMAINING_MATERIALS = 0; // 不能使用剩余原材料,将剩余原材料数量置为0
}
// 复制订单数据以便修改
Map<String, SteelOrder> remainingOrders = new LinkedHashMap<>();
for (Map.Entry<String, SteelOrder> entry : orders.entrySet()) {
remainingOrders.put(entry.getKey(), new SteelOrder(
entry.getValue().type,
entry.getValue().length,
entry.getValue().quantity,
entry.getValue().materialType,
entry.getValue().description,
entry.getValue().specifications));
}
// 记录当前处于切割状态的订单
Set<String> cuttingOrders = new HashSet<>();
List<UsedPattern> usedPatterns = new ArrayList<>();
int remainingMaterialsUsed = 0; // 已使用的剩余原材料数量
// int standardMaterialsUsed = 0; // 已使用的标准原材料数量
// 按余料升序排序,余料相同则按类型数降序排序
List<CuttingPattern> allPatterns = new ArrayList<>();
allPatterns.addAll(remainingPatterns);
allPatterns.addAll(standardPatterns);
// allPatterns.sort(Comparator.comparing((CuttingPattern p) -> p.remainder)
// .thenComparing(p -> -calculateTotalPieces(p.quantities)));
// 原有排序逻辑增强
allPatterns.sort(Comparator.comparing((CuttingPattern p) -> p.remainder)
.thenComparing(p -> -calculateTotalPieces(p.quantities))
// 新增:优先包含小长度订单的方案(小长度更易填充余料)
.thenComparing(p -> {
BigDecimal minLength = BigDecimal.valueOf(Integer.MAX_VALUE);
for (String type : p.quantities.keySet()) {
BigDecimal len = orders.get(type).length;
if (len.compareTo(minLength) < 0) {
minLength = len;
}
}
return minLength; // 小长度在前
}));
int currentPatternNumber = 0;
while (hasRemainingOrders(remainingOrders)) {
currentPatternNumber++;
boolean patternUsed = false;
// 尝试优先使用剩余原材料的模式
for (CuttingPattern pattern : allPatterns) {
// 如果是标准原材料模式,但剩余原材料还没用完,则跳过
if (pattern.isStandard && remainingMaterialsUsed < MAX_REMAINING_MATERIALS) {
continue;
}
// 检查该模式是否包含任何已在切割状态的订单
boolean containsCuttingOrder = false;
for (String type : pattern.quantities.keySet()) {
if (cuttingOrders.contains(type)) {
containsCuttingOrder = true;
break;
}
}
// 如果模式不包含任何已在切割状态的订单,且切割状态订单数已满,则跳过
if (!containsCuttingOrder && cuttingOrders.size() >= MAX_CUTTING_ORDERS) {
continue;
}
// 检查该模式是否还能满足任何剩余需求
boolean canUse = false;
for (Map.Entry<String, Integer> entry : pattern.quantities.entrySet()) {
String type = entry.getKey();
int needed = remainingOrders.get(type).quantity;
if (needed > 0 && entry.getValue() > 0) {
canUse = true;
break;
}
}
if (!canUse) {
continue;
}
// 计算该模式可以使用的最大次数
int maxUses = Integer.MAX_VALUE;
for (Map.Entry<String, Integer> entry : pattern.quantities.entrySet()) {
String type = entry.getKey();
int available = remainingOrders.get(type).quantity;
int neededPerUse = entry.getValue();
if (neededPerUse > 0) {
int possibleUses = available / neededPerUse;
if (possibleUses < maxUses) {
maxUses = possibleUses;
}
}
}
// 限制使用次数,避免超出原材料数量
if (pattern.isStandard) {
maxUses = Math.min(maxUses, Integer.MAX_VALUE); // 标准原材料无数量限制
} else {
int remainingMaterialsLeft = MAX_REMAINING_MATERIALS - remainingMaterialsUsed;
maxUses = Math.min(maxUses, remainingMaterialsLeft);
}
if (maxUses > 0) {
// 检查使用此模式后,是否会导致超过最大切割状态订单数
Set<String> newCuttingOrders = new HashSet<>(cuttingOrders);
for (String type : pattern.quantities.keySet()) {
if (remainingOrders.get(type).quantity > 0) {
newCuttingOrders.add(type);
}
}
if (newCuttingOrders.size() > MAX_CUTTING_ORDERS) {
continue;
}
// 使用该模式
usedPatterns.add(new UsedPattern(pattern, maxUses));
if (pattern.isStandard) {
} else {
remainingMaterialsUsed += maxUses;
}
// 更新剩余需求并检查是否有订单完成
for (Map.Entry<String, Integer> entry : pattern.quantities.entrySet()) {
String type = entry.getKey();
int used = entry.getValue() * maxUses;
remainingOrders.get(type).quantity -= used;
// 检查订单是否完成
if (remainingOrders.get(type).quantity == 0 && !completionPatterns.containsKey(type)) {
completionPatterns.put(type, currentPatternNumber);
}
}
// 更新切割状态订单
cuttingOrders = newCuttingOrders;
// 移除已完成的切割状态订单
Iterator<String> it = cuttingOrders.iterator();
while (it.hasNext()) {
String type = it.next();
if (remainingOrders.get(type).quantity == 0) {
it.remove();
}
}
patternUsed = true;
break;
}
}
if (!patternUsed) {
// 如果没有可用的模式,尝试单独切割剩余需求量最大的类型
String maxType = findMaxRemainingType(remainingOrders);
if (maxType != null) {
SteelOrder order = remainingOrders.get(maxType);
// 优先使用剩余原材料
boolean useStandard = remainingMaterialsUsed >= MAX_REMAINING_MATERIALS;
BigDecimal materialLength = useStandard ? STANDARD_RAW_MATERIAL_LENGTH : REMAINING_RAW_MATERIAL_LENGTH;
// 新增:计算单类型最大可切割数量(减少废料)
int maxNum = 0;
for (int num = 1; ; num++) {
BigDecimal totalLoss = CUTTING_LOSS.multiply(BigDecimal.valueOf(num - 1)); // n个工件有n-1次切割
BigDecimal totalLength = order.length.multiply(BigDecimal.valueOf(num)).add(totalLoss);
if (totalLength.compareTo(materialLength) > 0) {
maxNum = num - 1;
break;
}
}
if (maxNum == 0) maxNum = 1; // 至少切1个
// 创建一个只切割该类型的模式,考虑切割损耗
Map<String, Integer> singlePatternMap = new LinkedHashMap<>();
singlePatternMap.put(maxType, maxNum);
CuttingPattern singlePattern = new CuttingPattern(
singlePatternMap,
materialLength.subtract(order.length.multiply(BigDecimal.valueOf(maxNum)).add(CUTTING_LOSS.multiply(BigDecimal.valueOf(maxNum - 1)))),
useStandard
);
// 使用该模式一次
usedPatterns.add(new UsedPattern(singlePattern, 1));
if (useStandard) {
// standardMaterialsUsed++;
} else {
remainingMaterialsUsed++;
}
order.quantity--;
// 检查订单是否完成
if (order.quantity == 0 && !completionPatterns.containsKey(maxType)) {
completionPatterns.put(maxType, currentPatternNumber);
}
// 更新切割状态订单
if (order.quantity > 0) {
cuttingOrders.add(maxType);
} else {
cuttingOrders.remove(maxType);
}
patternUsed = true;
}
if (!patternUsed) {
// 如果还是没有可用的模式,说明算法遇到了问题
break;
}
}
}
// 释放大型对象
remainingPatterns = null;
standardPatterns = null;
remainingOrders = null;
return usedPatterns;
}
// 计算方案的总钢材数量
private static int calculateTotalPieces(Map<String, Integer> quantities) {
int totalPieces = 0;
for (int quantity : quantities.values()) {
totalPieces += quantity;
}
return totalPieces;
}
// 查找剩余需求量最大的类型
private static String findMaxRemainingType(Map<String, SteelOrder> orders) {
String maxType = null;
int maxQuantity = 0;
for (Map.Entry<String, SteelOrder> entry : orders.entrySet()) {
if (entry.getValue().quantity > maxQuantity) {
maxQuantity = entry.getValue().quantity;
maxType = entry.getKey();
}
}
return maxType;
}
// 检查是否还有剩余订单
private static boolean hasRemainingOrders(Map<String, SteelOrder> orders) {
for (SteelOrder order : orders.values()) {
if (order.quantity > 0) {
return true;
}
}
return false;
}
// 输出结果
private static void printResults(
List<org.springblade.schedulemanagement.utils.CuttingPatternByUtils.UsedPattern> usedPatterns,
Map<String, SteelOrder> originalOrders,
Map<String, Integer> completionPatterns, List<CuttingPatternVO> cuttingPatternVOS) {
// int totalRemainingMaterials = 0;
// int totalStandardMaterials = 0;
BigDecimal totalRemainder = BigDecimal.ZERO;
int patternNumber = 1;
for (org.springblade.schedulemanagement.utils.CuttingPatternByUtils.UsedPattern usedPattern : usedPatterns) {
org.springblade.schedulemanagement.utils.CuttingPatternByUtils.CuttingPattern pattern = usedPattern.pattern;
int uses = usedPattern.uses;
// if (pattern.isStandard) {
// totalStandardMaterials += uses;
// } else {
// totalRemainingMaterials += uses;
// }
totalRemainder = totalRemainder.add(pattern.remainder.multiply(BigDecimal.valueOf(uses)));
CuttingPatternVO cuttingPatternVO = new CuttingPatternVO();
CuttingSchedulePlan cuttingSchedulePlan = new CuttingSchedulePlan();
List<CuttingSchedulePlanRef> cuttingSchedulePlanRefs = new ArrayList<>();
for (Map.Entry<String, Integer> entry : pattern.quantities.entrySet()) {
String type = entry.getKey();
int quantity = entry.getValue();
SteelOrder order = originalOrders.get(type);
cuttingSchedulePlan.setMaterialType(pattern.isStandard ? "" : "余料");
cuttingSchedulePlan.setDescription(order.description);
cuttingSchedulePlan.setSpecifications(order.specifications);
CuttingSchedulePlanRef cuttingSchedulePlanRef = new CuttingSchedulePlanRef();
cuttingSchedulePlanRef.setModel(type);
cuttingSchedulePlanRef.setSingleNum(quantity);
cuttingSchedulePlanRef.setLength(order.length);
cuttingSchedulePlanRef.setTotalNum(uses * quantity);
cuttingSchedulePlanRefs.add(cuttingSchedulePlanRef);
}
String OrderCompletionDetails = "";
// 检查该方案是否完成了任何订单
for (String type : pattern.quantities.keySet()) {
if (completionPatterns.getOrDefault(type, -1) == patternNumber) {
//该芯轴编号的订单已完成
OrderCompletionDetails = OrderCompletionDetails + type + ",";
}
}
patternNumber++;
if (!OrderCompletionDetails.equals("")) {
OrderCompletionDetails = OrderCompletionDetails.substring(0, OrderCompletionDetails.length() - 1);
}
cuttingSchedulePlan.setOrderCompletionDetails(OrderCompletionDetails);
cuttingSchedulePlan.setNum(uses);
cuttingSchedulePlan.setWasteLength(pattern.remainder);
cuttingPatternVO.setCuttingSchedulePlan(cuttingSchedulePlan);
cuttingPatternVO.setCuttingSchedulePlanRefs(cuttingSchedulePlanRefs);
cuttingPatternVOS.add(cuttingPatternVO);
}
}
// 切割方案类
static class CuttingPattern {
Map<String, Integer> quantities;
BigDecimal remainder;
boolean isStandard; // 是否为标准原材料
public CuttingPattern(Map<String, Integer> quantities, BigDecimal remainder, boolean isStandard) {
this.quantities = quantities;
this.remainder = remainder.setScale(1, ROUNDING_MODE);
this.isStandard = isStandard;
}
}
// 已使用的切割方案类
static class UsedPattern {
CuttingPattern pattern;
int uses;
public UsedPattern(CuttingPattern pattern, int uses) {
this.pattern = pattern;
this.uses = uses;
}
}
// 组合回调接口
interface CombinationCallback {
void process(List<String> combination);
}
}
怎么优化,让废料长度变的更小