Commit f864f507 authored by Tong Li's avatar Tong Li

优化

parent 6ba23982
...@@ -304,7 +304,7 @@ int opcount=allOperations.size(); ...@@ -304,7 +304,7 @@ int opcount=allOperations.size();
best.setScenarioID(sceneId); best.setScenarioID(sceneId);
// best.setOperatRel(_entryRel); // best.setOperatRel(_entryRel);
if(best.getInitMachines()==null) if(best.getInitMachines()==null||best.getInitMachines().size()==0)
{ {
best.setInitMachines(ProductionDeepCopyUtil.deepCopyList(machines,Machine.class)); best.setInitMachines(ProductionDeepCopyUtil.deepCopyList(machines,Machine.class));
} }
......
...@@ -19,36 +19,47 @@ import java.util.stream.Collectors; ...@@ -19,36 +19,47 @@ import java.util.stream.Collectors;
public class VariableNeighborhoodSearch { public class VariableNeighborhoodSearch {
private final Random rnd = new Random(); private final Random rnd = new Random();
// ==================== 常量定义 ==================== // ==================== 策略选择与轮换参数 ====================
private static final int MAX_CONSECUTIVE_SAME_STRATEGY = 3; private static final int MAX_CONSECUTIVE_SAME_STRATEGY = 4; // 同一策略最多连续使用次数,避免陷入局部最优
private static final int MAX_NEIGHBORHOODS = 4; // 优化:从7减少到4个邻域 private static final int MAX_NEIGHBORHOODS = 4; // 使用的邻域数量(0-3:插单/交换/并行合并/重排)
private static final double HIGH_PRIORITY_GROUP_PROBABILITY = 0.7;
private static final int MAX_ATTEMPTS = 50; // ==================== 邻域搜索参数 ====================
private static final int NEIGHBOR_SELECTION_TOP_K = 3; private static final double HIGH_PRIORITY_GROUP_PROBABILITY = 0.7; // 优先选择高优先级工单的概率
private static final int MOVE_STEPS_MIN = 1; private static final int MAX_ATTEMPTS = 30; // 每次邻域搜索最大尝试次数,减少无效搜索
private static final int MOVE_STEPS_MAX = 5; private static final int NEIGHBOR_SELECTION_TOP_K = 3; // 每次选择K个候选工序进行操作
private static final int NON_BOTTLENECK_MOVE_STEPS_MAX = 3;
private static final int RANDOM_NEIGHBOR_TYPE_COUNT = 5; // ==================== 工序移动参数 ====================
private static final double INITIAL_SUCCESS_RATE = 0.3; private static final int MOVE_STEPS_MIN = 1; // 工序移动最小步数
private static final double LOW_SUCCESS_RATE_THRESHOLD = 0.1; private static final int MOVE_STEPS_MAX = 5; // 工序移动最大步数
private static final int LOW_SUCCESS_RATE_MIN_TRIALS = 10; private static final int NON_BOTTLENECK_MOVE_STEPS_MAX = 3; // 非瓶颈设备工序移动最大步数
private static final double EXPLORATION_BONUS_FOR_LOW_USE = 0.3;
private static final double EXPLORATION_BONUS_FOR_LOW_SUCCESS = 0.2; // ==================== 随机邻域参数 ====================
private static final double RANDOM_NOISE_FACTOR = 0.15; private static final int RANDOM_NEIGHBOR_TYPE_COUNT = 3; // 随机邻域尝试次数
private static final double FORCE_ROTATE_PENALTY = 0.5;
private static final double PERCENTILE_90 = 0.90; // ==================== 策略成功率评分参数 ====================
private static final double BOTTLENECK_MACHINE_BASE_SCORE = 10000; private static final double INITIAL_SUCCESS_RATE = 0.3; // 新策略的初始成功率(未使用时的默认值)
private static final double WAIT_TIME_WEIGHT = 30; private static final double LOW_SUCCESS_RATE_THRESHOLD = 0.1; // 低成功率阈值,低于此值需要额外探索奖励
private static final double MACHINE_WAIT_TIME_WEIGHT = 30; private static final int LOW_SUCCESS_RATE_MIN_TRIALS = 10; // 低成功率判断的最小尝试次数
private static final double CRITICAL_PATH_WEIGHT_ON_BOTTLENECK = 25; private static final double EXPLORATION_BONUS_FOR_LOW_USE = 0.15; // 低使用次数策略的探索奖励分
private static final double CRITICAL_PATH_WEIGHT_OFF_BOTTLENECK = 20; private static final double EXPLORATION_BONUS_FOR_LOW_SUCCESS = 0.1; // 低成功率策略的探索奖励分
private static final double DELAY_CONTRIBUTION_WEIGHT_ON_BOTTLENECK = 15; private static final double RANDOM_NOISE_FACTOR = 0.1; // 策略排序时的随机扰动因子,避免陷入局部最优
private static final double DELAY_CONTRIBUTION_WEIGHT_OFF_BOTTLENECK = 10; private static final double FORCE_ROTATE_PENALTY = 0.5; // 强制轮换时的惩罚系数
private static final double NORMALIZATION_CAP = 1.5;
private static final int TOP_BOTTLENECK_OPS_TO_RETURN = 10; // ==================== 瓶颈设备评分参数 ====================
private static final int TOP_MACHINES_TO_LOG = 5; private static final double PERCENTILE_90 = 0.90; // 计算瓶颈评分时使用的90分位数,避免极端值影响
private static final double PRIORITY_EPSILON = 0.001; private static final double BOTTLENECK_MACHINE_BASE_SCORE = 10000; // 瓶颈设备工序的基础评分,确保排在前面
private static final int MIN_USE_COUNT_FOR_EXPLORATION_BONUS = 5; private static final double WAIT_TIME_WEIGHT = 30; // 工序等待时间在瓶颈评分中的权重
private static final double MACHINE_WAIT_TIME_WEIGHT = 30; // 设备等待时间在瓶颈评分中的权重
private static final double CRITICAL_PATH_WEIGHT_ON_BOTTLENECK = 25; // 关键路径在瓶颈设备评分中的权重
private static final double CRITICAL_PATH_WEIGHT_OFF_BOTTLENECK = 20; // 关键路径在非瓶颈设备评分中的权重
private static final double DELAY_CONTRIBUTION_WEIGHT_ON_BOTTLENECK = 15; // 延迟贡献度在瓶颈设备评分中的权重
private static final double DELAY_CONTRIBUTION_WEIGHT_OFF_BOTTLENECK = 10; // 延迟贡献度在非瓶颈设备评分中的权重
private static final double NORMALIZATION_CAP = 1.5; // 归一化截断值,避免极端值过度影响
// ==================== 日志与输出参数 ====================
private static final int TOP_BOTTLENECK_OPS_TO_RETURN = 10; // 返回的瓶颈工序数量
private static final int TOP_MACHINES_TO_LOG = 5; // 日志输出的设备数量
private static final int MIN_USE_COUNT_FOR_EXPLORATION_BONUS = 5; // 低使用次数判断的最小阈值
// 日志级别 // 日志级别
private static final int LOG_LEVEL_DEBUG = 0; private static final int LOG_LEVEL_DEBUG = 0;
...@@ -729,9 +740,9 @@ public class VariableNeighborhoodSearch { ...@@ -729,9 +740,9 @@ public class VariableNeighborhoodSearch {
if (allResults != null && !allResults.isEmpty()) { if (allResults != null && !allResults.isEmpty()) {
List<BottleneckOperationAnalysis> bottleneckAnalyses = analyzeBottleneckOperations(allResults, bottleneckMachineId); List<BottleneckOperationAnalysis> bottleneckAnalyses = analyzeBottleneckOperations(allResults, bottleneckMachineId);
// 输出瓶颈工序TOP 5 // 输出瓶颈工序TOP
log("变邻域搜索 - ===== 瓶颈工序TOP 5 (按瓶颈评分) ====="); log(String.format("变邻域搜索 - ===== 瓶颈工序TOP %d (按瓶颈评分) =====", TOP_MACHINES_TO_LOG));
for (int i = 0; i < Math.min(5, bottleneckAnalyses.size()); i++) { for (int i = 0; i < Math.min(TOP_MACHINES_TO_LOG, bottleneckAnalyses.size()); i++) {
BottleneckOperationAnalysis boa = bottleneckAnalyses.get(i); BottleneckOperationAnalysis boa = bottleneckAnalyses.get(i);
GAScheduleResult op = boa.operation; GAScheduleResult op = boa.operation;
log(String.format( log(String.format(
...@@ -745,7 +756,7 @@ public class VariableNeighborhoodSearch { ...@@ -745,7 +756,7 @@ public class VariableNeighborhoodSearch {
// 更新关键工序为识别出的瓶颈工序 // 更新关键工序为识别出的瓶颈工序
if (criticalOps != null && !criticalOps.isEmpty() && !bottleneckAnalyses.isEmpty()) { if (criticalOps != null && !criticalOps.isEmpty() && !bottleneckAnalyses.isEmpty()) {
List<GAScheduleResult> topBottlenecks = bottleneckAnalyses.stream() List<GAScheduleResult> topBottlenecks = bottleneckAnalyses.stream()
.limit(Math.min(10, bottleneckAnalyses.size())) .limit(Math.min(TOP_BOTTLENECK_OPS_TO_RETURN, bottleneckAnalyses.size()))
.map(boa -> boa.operation) .map(boa -> boa.operation)
.collect(Collectors.toList()); .collect(Collectors.toList());
analysis.put("criticalOps", topBottlenecks); analysis.put("criticalOps", topBottlenecks);
...@@ -869,8 +880,8 @@ public class VariableNeighborhoodSearch { ...@@ -869,8 +880,8 @@ public class VariableNeighborhoodSearch {
Collections.sort(machineWaitTimes); Collections.sort(machineWaitTimes);
// 使用90分位数代替最大值,避免极端值影响 // 使用90分位数代替最大值,避免极端值影响
double p90WaitTime = Math.max(1, getPercentile(waitTimes, 0.90)); // 防止0导致NaN double p90WaitTime = Math.max(1, getPercentile(waitTimes, PERCENTILE_90)); // 防止0导致NaN
double p90MachineWaitTime = Math.max(1, getPercentile(machineWaitTimes, 0.90)); // 防止0导致NaN double p90MachineWaitTime = Math.max(1, getPercentile(machineWaitTimes, PERCENTILE_90)); // 防止0导致NaN
// 优先找出瓶颈设备上的工序 // 优先找出瓶颈设备上的工序
List<BottleneckOperationAnalysis> bottleneckMachineOps = new ArrayList<>(); List<BottleneckOperationAnalysis> bottleneckMachineOps = new ArrayList<>();
...@@ -892,42 +903,42 @@ public class VariableNeighborhoodSearch { ...@@ -892,42 +903,42 @@ public class VariableNeighborhoodSearch {
if (isOnBottleneckMachine) { if (isOnBottleneckMachine) {
// 瓶颈设备上的工序:基础分极高,确保排在最前面 // 瓶颈设备上的工序:基础分极高,确保排在最前面
score += 10000; // 提高到10000,绝对保证排在前面 score += BOTTLENECK_MACHINE_BASE_SCORE; // 绝对保证排在前面
// 在瓶颈设备工序内部进行排序(使用截断值) // 在瓶颈设备工序内部进行排序(使用截断值)
// 工序等待时间 (权重 30%) // 工序等待时间
double normalizedWaitTime = Math.min(boa.waitTime / p90WaitTime, 1.5); double normalizedWaitTime = Math.min(boa.waitTime / p90WaitTime, NORMALIZATION_CAP);
score += normalizedWaitTime * 30; score += normalizedWaitTime * WAIT_TIME_WEIGHT;
// 设备等待时间 (权重 30%) // 设备等待时间
double normalizedMachineWaitTime = Math.min(boa.machineWaitTime / p90MachineWaitTime, 1.5); double normalizedMachineWaitTime = Math.min(boa.machineWaitTime / p90MachineWaitTime, NORMALIZATION_CAP);
score += normalizedMachineWaitTime * 30; score += normalizedMachineWaitTime * MACHINE_WAIT_TIME_WEIGHT;
// 关键路径评分 (权重 25%) // 关键路径评分
if (boa.onCriticalPath) { if (boa.onCriticalPath) {
score += 25; score += CRITICAL_PATH_WEIGHT_ON_BOTTLENECK;
} }
// 延迟贡献度评分 (权重 15%) // 延迟贡献度评分
score += boa.delayContribution * 15; score += boa.delayContribution * DELAY_CONTRIBUTION_WEIGHT_ON_BOTTLENECK;
} else { } else {
// 非瓶颈设备上的工序:基础分低 // 非瓶颈设备上的工序:基础分低
score += 0; score += 0;
// 使用截断值,避免极端值影响 // 使用截断值,避免极端值影响
double normalizedMachineWaitTime = Math.min(boa.machineWaitTime / p90MachineWaitTime, 1.5); double normalizedMachineWaitTime = Math.min(boa.machineWaitTime / p90MachineWaitTime, NORMALIZATION_CAP);
score += normalizedMachineWaitTime * 30; score += normalizedMachineWaitTime * MACHINE_WAIT_TIME_WEIGHT;
double normalizedWaitTime = Math.min(boa.waitTime / p90WaitTime, 1.5); double normalizedWaitTime = Math.min(boa.waitTime / p90WaitTime, NORMALIZATION_CAP);
score += normalizedWaitTime * 20; score += normalizedWaitTime * WAIT_TIME_WEIGHT;
// 关键路径评分 (权重 20%) // 关键路径评分
if (boa.onCriticalPath) { if (boa.onCriticalPath) {
score += 20; score += CRITICAL_PATH_WEIGHT_OFF_BOTTLENECK;
} }
// 延迟贡献度评分 (权重 10%) // 延迟贡献度评分
score += boa.delayContribution * 10; score += boa.delayContribution * DELAY_CONTRIBUTION_WEIGHT_OFF_BOTTLENECK;
} }
boa.bottleneckScore = score; boa.bottleneckScore = score;
...@@ -937,12 +948,12 @@ public class VariableNeighborhoodSearch { ...@@ -937,12 +948,12 @@ public class VariableNeighborhoodSearch {
log(String.format("变邻域搜索 - 瓶颈设备ID=%d, 其上工序数=%d", log(String.format("变邻域搜索 - 瓶颈设备ID=%d, 其上工序数=%d",
bottleneckMachineId, bottleneckMachineOps.size())); bottleneckMachineId, bottleneckMachineOps.size()));
// 输出各设备工序数量统计(前5个) // 输出各设备工序数量统计
List<Map.Entry<Long, Long>> topMachinesByCount = machineOpCount.entrySet().stream() List<Map.Entry<Long, Long>> topMachinesByCount = machineOpCount.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue())) .sorted((a, b) -> Long.compare(b.getValue(), a.getValue()))
.limit(5) .limit(TOP_MACHINES_TO_LOG)
.collect(Collectors.toList()); .collect(Collectors.toList());
StringBuilder machineCountStr = new StringBuilder("变邻域搜索 - 设备工序数(前5): "); StringBuilder machineCountStr = new StringBuilder(String.format("变邻域搜索 - 设备工序数(前%d): ", TOP_MACHINES_TO_LOG));
for (Map.Entry<Long, Long> entry : topMachinesByCount) { for (Map.Entry<Long, Long> entry : topMachinesByCount) {
machineCountStr.append(String.format("[%d:%d] ", entry.getKey(), entry.getValue())); machineCountStr.append(String.format("[%d:%d] ", entry.getKey(), entry.getValue()));
} }
...@@ -983,37 +994,37 @@ public class VariableNeighborhoodSearch { ...@@ -983,37 +994,37 @@ public class VariableNeighborhoodSearch {
// 按成功率排序,但加入随机扰动和探索机制 // 按成功率排序,但加入随机扰动和探索机制
strategies.sort((a, b) -> { strategies.sort((a, b) -> {
double successRateA = strategyTotalCount[a] > 0 ? double successRateA = strategyTotalCount[a] > 0 ?
(double) strategySuccessCount[a] / strategyTotalCount[a] : 0.3; (double) strategySuccessCount[a] / strategyTotalCount[a] : INITIAL_SUCCESS_RATE;
double successRateB = strategyTotalCount[b] > 0 ? double successRateB = strategyTotalCount[b] > 0 ?
(double) strategySuccessCount[b] / strategyTotalCount[b] : 0.3; (double) strategySuccessCount[b] / strategyTotalCount[b] : INITIAL_SUCCESS_RATE;
// 强制轮换:给最后使用的策略降权 // 强制轮换:给最后使用的策略降权
if (needForceRotate && a == lastUsedStrategy) { if (needForceRotate && a == lastUsedStrategy) {
successRateA -= 0.5; // 大幅降权 successRateA -= FORCE_ROTATE_PENALTY; // 大幅降权
} }
if (needForceRotate && b == lastUsedStrategy) { if (needForceRotate && b == lastUsedStrategy) {
successRateB -= 0.5; successRateB -= FORCE_ROTATE_PENALTY;
} }
// 给使用次数少的策略加分,鼓励探索 // 给使用次数少的策略加分,鼓励探索
if (strategyTotalCount[a] < 5) { if (strategyTotalCount[a] < MIN_USE_COUNT_FOR_EXPLORATION_BONUS) {
successRateA += 0.3; successRateA += EXPLORATION_BONUS_FOR_LOW_USE;
} }
if (strategyTotalCount[b] < 5) { if (strategyTotalCount[b] < MIN_USE_COUNT_FOR_EXPLORATION_BONUS) {
successRateB += 0.3; successRateB += EXPLORATION_BONUS_FOR_LOW_USE;
} }
// 如果策略被尝试次数很多但成功率很低,给它额外加分鼓励探索 // 如果策略被尝试次数很多但成功率很低,给它额外加分鼓励探索
if (strategyTotalCount[a] > 10 && successRateA < 0.1) { if (strategyTotalCount[a] > LOW_SUCCESS_RATE_MIN_TRIALS && successRateA < LOW_SUCCESS_RATE_THRESHOLD) {
successRateA += 0.2; successRateA += EXPLORATION_BONUS_FOR_LOW_SUCCESS;
} }
if (strategyTotalCount[b] > 10 && successRateB < 0.1) { if (strategyTotalCount[b] > LOW_SUCCESS_RATE_MIN_TRIALS && successRateB < LOW_SUCCESS_RATE_THRESHOLD) {
successRateB += 0.2; successRateB += EXPLORATION_BONUS_FOR_LOW_SUCCESS;
} }
// 加入少量随机扰动,避免陷入局部最优 // 加入少量随机扰动,避免陷入局部最优
successRateA += rnd.nextDouble() * 0.15; successRateA += rnd.nextDouble() * RANDOM_NOISE_FACTOR;
successRateB += rnd.nextDouble() * 0.15; successRateB += rnd.nextDouble() * RANDOM_NOISE_FACTOR;
return Double.compare(successRateB, successRateA); return Double.compare(successRateB, successRateA);
}); });
...@@ -2040,7 +2051,6 @@ public class VariableNeighborhoodSearch { ...@@ -2040,7 +2051,6 @@ public class VariableNeighborhoodSearch {
} else { } else {
chromosome.setResultOld(new CopyOnWriteArrayList<>()); chromosome.setResultOld(new CopyOnWriteArrayList<>());
} }
//decoder.decodeChromosomeWithCache(chromosome,true);
}); });
log(String.format("变邻域搜索 -复制对象E")); log(String.format("变邻域搜索 -复制对象E"));
......
...@@ -40,7 +40,7 @@ public class PlanResultServiceTest { ...@@ -40,7 +40,7 @@ public class PlanResultServiceTest {
// sortService.test1(); // sortService.test1();
// nsgaiiUtils.Test(); // nsgaiiUtils.Test();
planResultService.execute2("F6747C64865D4609989D943709939331");//2000 planResultService.execute2("E29F2B3ADA8149F6B916B5119296A92B");//2000
// planResultService.execute2("08B1D87FE1B84ECDBAC2E546DDB6FB81");//2000 // planResultService.execute2("08B1D87FE1B84ECDBAC2E546DDB6FB81");//2000
// planResultService.execute2("0428340BB4F540938F1FB5599F03E8A4");//5000 // planResultService.execute2("0428340BB4F540938F1FB5599F03E8A4");//5000
......
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