Commit 7d27c0ff authored by DESKTOP-VKRD9QF\Administration's avatar DESKTOP-VKRD9QF\Administration

Merge branch 'master' of http://39.100.78.207:1213/tongli/hyh.apsj

# Conflicts:
#	src/main/java/com/aps/entity/Algorithm/Chromosome.java
parents 8231751e 08991d0b
......@@ -45,6 +45,16 @@ public class RedisUtils {
@Getter
public static String prefix = "aps:";
/**
* 最大重试次数
*/
private static final int MAX_RETRY_TIMES = 3;
/**
* 重试间隔(毫秒)
*/
private static final long RETRY_DELAY_MS = 500;
/**
* 设置redis前缀进行操作
*
......@@ -62,7 +72,7 @@ public class RedisUtils {
* @return: boolean
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(prefix + key);
return executeWithRetry(() -> redisTemplate.hasKey(prefix + key), "exists(" + key + ")");
}
public Set<String> keys(final String pattern) {
......@@ -164,6 +174,7 @@ public class RedisUtils {
* @param key 可以传一个值 或多个
*/
public void del(String... key) {
executeWithRetry(() -> {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(prefix + key[0]);
......@@ -171,6 +182,7 @@ public class RedisUtils {
redisTemplate.delete(Arrays.asList(key).stream().map(e -> prefix + e).collect(Collectors.toList()));
}
}
}, "del(" + Arrays.toString(key) + ")");
}
public void del(Collection<String> keys) {
......@@ -184,7 +196,7 @@ public class RedisUtils {
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(prefix + key);
return executeWithRetry(() -> key == null ? null : redisTemplate.opsForValue().get(prefix + key), "get(" + key + ")");
}
/**
......@@ -207,6 +219,7 @@ public class RedisUtils {
* @return true成功 false失败
*/
public Boolean set(String key, Object value) {
return executeWithRetry(() -> {
try {
redisTemplate.opsForValue().set(prefix + key, value);
return true;
......@@ -214,6 +227,7 @@ public class RedisUtils {
log.error("Exception: {}", e.getMessage());
return false;
}
}, "set(" + key + ")");
}
/**
......@@ -814,4 +828,120 @@ public class RedisUtils {
redisLockUtil.releaseLock(prefix + key, "");
}
/**
* 通用重试执行器:用于处理 Redis 连接异常
* @param operation Redis 操作函数
* @param operationName 操作名称(用于日志)
* @param <T> 返回值类型
* @return 操作结果
*/
private <T> T executeWithRetry(java.util.function.Supplier<T> operation, String operationName) {
Exception lastException = null;
for (int attempt = 1; attempt <= MAX_RETRY_TIMES; attempt++) {
try {
if (attempt > 1) {
log.warn("Redis 操作重试 {}/{}: {}", attempt, MAX_RETRY_TIMES, operationName);
}
return operation.get();
} catch (Exception e) {
lastException = e;
log.error("Redis 操作失败 {}/{}: {}, 错误: {}", attempt, MAX_RETRY_TIMES, operationName, e.getMessage());
if (attempt < MAX_RETRY_TIMES) {
try {
log.warn("等待 {}ms 后重试...", RETRY_DELAY_MS);
Thread.sleep(RETRY_DELAY_MS);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("Redis 操作重试被中断", ie);
}
}
}
}
throw new RuntimeException("Redis 操作失败,已重试 " + MAX_RETRY_TIMES + " 次: " + operationName, lastException);
}
/**
* 无返回值的重试执行器
* @param operation Redis 操作函数
* @param operationName 操作名称(用于日志)
*/
private void executeWithRetry(Runnable operation, String operationName) {
executeWithRetry(() -> {
operation.run();
return null;
}, operationName);
}
// ==================== 以下是添加了重试机制的方法 ====================
/**
* 判断是否有这个缓存key(带重试)
* @param key 缓存key
* @return boolean
*/
public boolean existsWithRetry(final String key) {
return executeWithRetry(() -> exists(key), "exists(" + key + ")");
}
/**
* 普通缓存获取(带重试)
* @param key 键
* @return 值
*/
public Object getWithRetry(String key) {
return executeWithRetry(() -> get(key), "get(" + key + ")");
}
/**
* 普通缓存放入(带重试)
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public Boolean setWithRetry(String key, Object value) {
return executeWithRetry(() -> set(key, value), "set(" + key + ")");
}
/**
* 普通缓存放入并设置时间(带重试)
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return true成功 false失败
*/
public Boolean setWithRetry(String key, Object value, Long time) {
return executeWithRetry(() -> set(key, value, time), "set(" + key + ", " + time + "s)");
}
/**
* 删除缓存(带重试)
* @param key 可以传一个值 或多个
*/
public void delWithRetry(String... key) {
executeWithRetry(() -> del(key), "del(" + Arrays.toString(key) + ")");
}
/**
* HashGet(带重试)
* @param key 键 不能为 null
* @param item 项 不能为 null
* @return 值
*/
public Object hgetWithRetry(String key, String item) {
return executeWithRetry(() -> hget(key, item), "hget(" + key + ", " + item + ")");
}
/**
* HashSet(带重试)
* @param key 键
* @param map 对应多个键值
* @return true成功 false失败
*/
public Boolean hmsetWithRetry(String key, Map<String, Object> map) {
return executeWithRetry(() -> hmset(key, map), "hmset(" + key + ")");
}
}
......@@ -173,8 +173,11 @@ public class ScheduleParams {
populationSize = (int) Math.max(MIN_POPULATION_SIZE,
Math.min(MAX_POPULATION_SIZE, MIN_POPULATION_SIZE + totalOps * populationSizeCoeff));
int maxthead= Runtime.getRuntime().availableProcessors() - 1;
populationSize= populationSize / maxthead*maxthead;
int maxthead = Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
if (populationSize >= maxthead) {
populationSize = populationSize / maxthead * maxthead;
}
populationSize = Math.max(MIN_POPULATION_SIZE, populationSize);
// 确保偶数(方便交叉)
// if (populationSize % 2 != 0) {
......
......@@ -165,15 +165,30 @@ public class FitnessCalculator {
* 比较两个染色体的优劣(基于fitnessLevel多层次比较)
*/
public boolean isBetter(Chromosome c1, Chromosome c2) {
for (int i = 0; i < c1.getFitnessLevel().length; i++) {
double[] Fitness1 = c1.getFitnessLevel();
double[] Fitness2 = c2.getFitnessLevel();
if (Fitness1[i] > Fitness2[i]) {
return true;
double[] fitness1 = c1.getFitnessLevel();
double[] fitness2 = c2.getFitnessLevel();
// 处理null或空数组的情况
if (fitness1 == null || fitness1.length == 0) {
return false; // c1没有fitness,认为c2更优
}
if (fitness2 == null || fitness2.length == 0) {
return true; // c2没有fitness,认为c1更优
}
// 取两个数组中较小的长度进行比较
int minLength = Math.min(fitness1.length, fitness2.length);
for (int i = 0; i < minLength; i++) {
if (fitness1[i] > fitness2[i]) {
return true;
} else if (fitness1[i] < fitness2[i]) {
return false;
}
}
// 如果前面都相等,长度更长的视为更优(有更多维度)
return fitness1.length > fitness2.length;
}
......
......@@ -194,7 +194,8 @@ public class GeneticDecoder {
// 缓存命中:复用缓存结果
// 赋值给染色体
// chromosome.setInitMachines(ProductionDeepCopyUtil.deepCopyList(cachedResult.getInitMachines(),Machine.class));
chromosome.setFitnessLevel(cachedResult.getFitnessLevel());
chromosome.setFitness(cachedResult.getFitness());
chromosome.setObjectives(cachedResult.getObjectives());
chromosome.setMakespan(cachedResult.getMakespan());
chromosome.setTotalFlowTime(cachedResult.getTotalFlowTime());
......
......@@ -105,34 +105,69 @@ public class GeneticOperations {
/**
* 锦标赛选择
*/
public List<Chromosome> tournamentSelection1(List<Chromosome> population, int tournamentSize) {
int populationSize = population.size();
List<Chromosome> selected = new ArrayList<>(); // 预初始化容量
public List<Chromosome> tournamentSelection2(List<Chromosome> population, int tournamentSize) {
// 前置边界校验(避免NPE和无效计算)
if (population == null || population.isEmpty()) {
return new ArrayList<>();
}
int populationSize = population.size()>100?((int)(population.size()*0.8)):population.size();
// 预计算需要选中的个体数量,避免动态判断
int needSelectCount = populationSize - Math.max(1, Math.min(tournamentSize, populationSize));
List<Chromosome> selected = new ArrayList<>(needSelectCount); // 预初始化容量
// 边界值处理:锦标赛规模不能超过种群规模,且至少为1
int effectiveTournamentSize = Math.max(1, Math.min(tournamentSize, populationSize));
List<Chromosome> selectedtemp = new ArrayList<>(needSelectCount); // 预初始化容量
// 预生成随机索引,减少Random对象创建开销
Random random = this.rnd;
// 固定次数循环,替代动态终止条件
for (int selectIdx = 0; selectIdx < needSelectCount; selectIdx++) {
while (selected.size() < populationSize-tournamentSize) {
// 优化1:不复制整个种群,直接随机抽取有效锦标赛规模的个体,避免大量内存拷贝和shuffle开销
// 优化1:Fisher-Yates抽样(无重复、无冲突,比HashSet高效)
List<Integer> indices = new ArrayList<>(populationSize);
for (int i = 0; i < populationSize; i++) {
indices.add(i);
}
// 仅打乱前effectiveTournamentSize个索引,减少计算量
for (int i = 0; i < effectiveTournamentSize; i++) {
int swapIdx = i + random.nextInt(populationSize - i);
Collections.swap(indices, i, swapIdx);
}
Chromosome bestCandidate=null;
Collections.shuffle(population, new Random());//随机排序
int endIndex = Math.min(effectiveTournamentSize, population.size());
for (int i = 0; i < effectiveTournamentSize; i++) {
int idx = indices.get(i);
Chromosome curr= population.get(idx);
if(bestCandidate!=null)
{
if(isBetter(curr,bestCandidate))
{
bestCandidate=curr;
}
}else {
bestCandidate=curr;
}
}
if (bestCandidate != null) {
selected.add(bestCandidate);
}
}
List<Chromosome> chromosomes=population.subList(0, endIndex);
chromosomes.sort((c1, c2) -> {
int rankCompare = Integer.compare(c1.getRank(), c2.getRank());
return rankCompare != 0 ? rankCompare : Double.compare(c1.getCrowdingDistance(), c2.getCrowdingDistance());
});
Chromosome bestCandidate = chromosomes.get(0);
return selected;// ProductionDeepCopyUtil.deepCopyList(selected, Chromosome.class);
}
// 深拷贝最优个体并加入选中列表(确保线程安全和种群独立性)
if (bestCandidate != null) {
selected.add(ProductionDeepCopyUtil.deepCopy(bestCandidate, Chromosome.class));
private boolean isBetter(Chromosome c1, Chromosome c2) {
for (int i = 0; i < c1.getFitnessLevel().length; i++) {
double[] Fitness1 = c1.getFitnessLevel();
double[] Fitness2 = c2.getFitnessLevel();
if (Fitness1[i] > Fitness2[i]) {
return true;
}
}
return selected;
return false;
}
......@@ -145,6 +180,15 @@ public class GeneticOperations {
return populationn;
}
// 重载,使用默认锦标赛大小3
public List<Chromosome> tournamentSelection2(List<Chromosome> population) {
// List<Chromosome> populationn= ProductionDeepCopyUtil.deepCopyList(tournamentSelection(population, param.getTournamentSize()),Chromosome.class);
List<Chromosome> populationn= tournamentSelection2(population, param.getTournamentSize());
// populationn.parallelStream().forEach(t->DelOrder(t));
return populationn;
}
/**
* 改进POX交叉(工序排序部分)
......@@ -166,7 +210,7 @@ public class GeneticOperations {
maxAllowedCount.put(job, Math.max(count1, count2)); // 取父代中该工件的最大出现次数
}
// 步骤1:随机划分工件集
// 步骤2:随机划分工件集
List<Integer> jobset1 = new ArrayList<>();
for (int i = 1; i <= orderCount; i++) {
jobset1.add(i);
......@@ -184,20 +228,32 @@ public class GeneticOperations {
}
}
// 步骤3:生成子代1(控制工件出现次数不超过父代最大值)
List<Integer> child1Os = generateChildOsWithCountLimit(
parent1, parent2, jobset1Set, maxAllowedCount, seqLength
);
// 步骤4:生成子代2(控制工件出现次数不超过父代最大值)
List<Integer> child2Os = generateChildOsWithCountLimit(
parent2, parent1, jobset1Set, maxAllowedCount, seqLength
);
List<Integer> child1Os, child2Os;
// 根据是否允许打破优先级选择不同的生成策略
if (_GlobalParam.isIsBreakPriority()) {
// 允许打破优先级,使用原来的方法
child1Os = generateChildOsWithCountLimit(parent1, parent2, jobset1Set, maxAllowedCount, seqLength);
child2Os = generateChildOsWithCountLimit(parent2, parent1, jobset1Set, maxAllowedCount, seqLength);
} else {
// 不允许打破优先级,使用优先级保护的方法
child1Os = generateChildOsWithPriorityProtection(parent1, parent2, jobset1Set, maxAllowedCount, seqLength);
child2Os = generateChildOsWithPriorityProtection(parent2, parent1, jobset1Set, maxAllowedCount, seqLength);
}
// 机器选择部分交叉
// 构建工序ID到机器选择的映射,避免因GlobalOpList顺序不同导致的问题
Map<Integer, Integer> parent1OpToMachine = buildOpToMachineMap(parent1);
Map<Integer, Integer> parent2OpToMachine = buildOpToMachineMap(parent2);
List<Integer> child1Ms = new ArrayList<>();
List<Integer> child2Ms = new ArrayList<>();
for (int i = 0; i < parent1.getMachineSelection().size(); i++) {
int m1 = parent1.getMachineSelection().get(i);
int m2 = parent2.getMachineSelection().get(i);
// 使用parent1的globalOpList作为基准(因为child1和child2都继承自parent1的orders/machines等)
for (GlobalOperationInfo opInfo : parent1.getGlobalOpList()) {
int opId = opInfo.getOp().getId();
int m1 = parent1OpToMachine.getOrDefault(opId, 1);
int m2 = parent2OpToMachine.getOrDefault(opId, 1);
if (rnd.nextDouble() < 0.5) {
child1Ms.add(m1);
child2Ms.add(m2);
......@@ -208,19 +264,131 @@ public class GeneticOperations {
}
Chromosome child1 = new Chromosome();
child1.setOrders(parent1.getOrders());
child1.setMachines(parent1.getMachines());
child1.setGlobalOpList(parent1.getGlobalOpList());
child1.setMachineSelection(child1Ms);
child1.setOperationSequencing(child1Os);
Chromosome child2 = new Chromosome();
child2.setOrders(parent1.getOrders());
child2.setMachines(parent1.getMachines());
child2.setGlobalOpList(parent1.getGlobalOpList());
child2.setMachineSelection(child2Ms);
child2.setOperationSequencing(child2Os);
return new Pair<>(child1, child2);
}
/**
* 核心方法:生成子代操作序列(遵循POX规则 + 工件出现次数限制 + 优先级保护)
* 当不允许打破优先级时,确保高优先级的工序始终在低优先级工序之前
* @param mainParent 主父代(优先取其基因)
* @param secondaryParent 次父代(备选基因)
* @param jobset1Set POX交叉的工件集判断依据
* @param maxAllowedCount 每个工件的最大允许出现次数
* @param seqLength 序列长度
* @return 符合规则的子代序列
*/
private List<Integer> generateChildOsWithPriorityProtection(
Chromosome mainParent, Chromosome secondaryParent,
Set<Integer> jobset1Set, Map<Integer, Integer> maxAllowedCount,
int seqLength
) {
List<Integer> mainSeq = mainParent.getOperationSequencing();
List<Integer> secondarySeq = secondaryParent.getOperationSequencing();
// 1. 首先按POX规则和次数限制收集候选序列
List<Integer> candidateSeq = new ArrayList<>();
Map<Integer, Integer> childJobCount = new HashMap<>();
for (int i = 0; i < seqLength; i++) {
int mainOp = mainSeq.get(i);
int secondaryOp = secondarySeq.get(i);
int selectedOp = mainOp;
// 遵循原POX交叉规则筛选候选工件
if (!(jobset1Set.contains(mainOp) || jobset1Set.contains(secondaryOp))) {
selectedOp = secondaryOp;
}
// 检查候选工件是否超过最大允许次数
int currentCount = childJobCount.getOrDefault(selectedOp, 0);
if (currentCount >= maxAllowedCount.get(selectedOp)) {
// 切换到另一个父代的工件
selectedOp = (selectedOp == mainOp) ? secondaryOp : mainOp;
// 极端情况:切换后仍超过次数,从所有工件中选一个未超次数的
if (childJobCount.getOrDefault(selectedOp, 0) >= maxAllowedCount.get(selectedOp)) {
selectedOp = findValidJob(maxAllowedCount, childJobCount);
}
}
childJobCount.put(selectedOp, childJobCount.getOrDefault(selectedOp, 0) + 1);
candidateSeq.add(selectedOp);
}
// 2. 按优先级重新排序,确保不打破优先级
List<IndexedOperation> indexedOps = new ArrayList<>();
for (int i = 0; i < candidateSeq.size(); i++) {
int groupId = candidateSeq.get(i);
Entry entry = allOperations.stream()
.filter(o -> o.getGroupId() == groupId)
.findFirst()
.orElse(null);
double priority = (entry != null) ? entry.getPriority() : 0;
indexedOps.add(new IndexedOperation(i, groupId, priority));
}
// 按优先级降序排序(高优先级在前),同时保持同一优先级内的相对顺序
indexedOps.sort((a, b) -> {
int priorityCompare = Double.compare(a.priority, b.priority);
if (priorityCompare != 0) {
return priorityCompare;
}
return Integer.compare(a.originalIndex, b.originalIndex);
});
// 构建最终的子序列
List<Integer> childOs = new ArrayList<>();
for (IndexedOperation io : indexedOps) {
childOs.add(io.groupId);
}
return childOs;
}
/**
* 辅助方法:构建工序ID到机器选择的映射
* @param chromosome 染色体
* @return 工序ID -> 机器选择索引的映射
*/
private Map<Integer, Integer> buildOpToMachineMap(Chromosome chromosome) {
Map<Integer, Integer> opToMachine = new HashMap<>();
List<GlobalOperationInfo> globalOpList = chromosome.getGlobalOpList();
List<Integer> machineSelection = chromosome.getMachineSelection();
if (globalOpList != null && machineSelection != null) {
for (int i = 0; i < Math.min(globalOpList.size(), machineSelection.size()); i++) {
GlobalOperationInfo opInfo = globalOpList.get(i);
if (opInfo != null && opInfo.getOp() != null) {
int opId = opInfo.getOp().getId();
opToMachine.put(opId, machineSelection.get(i));
}
}
}
return opToMachine;
}
/**
* 辅助类:用于保持原始索引和优先级的工序信息
*/
private static class IndexedOperation {
int originalIndex;
int groupId;
double priority;
IndexedOperation(int originalIndex, int groupId, double priority) {
this.originalIndex = originalIndex;
this.groupId = groupId;
this.priority = priority;
}
}
/**
* 核心方法:生成子代操作序列(遵循POX规则 + 工件出现次数限制)
* @param mainParent 主父代(优先取其基因)
......
......@@ -643,16 +643,29 @@ public class HillClimbing {
/**
* 比较两个染色体的优劣
*/
private boolean isBetter(Chromosome c1, Chromosome c2) {
public boolean isBetter(Chromosome c1, Chromosome c2) {
double[] fitness1 = c1.getFitnessLevel();
double[] fitness2 = c2.getFitnessLevel();
for (int i = 0; i < c1.getFitnessLevel().length; i++) {
double[] Fitness1 = c1.getFitnessLevel();
double[] Fitness2 = c2.getFitnessLevel();
if (Fitness1[i] > Fitness2[i]) {
return true;
// 处理null或空数组的情况
if (fitness1 == null || fitness1.length == 0) {
return false; // c1没有fitness,认为c2更优
}
if (fitness2 == null || fitness2.length == 0) {
return true; // c2没有fitness,认为c1更优
}
// 取两个数组中较小的长度进行比较
int minLength = Math.min(fitness1.length, fitness2.length);
for (int i = 0; i < minLength; i++) {
if (fitness1[i] > fitness2[i]) {
return true;
} else if (fitness1[i] < fitness2[i]) {
return false;
}
}
// 如果前面都相等,长度更长的视为更优(有更多维度)
return fitness1.length > fitness2.length;
}
}
\ No newline at end of file
......@@ -104,7 +104,7 @@ public class HybridAlgorithm {
GeneticOperations geneticOps = new GeneticOperations(_GlobalParam,allOperations,param);
int opcount=allOperations.size();
// 预生成全局工序列表(所有初始化方法共享同一顺序)
List<GlobalOperationInfo> globalOpList = initialization.generateGlobalOpList();
List<GlobalOperationInfo> globalOpList =new ArrayList<>();// initialization.generateGlobalOpList();
// 初始化变邻域搜索
_vns = new VariableNeighborhoodSearch( allOperations,orders,materials,_entryRel, _fitnessCalculator );
_hillClimbing = new HillClimbing(allOperations,orders,materials,_entryRel, _fitnessCalculator);
......@@ -140,11 +140,13 @@ int opcount=allOperations.size();
// return getBestChromosome(population.get(0), param.getBaseTime(), starttime);
// 步骤2:对初始种群进行爬山法局部优化
if(opcount<20 ) {
if(opcount<100 ) {
Chromosome best=population.get(0);
geneticOps.DelOrder(best);
FileHelper.writeLogFile("爬山法局部优化-----------开始-------");
GeneticDecoder hillClimbingDecoder = new GeneticDecoder(_GlobalParam, param.getBaseTime(), machines, orders, materials, machineScheduler, materialRequirementService, sceneId);
Chromosome optimized = _hillClimbing.search(population.get(0), hillClimbingDecoder,machines);
Chromosome optimized = _hillClimbing.searchAll(best,machines,hillClimbingDecoder);
FileHelper.writeLogFile("爬山法局部优化-----------结束-------");
return getBestChromosome(optimized, param.getBaseTime(), starttime);
}
......@@ -161,14 +163,17 @@ int opcount=allOperations.size();
if(opcount<800 ) {
Chromosome best=population.get(0);
geneticOps.DelOrder(best);
FileHelper.writeLogFile("模拟退火+爬山法优化-----------开始-------");
Chromosome saHcOptimized = _simulatedAnnealing.searchWithHillClimbing(population.get(0),_vns, saDecoder1, machines);
Chromosome saHcOptimized = _simulatedAnnealing.searchWithHillClimbing(best,_vns, saDecoder1, machines);
FileHelper.writeLogFile("模拟退火+爬山法优化-----------结束-------");
return getBestChromosome(saHcOptimized, param.getBaseTime(), starttime);
}
if(opcount>=800 ) {
Chromosome best=population.get(0);
geneticOps.DelOrder(best);
best = _simulatedAnnealing.search(best, _tabuSearch, _vns, saDecoder1, machines);
best = _vns.search(best, vnsDecoder1, machines);
best = _tabuSearch.search(best, _vns, tabuDecoder1, machines);
......@@ -197,6 +202,7 @@ int opcount=allOperations.size();
int maxNoImprove = 10;
// 步骤2:迭代进化
FileHelper.writeLogFile("迭代进化-----------开始-------"+param.getMaxIterations());
for (int iter = 0; iter < param.getMaxIterations(); iter++) {
// 解码并计算适应度
FileHelper.writeLogFile("迭代进化------"+iter+"-----开始-------");
......@@ -215,7 +221,7 @@ int opcount=allOperations.size();
FileHelper.writeLogFile("选择操作-----------开始-------");
// 选择操作
List<Chromosome> selected = geneticOps.tournamentSelection(population);
List<Chromosome> selected = geneticOps.tournamentSelection2(population);
FileHelper.writeLogFile("选择操作-----------结束-------");
......@@ -233,13 +239,13 @@ int opcount=allOperations.size();
Chromosome parent2 = selected.get(rnd.nextInt(selected.size()));
// 交叉
Chromosome child = parent1;
// if (rnd.nextDouble() < param.getCrossoverProb()) {
// // 假设PoxCrossover返回包含两个子染色体的数组
// Pair<Chromosome, Chromosome> children = geneticOps.poxCrossover(parent1, parent2, ordercount);
// child=children.getFirst();
// initDataToChromosome(child,param, allOperations,globalOpList);
// child.setID(UUID.randomUUID().toString());
// }
if (rnd.nextDouble() < param.getCrossoverProb()) {
// 假设PoxCrossover返回包含两个子染色体的数组
Pair<Chromosome, Chromosome> children = geneticOps.poxCrossover(parent1, parent2, ordercount);
child=children.getFirst();
// initDataToChromosome(child,param, allOperations,globalOpList);
child.setID(UUID.randomUUID().toString());
}
// // 变异
// if (rnd.nextDouble() < param.getMutationProb()) {
// geneticOps.mutate(child, globalOpList);
......@@ -252,7 +258,7 @@ int opcount=allOperations.size();
newPopulation.add(child);
FileHelper.writeLogFile("交叉-----------结束-------"+n);
n++;
}
population= chromosomeDistinctByObjectives(newPopulation);;
......@@ -301,7 +307,7 @@ int opcount=allOperations.size();
best.setScenarioID(sceneId);
// best.setOperatRel(_entryRel);
if(best.getInitMachines()==null)
if(best.getInitMachines()==null||best.getInitMachines().size()==0)
{
best.setInitMachines(ProductionDeepCopyUtil.deepCopyList(machines,Machine.class));
}
......@@ -378,14 +384,7 @@ int opcount=allOperations.size();
* 比较两个染色体的优劣(基于fitnessLevel多层次比较)
*/
private boolean isBetter(Chromosome c1, Chromosome c2) {
for (int i = 0; i < c1.getFitnessLevel().length; i++) {
double[] Fitness1 = c1.getFitnessLevel();
double[] Fitness2 = c2.getFitnessLevel();
if (Fitness1[i] > Fitness2[i]) {
return true;
}
}
return false;
return _fitnessCalculator.isBetter(c1,c2);
}
......
......@@ -68,7 +68,7 @@ public class Initialization {
int gsCount = (int) (param.getPopulationSize() * param.getGsRatio());
int lsCount =gsCount+ (int) (param.getPopulationSize() * param.getLsRatio());
int rsCount = param.getPopulationSize() - gsCount - lsCount;
int populationSize=param.getPopulationSize();
int populationSize=param.getPopulationSize();
// 并行循环:对应 Parallel.For(0, PopulationSize, i => { ... })
IntStream.range(0, populationSize)
.parallel() // 开启并行
......@@ -76,7 +76,7 @@ int populationSize=param.getPopulationSize();
Chromosome chromo = new Chromosome(); // 初始化染色体
// chromo.setObjectiveWeights(_objectiveWeights);
// chromo.setInitMachines(ProductionDeepCopyUtil.deepCopyList(machines));
chromo.setOrders(new CopyOnWriteArrayList<>(orders));
chromo.setOrders(new CopyOnWriteArrayList<>(orders));
// 全局选择(GS):按GlobalOpId顺序生成MachineSelection
if (i < gsCount) {
chromo.setGsOrls(1);
......@@ -347,13 +347,16 @@ chromo.setOrders(new CopyOnWriteArrayList<>(orders));
List<Chromosome> population = new ArrayList<>();
int populationSize = param.getPopulationSize();
this.baseTime=param.getBaseTime();
// 按比例生成不同启发式规则的个体
int sptCount = (int) (populationSize * 0.2);
int lptCount = sptCount + (int) (populationSize * 0.1);
int eddCount = lptCount + (int) (populationSize * 0.2);
int crCount = eddCount + (int) (populationSize * 0.2);
int sstCount = crCount + (int) (populationSize * 0.2);
int lsCount = populationSize - sstCount;
// 按比例生成不同启发式规则的个体(优化:增加混合策略和瓶颈优先)
int sptCount = (int) (populationSize * 0.15);
int eddsptCount = sptCount + (int) (populationSize * 0.15);
int eddCount = eddsptCount + (int) (populationSize * 0.15);
int bottleneckCount = eddCount + (int) (populationSize * 0.15);
int sstCount = bottleneckCount + (int) (populationSize * 0.15);
int crCount = sstCount + (int) (populationSize * 0.1);
int lptCount = crCount + (int) (populationSize * 0.1);
int lsCount = populationSize - lptCount;
IntStream.range(0, populationSize)
.parallel()
.forEach(i -> {
......@@ -364,23 +367,31 @@ chromo.setOrders(new CopyOnWriteArrayList<>(orders));
// SPT规则
chromo.setGenerateType("SPT");
generateChromosomeByType(chromo, 1);
} else if (i < lptCount) {
// LPT规则
chromo.setGenerateType("LPT");
generateChromosomeByType(chromo, 2);
} else if (i < eddsptCount) {
// EDD+SPT混合策略
chromo.setGenerateType("EDD+SPT");
generateEDDSPTChromosome(chromo);
} else if (i < eddCount) {
// EDD规则
chromo.setGenerateType("EDD");
generateChromosomeByType(chromo, 3);
} else if (i < bottleneckCount) {
// 瓶颈优先策略
chromo.setGenerateType("BottleneckFirst");
generateBottleneckFirstChromosome(chromo);
} else if (i < sstCount) {
// SST规则(已有物料排序)
chromo.setGenerateType("SST");
generateSSTChromosome(chromo);
} else if (i < crCount) {
// CR规则
chromo.setGenerateType("CR");
generateCRChromosome(chromo);
} else if (i < sstCount) {
// SST规则
chromo.setGenerateType("SST");
generateSSTChromosome(chromo);
}else {
} else if (i < lptCount) {
// LPT规则
chromo.setGenerateType("LPT");
generateChromosomeByType(chromo, 2);
} else {
// 随机规则
chromo.setGenerateType("LS");
generateLsChromosome(chromo);
......@@ -425,6 +436,7 @@ chromo.setOrders(new CopyOnWriteArrayList<>(orders));
if (considerSequence) {
sortedOps.sort(Comparator.comparing((Entry op) -> op.getPriority())
.thenComparing(Entry::getSequence)
.thenComparing(op -> op.getProductId() != null ? op.getProductId() : "") // 相同物料排在一起,减少换型
.thenComparing(op -> {
if (op.getMachineOptions() == null || op.getMachineOptions().isEmpty()) {
return sortType == 1 ? 0.0 : Double.MAX_VALUE; // 空值默认排最前/最后
......@@ -436,7 +448,7 @@ chromo.setOrders(new CopyOnWriteArrayList<>(orders));
}
else{
sortedOps.sort(Comparator.comparing((Entry op) -> op.getPriority())
.thenComparing(op -> op.getProductId() != null ? op.getProductId() : "") // 相同物料排在一起,减少换型
.thenComparing(op -> {
if (op.getMachineOptions() == null || op.getMachineOptions().isEmpty()) {
return sortType == 1 ? 0.0 : Double.MAX_VALUE; // 空值默认排最前/最后
......@@ -451,12 +463,13 @@ chromo.setOrders(new CopyOnWriteArrayList<>(orders));
if (considerSequence) {
sortedOps.sort(Comparator.comparing((Entry op) -> op.getPriority())
.thenComparing(Entry::getSequence)
.thenComparing(op -> op.getProductId() != null ? op.getProductId() : "") // 相同物料排在一起,减少换型
.thenComparing(op -> dueDateMap.get(op.getGroupId()),
Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(randomIds::get));
}else {
sortedOps.sort(Comparator.comparing((Entry op) -> op.getPriority())
.thenComparing(op -> op.getProductId() != null ? op.getProductId() : "") // 相同物料排在一起,减少换型
.thenComparing(op -> dueDateMap.get(op.getGroupId()),
Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(randomIds::get));
......@@ -551,6 +564,7 @@ chromo.setOrders(new CopyOnWriteArrayList<>(orders));
if (considerSequence) {
sortedOps.sort(Comparator.comparing((Entry op) -> -op.getPriority())
.thenComparing(Entry::getSequence)
.thenComparing(op -> op.getProductId() != null ? op.getProductId() : "") // 相同物料排在一起,减少换型
.thenComparing(op -> {
Order order = orderMap.get(op.getGroupId());
return calculateCriticalRatio(order, remainingProcessingTime.getOrDefault(op.getGroupId(), 0.0));
......@@ -558,7 +572,7 @@ chromo.setOrders(new CopyOnWriteArrayList<>(orders));
.thenComparing(randomIds::get));
}else {
sortedOps.sort(Comparator.comparing((Entry op) -> -op.getPriority())
.thenComparing(op -> op.getProductId() != null ? op.getProductId() : "") // 相同物料排在一起,减少换型
.thenComparing(op -> {
Order order = orderMap.get(op.getGroupId());
return calculateCriticalRatio(order, remainingProcessingTime.getOrDefault(op.getGroupId(), 0.0));
......@@ -747,6 +761,189 @@ chromo.setOrders(new CopyOnWriteArrayList<>(orders));
return loadCost + processingCost + setupCost;
}
/**
* EDD+SPT混合策略:先按截止日期,再按加工时间
*/
private Chromosome generateEDDSPTChromosome(Chromosome chromosome) {
List<Integer> ms = new ArrayList<>();
List<Integer> os = new ArrayList<>();
Map<Long, Double> machineLoad = new HashMap<>();
Random rnd = new Random();
List<GlobalOperationInfo> globalOpList = new ArrayList<>();
Map<Integer, LocalDateTime> dueDateMap = new HashMap<>();
for (Order order : orders) {
dueDateMap.put(order.getId(), order.getDueDate());
}
Map<Entry, Integer> randomIds = new HashMap<>();
List<Integer> indices = new ArrayList<>();
for (int i = 0; i < allOperations.size(); i++) {
indices.add(i);
}
Collections.shuffle(indices, rnd);
for (int i = 0; i < allOperations.size(); i++) {
randomIds.put(allOperations.get(i), indices.get(i));
}
List<Entry> sortedOps = new ArrayList<>(allOperations);
boolean considerSequence = rnd.nextBoolean();
if (considerSequence) {
sortedOps.sort(Comparator.comparing((Entry op) -> op.getPriority())
.thenComparing(Entry::getSequence)
.thenComparing(op -> dueDateMap.get(op.getGroupId()),
Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(op -> op.getProductId() != null ? op.getProductId() : "")
.thenComparing(op -> {
if (op.getMachineOptions() == null || op.getMachineOptions().isEmpty()) {
return 0.0;
}
return op.getMachineOptions().get(0).getProcessingTime() * op.getQuantity();
})
.thenComparing(randomIds::get));
} else {
sortedOps.sort(Comparator.comparing((Entry op) -> op.getPriority())
.thenComparing(op -> dueDateMap.get(op.getGroupId()),
Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(op -> op.getProductId() != null ? op.getProductId() : "")
.thenComparing(op -> {
if (op.getMachineOptions() == null || op.getMachineOptions().isEmpty()) {
return 0.0;
}
return op.getMachineOptions().get(0).getProcessingTime() * op.getQuantity();
})
.thenComparing(randomIds::get));
}
int globalOpId = 0;
for (Entry op : sortedOps) {
int groupId = op.getGroupId();
List<MachineOption> optionalMachines = op.getMachineOptions();
double minLoad = optionalMachines.stream()
.mapToDouble(m -> machineLoad.getOrDefault(m.getMachineId(), 0.0) + m.getProcessingTime())
.min()
.orElse(Double.MAX_VALUE);
List<MachineOption> minLoadMachines = optionalMachines.stream()
.filter(m -> machineLoad.getOrDefault(m.getMachineId(), 0.0) + m.getProcessingTime() == minLoad)
.collect(Collectors.toList());
MachineOption selectedMachine = minLoadMachines.get(rnd.nextInt(minLoadMachines.size()));
OptionalInt index = IntStream.range(0, optionalMachines.size())
.filter(i -> selectedMachine.getMachineId() == optionalMachines.get(i).getMachineId())
.findFirst();
int machineSeq = index.orElse(0) + 1;
ms.add(machineSeq);
machineLoad.put(selectedMachine.getMachineId(),
machineLoad.getOrDefault(selectedMachine.getMachineId(), 0.0) + selectedMachine.getProcessingTime());
os.add(groupId);
GlobalOperationInfo info = new GlobalOperationInfo();
info.setGlobalOpId(globalOpId);
info.setGroupId(op.getGroupId());
info.setSequence(op.getSequence());
info.setOp(op);
globalOpList.add(info);
globalOpId++;
}
chromosome.setGlobalOpList(globalOpList);
chromosome.setOperationSequencing(os);
chromosome.setMachineSelection(ms);
return chromosome;
}
/**
* 瓶颈优先策略:优先安排预计为瓶颈的设备上的工序
*/
private Chromosome generateBottleneckFirstChromosome(Chromosome chromosome) {
List<Integer> ms = new ArrayList<>();
List<Integer> os = new ArrayList<>();
Map<Long, Double> machineLoad = new HashMap<>();
Random rnd = new Random();
List<GlobalOperationInfo> globalOpList = new ArrayList<>();
Map<Entry, Integer> randomIds = new HashMap<>();
List<Integer> indices = new ArrayList<>();
for (int i = 0; i < allOperations.size(); i++) {
indices.add(i);
}
Collections.shuffle(indices, rnd);
for (int i = 0; i < allOperations.size(); i++) {
randomIds.put(allOperations.get(i), indices.get(i));
}
List<Entry> sortedOps = new ArrayList<>(allOperations);
Map<Long, Integer> machineOperationCount = new HashMap<>();
Map<Long, Double> machineTotalTime = new HashMap<>();
for (Entry op : allOperations) {
if (op.getMachineOptions() != null && !op.getMachineOptions().isEmpty()) {
for (MachineOption mo : op.getMachineOptions()) {
machineOperationCount.put(mo.getMachineId(),
machineOperationCount.getOrDefault(mo.getMachineId(), 0) + 1);
machineTotalTime.put(mo.getMachineId(),
machineTotalTime.getOrDefault(mo.getMachineId(), 0.0)
+ mo.getProcessingTime() * op.getQuantity());
}
}
}
sortedOps.sort(Comparator.comparing((Entry op) -> -op.getPriority())
.thenComparing((Entry op) -> {
if (op.getMachineOptions() == null || op.getMachineOptions().isEmpty()) {
return 0.0;
}
double maxTime = 0;
for (MachineOption mo : op.getMachineOptions()) {
double time = machineTotalTime.getOrDefault(mo.getMachineId(), 0.0);
if (time > maxTime) maxTime = time;
}
return -maxTime;
})
.thenComparing(op -> op.getProductId() != null ? op.getProductId() : "")
.thenComparing(randomIds::get));
int globalOpId = 0;
for (Entry op : sortedOps) {
int groupId = op.getGroupId();
List<MachineOption> optionalMachines = op.getMachineOptions();
double minLoad = optionalMachines.stream()
.mapToDouble(m -> machineLoad.getOrDefault(m.getMachineId(), 0.0) + m.getProcessingTime())
.min()
.orElse(Double.MAX_VALUE);
List<MachineOption> minLoadMachines = optionalMachines.stream()
.filter(m -> machineLoad.getOrDefault(m.getMachineId(), 0.0) + m.getProcessingTime() == minLoad)
.collect(Collectors.toList());
MachineOption selectedMachine = minLoadMachines.get(rnd.nextInt(minLoadMachines.size()));
OptionalInt index = IntStream.range(0, optionalMachines.size())
.filter(i -> selectedMachine.getMachineId() == optionalMachines.get(i).getMachineId())
.findFirst();
int machineSeq = index.orElse(0) + 1;
ms.add(machineSeq);
machineLoad.put(selectedMachine.getMachineId(),
machineLoad.getOrDefault(selectedMachine.getMachineId(), 0.0) + selectedMachine.getProcessingTime());
os.add(groupId);
GlobalOperationInfo info = new GlobalOperationInfo();
info.setGlobalOpId(globalOpId);
info.setGroupId(op.getGroupId());
info.setSequence(op.getSequence());
info.setOp(op);
globalOpList.add(info);
globalOpId++;
}
chromosome.setGlobalOpList(globalOpList);
chromosome.setOperationSequencing(os);
chromosome.setMachineSelection(ms);
return chromosome;
}
/**
* 随机
*/
......@@ -768,6 +965,7 @@ chromo.setOrders(new CopyOnWriteArrayList<>(orders));
randomIds.put(allOperations.get(i), indices.get(i));
}
sortedOps.sort(Comparator.comparing((Entry op) -> -op.getPriority())
.thenComparing(op -> op.getProductId() != null ? op.getProductId() : "") // 相同物料排在一起,减少换型
.thenComparing(randomIds::get));
......
......@@ -149,10 +149,13 @@ public class MaterialRequirementService {
if(MaterialRequirements!=null)
{
List<Entry> Operations= _allOperations.stream()
List<Entry> Operations= _allOperations.stream().filter(t -> t.getOrderId() != null)
.filter(t->t.getOrderId().equals(orderId))
.collect(Collectors.toList());
if(Operations==null)
{
int i=1;
}
for (Entry operation : Operations) {
List<Routingsupporting> MaterialRequirement_entrys = routingsupportings.stream()
.filter(t -> t.getRoutingDetailId().equals(operation.getRoutingDetailId()))
......@@ -524,7 +527,10 @@ public class MaterialRequirementService {
for (Integer groupId : operationSequencing)
{
Order demand=orders.stream().filter(t->t.getId()==groupId).findFirst().orElse(null);
if(demand==null)
{
continue;
}
if (demand.getFinishOrderId() != null && !demand.getFinishOrderId().isEmpty()) {
continue;
}
......@@ -606,7 +612,7 @@ public class MaterialRequirementService {
List<Entry> _allOperations= chromosome.getAllOperations();
List<Entry> Operations= _allOperations.stream()
List<Entry> Operations= _allOperations.stream().filter(t -> t.getOrderId() != null)
.filter(t->t.getOrderId().equals(childorderId))
.collect(Collectors.toList());
......
......@@ -232,6 +232,7 @@ public class RoutingDataService {
entry.setProductCode(order.getMaterialCode());
entry.setProductName(order.getMaterialName());
entry.setPriority(order.getActualPriority());
order.setId(entry.getGroupId());
}
List<ProdEquipment> Equipments = ProdEquipments.stream()
......
......@@ -106,13 +106,13 @@ public class SimulatedAnnealing {
// log("模拟退火+爬山法 - 初始化解码完成");
// 初始化温度
// 初始化温度(优化:更快收敛)
double temperature = 100.0;
double coolingRate = 0.95;
double temperatureThreshold = 1.0;
int maxIterations = 300;
double coolingRate = 0.90; // 优化:降温更快
double temperatureThreshold = 5.0; // 优化:温度阈值更高
int maxIterations = 100; // 优化:从300减少到100次
int noImproveCount = 0;
int maxNoImprove = 30; // 降低最大无改进次数
int maxNoImprove = 15; // 优化:从30减少到15次
// 新增:改进率监控参数
int stagnantWindow = 15; // 观察窗口大小
......@@ -239,13 +239,13 @@ public class SimulatedAnnealing {
// log("模拟退火+爬山法 - 初始化解码完成");
// 初始化温度
// 初始化温度(优化:更快收敛)
double temperature = 100.0;
double coolingRate = 0.95;
double temperatureThreshold = 1.0;
int maxIterations = 300;
double coolingRate = 0.90; // 优化:降温更快
double temperatureThreshold = 5.0; // 优化:温度阈值更高
int maxIterations = 80; // 优化:从300减少到80次
int noImproveCount = 0;
int maxNoImprove = 20; // 降低最大无改进次数,更快提前结束
int maxNoImprove = 10; // 优化:从20减少到10次
// 新增:改进率监控参数
int stagnantWindow = 10; // 观察窗口大小
......@@ -361,10 +361,17 @@ public class SimulatedAnnealing {
StringBuilder sb = new StringBuilder("模拟退火 - 改进详情: 迭代" + iteration + ", ");
double[] currentFitness = best.getFitnessLevel();
for (int i = 0; i < currentFitness.length; i++) {
// 处理null或空数组的情况
if (currentFitness != null && currentFitness.length > 0 &&
initialFitnessLevel != null && initialFitnessLevel.length > 0) {
int minLength = Math.min(currentFitness.length, initialFitnessLevel.length);
for (int i = 0; i < minLength; i++) {
double improvement = currentFitness[i] - initialFitnessLevel[i];
sb.append(String.format("KPI%d: %.4f→%.4f(+%.4f) ", i+1, initialFitnessLevel[i], currentFitness[i], improvement));
}
} else {
sb.append("(KPI数据不可用) ");
}
double totalImprovement = best.getFitness() - initialFitness;
sb.append(String.format("总Fitness: %.4f→%.4f(+%.4f)", initialFitness, best.getFitness(), totalImprovement));
......@@ -392,11 +399,18 @@ public class SimulatedAnnealing {
sb.append(String.format("总迭代%d次, 成功改进%d次, 改进率%.2f%%. ",
totalIterations, improveCount, totalIterations > 0 ? (double)improveCount / totalIterations * 100 : 0));
for (int i = 0; i < currentFitness.length; i++) {
// 处理null或空数组的情况
if (currentFitness != null && currentFitness.length > 0 &&
initialFitnessLevel != null && initialFitnessLevel.length > 0) {
int minLength = Math.min(currentFitness.length, initialFitnessLevel.length);
for (int i = 0; i < minLength; i++) {
double improvement = currentFitness[i] - initialFitnessLevel[i];
sb.append(String.format("KPI%d: %.4f→%.4f(%.2f%%) ", i+1, initialFitnessLevel[i], currentFitness[i],
initialFitnessLevel[i] > 0 ? improvement / initialFitnessLevel[i] * 100 : 0));
}
} else {
sb.append("(KPI数据不可用) ");
}
double totalImprovement = best.getFitness() - initialFitness;
sb.append(String.format("总Fitness: %.4f→%.4f(%.2f%%)", initialFitness, best.getFitness(),
......@@ -408,10 +422,13 @@ public class SimulatedAnnealing {
private void writeKpi(Chromosome chromosome) {
String fitness = "";
double[] fitness1 = chromosome.getFitnessLevel();
if (fitness1 != null) {
for (int i = 0; i < fitness1.length; i++) {
fitness += fitness1[i] + ",";
}
} else {
fitness = "null (未计算)";
}
log(String.format("模拟退火 - kpi:%s", fitness),true);
......
......@@ -65,7 +65,7 @@ public class TabuSearch {
Chromosome current = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
Chromosome best = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
this.bestFitness = best.getFitnessLevel().clone();
writeKpi(best);
writeKpi(best);
// 记录初始KPI用于计算改进率
double[] initialFitnessLevel = best.getFitnessLevel().clone();
double initialFitness = best.getFitness();
......@@ -73,8 +73,8 @@ writeKpi(best);
int iterations = 0;
int improveCount = 0;
int noImprovementCount = 0;
int maxNoImprovement = 15; // 降低到15次无改进则停止
int maxIterations = Math.min(cachedAllOperations.size(), 80); // 降低到最多80次
int maxNoImprovement = 5; // 优化:5次无改进则停止
int maxIterations = Math.min(cachedAllOperations.size(), 20); // 优化:最多20次迭代
// 改进率监控
int stagnantWindow = 10;
......@@ -213,12 +213,15 @@ writeKpi(best);
private void writeKpi(Chromosome chromosome) {
String fitness = "";
double[] fitness1 = chromosome.getFitnessLevel();
if (fitness1 != null) {
for (int i = 0; i < fitness1.length; i++) {
fitness += fitness1[i] + ",";
}
} else {
fitness = "null (未计算)";
}
log(String.format("变邻域搜索 - kpi:%s", fitness),true);
log(String.format("禁忌搜索 - kpi:%s", fitness),true);
}
/**
......@@ -228,10 +231,17 @@ writeKpi(best);
StringBuilder sb = new StringBuilder("禁忌搜索 - 改进详情: 迭代" + iteration + ", ");
double[] currentFitness = best.getFitnessLevel();
for (int i = 0; i < currentFitness.length; i++) {
// 处理null或空数组的情况
if (currentFitness != null && currentFitness.length > 0 &&
initialFitnessLevel != null && initialFitnessLevel.length > 0) {
int minLength = Math.min(currentFitness.length, initialFitnessLevel.length);
for (int i = 0; i < minLength; i++) {
double improvement = currentFitness[i] - initialFitnessLevel[i];
sb.append(String.format("KPI%d: %.4f→%.4f(+%.4f) ", i+1, initialFitnessLevel[i], currentFitness[i], improvement));
}
} else {
sb.append("(KPI数据不可用) ");
}
double totalImprovement = best.getFitness() - initialFitness;
sb.append(String.format("总Fitness: %.4f→%.4f(+%.4f)", initialFitness, best.getFitness(), totalImprovement));
......@@ -260,11 +270,18 @@ writeKpi(best);
sb.append(String.format("总迭代%d次, 成功改进%d次, 改进率%.2f%%. ",
totalIterations, improveCount, totalIterations > 0 ? (double)improveCount / totalIterations * 100 : 0));
for (int i = 0; i < currentFitness.length; i++) {
// 处理null或空数组的情况
if (currentFitness != null && currentFitness.length > 0 &&
initialFitnessLevel != null && initialFitnessLevel.length > 0) {
int minLength = Math.min(currentFitness.length, initialFitnessLevel.length);
for (int i = 0; i < minLength; i++) {
double improvement = currentFitness[i] - initialFitnessLevel[i];
sb.append(String.format("KPI%d: %.4f→%.4f(%.2f%%) ", i+1, initialFitnessLevel[i], currentFitness[i],
initialFitnessLevel[i] > 0 ? improvement / initialFitnessLevel[i] * 100 : 0));
}
} else {
sb.append("(KPI数据不可用) ");
}
double totalImprovement = best.getFitness() - initialFitness;
sb.append(String.format("总Fitness: %.4f→%.4f(%.2f%%)", initialFitness, best.getFitness(),
......
......@@ -19,36 +19,47 @@ import java.util.stream.Collectors;
public class VariableNeighborhoodSearch {
private final Random rnd = new Random();
// ==================== 常量定义 ====================
private static final int MAX_CONSECUTIVE_SAME_STRATEGY = 3;
private static final int MAX_NEIGHBORHOODS = 7;
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 int MOVE_STEPS_MIN = 1;
private static final int MOVE_STEPS_MAX = 5;
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 double LOW_SUCCESS_RATE_THRESHOLD = 0.1;
private static final int LOW_SUCCESS_RATE_MIN_TRIALS = 10;
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 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 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 double PRIORITY_EPSILON = 0.001;
private static final int MIN_USE_COUNT_FOR_EXPLORATION_BONUS = 5;
// ==================== 策略选择与轮换参数 ====================
private static final int MAX_CONSECUTIVE_SAME_STRATEGY = 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 = 30; // 每次邻域搜索最大尝试次数,减少无效搜索
private static final int NEIGHBOR_SELECTION_TOP_K = 3; // 每次选择K个候选工序进行操作
// ==================== 工序移动参数 ====================
private static final int MOVE_STEPS_MIN = 1; // 工序移动最小步数
private static final int MOVE_STEPS_MAX = 5; // 工序移动最大步数
private static final int NON_BOTTLENECK_MOVE_STEPS_MAX = 3; // 非瓶颈设备工序移动最大步数
// ==================== 随机邻域参数 ====================
private static final int RANDOM_NEIGHBOR_TYPE_COUNT = 3; // 随机邻域尝试次数
// ==================== 策略成功率评分参数 ====================
private static final double INITIAL_SUCCESS_RATE = 0.3; // 新策略的初始成功率(未使用时的默认值)
private static final double LOW_SUCCESS_RATE_THRESHOLD = 0.1; // 低成功率阈值,低于此值需要额外探索奖励
private static final int LOW_SUCCESS_RATE_MIN_TRIALS = 10; // 低成功率判断的最小尝试次数
private static final double EXPLORATION_BONUS_FOR_LOW_USE = 0.15; // 低使用次数策略的探索奖励分
private static final double EXPLORATION_BONUS_FOR_LOW_SUCCESS = 0.1; // 低成功率策略的探索奖励分
private static final double RANDOM_NOISE_FACTOR = 0.1; // 策略排序时的随机扰动因子,避免陷入局部最优
private static final double FORCE_ROTATE_PENALTY = 0.5; // 强制轮换时的惩罚系数
// ==================== 瓶颈设备评分参数 ====================
private static final double PERCENTILE_90 = 0.90; // 计算瓶颈评分时使用的90分位数,避免极端值影响
private static final double BOTTLENECK_MACHINE_BASE_SCORE = 10000; // 瓶颈设备工序的基础评分,确保排在前面
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;
......@@ -378,10 +389,17 @@ public class VariableNeighborhoodSearch {
StringBuilder sb = new StringBuilder("变邻域搜索 - 改进详情: 轮次" + round + ", 邻域=" + neighborhoodName + ", ");
double[] currentFitness = best.getFitnessLevel();
for (int i = 0; i < currentFitness.length; i++) {
// 处理null或空数组的情况
if (currentFitness != null && currentFitness.length > 0 &&
initialFitnessLevel != null && initialFitnessLevel.length > 0) {
int minLength = Math.min(currentFitness.length, initialFitnessLevel.length);
for (int i = 0; i < minLength; i++) {
double improvement = currentFitness[i] - initialFitnessLevel[i];
sb.append(String.format("KPI%d: %.4f→%.4f(+%.4f) ", i+1, initialFitnessLevel[i], currentFitness[i], improvement));
}
} else {
sb.append("(KPI数据不可用) ");
}
double totalImprovement = best.getFitness() - initialFitness;
sb.append(String.format("总Fitness: %.4f→%.4f(+%.4f)", initialFitness, best.getFitness(), totalImprovement));
......@@ -409,11 +427,17 @@ public class VariableNeighborhoodSearch {
sb.append("\n");
// 记录KPI改进
for (int i = 0; i < currentFitness.length; i++) {
if (currentFitness != null && currentFitness.length > 0 &&
initialFitnessLevel != null && initialFitnessLevel.length > 0) {
int minLength = Math.min(currentFitness.length, initialFitnessLevel.length);
for (int i = 0; i < minLength; i++) {
double improvement = currentFitness[i] - initialFitnessLevel[i];
sb.append(String.format("KPI%d: %.4f→%.4f(%.2f%%) ", i+1, initialFitnessLevel[i], currentFitness[i],
initialFitnessLevel[i] > 0 ? improvement / initialFitnessLevel[i] * 100 : 0));
}
} else {
sb.append("(KPI数据不可用) ");
}
double totalImprovement = best.getFitness() - initialFitness;
sb.append(String.format("总Fitness: %.4f→%.4f(%.2f%%)", initialFitness, best.getFitness(),
......@@ -424,10 +448,13 @@ public class VariableNeighborhoodSearch {
private void writeKpi(Chromosome chromosome) {
String fitness = "";
double[] fitness1 = chromosome.getFitnessLevel();
if (fitness1 != null) {
for (int i = 0; i < fitness1.length; i++) {
fitness += fitness1[i] + ",";
}
} else {
fitness = "null (未计算)";
}
log(String.format("变邻域搜索 - kpi:%s", fitness),true);
......@@ -713,9 +740,9 @@ public class VariableNeighborhoodSearch {
if (allResults != null && !allResults.isEmpty()) {
List<BottleneckOperationAnalysis> bottleneckAnalyses = analyzeBottleneckOperations(allResults, bottleneckMachineId);
// 输出瓶颈工序TOP 5
log("变邻域搜索 - ===== 瓶颈工序TOP 5 (按瓶颈评分) =====");
for (int i = 0; i < Math.min(5, bottleneckAnalyses.size()); i++) {
// 输出瓶颈工序TOP
log(String.format("变邻域搜索 - ===== 瓶颈工序TOP %d (按瓶颈评分) =====", TOP_MACHINES_TO_LOG));
for (int i = 0; i < Math.min(TOP_MACHINES_TO_LOG, bottleneckAnalyses.size()); i++) {
BottleneckOperationAnalysis boa = bottleneckAnalyses.get(i);
GAScheduleResult op = boa.operation;
log(String.format(
......@@ -729,7 +756,7 @@ public class VariableNeighborhoodSearch {
// 更新关键工序为识别出的瓶颈工序
if (criticalOps != null && !criticalOps.isEmpty() && !bottleneckAnalyses.isEmpty()) {
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)
.collect(Collectors.toList());
analysis.put("criticalOps", topBottlenecks);
......@@ -853,8 +880,8 @@ public class VariableNeighborhoodSearch {
Collections.sort(machineWaitTimes);
// 使用90分位数代替最大值,避免极端值影响
double p90WaitTime = Math.max(1, getPercentile(waitTimes, 0.90)); // 防止0导致NaN
double p90MachineWaitTime = Math.max(1, getPercentile(machineWaitTimes, 0.90)); // 防止0导致NaN
double p90WaitTime = Math.max(1, getPercentile(waitTimes, PERCENTILE_90)); // 防止0导致NaN
double p90MachineWaitTime = Math.max(1, getPercentile(machineWaitTimes, PERCENTILE_90)); // 防止0导致NaN
// 优先找出瓶颈设备上的工序
List<BottleneckOperationAnalysis> bottleneckMachineOps = new ArrayList<>();
......@@ -876,42 +903,42 @@ public class VariableNeighborhoodSearch {
if (isOnBottleneckMachine) {
// 瓶颈设备上的工序:基础分极高,确保排在最前面
score += 10000; // 提高到10000,绝对保证排在前面
score += BOTTLENECK_MACHINE_BASE_SCORE; // 绝对保证排在前面
// 在瓶颈设备工序内部进行排序(使用截断值)
// 工序等待时间 (权重 30%)
double normalizedWaitTime = Math.min(boa.waitTime / p90WaitTime, 1.5);
score += normalizedWaitTime * 30;
// 工序等待时间
double normalizedWaitTime = Math.min(boa.waitTime / p90WaitTime, NORMALIZATION_CAP);
score += normalizedWaitTime * WAIT_TIME_WEIGHT;
// 设备等待时间 (权重 30%)
double normalizedMachineWaitTime = Math.min(boa.machineWaitTime / p90MachineWaitTime, 1.5);
score += normalizedMachineWaitTime * 30;
// 设备等待时间
double normalizedMachineWaitTime = Math.min(boa.machineWaitTime / p90MachineWaitTime, NORMALIZATION_CAP);
score += normalizedMachineWaitTime * MACHINE_WAIT_TIME_WEIGHT;
// 关键路径评分 (权重 25%)
// 关键路径评分
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 {
// 非瓶颈设备上的工序:基础分低
score += 0;
// 使用截断值,避免极端值影响
double normalizedMachineWaitTime = Math.min(boa.machineWaitTime / p90MachineWaitTime, 1.5);
score += normalizedMachineWaitTime * 30;
double normalizedMachineWaitTime = Math.min(boa.machineWaitTime / p90MachineWaitTime, NORMALIZATION_CAP);
score += normalizedMachineWaitTime * MACHINE_WAIT_TIME_WEIGHT;
double normalizedWaitTime = Math.min(boa.waitTime / p90WaitTime, 1.5);
score += normalizedWaitTime * 20;
double normalizedWaitTime = Math.min(boa.waitTime / p90WaitTime, NORMALIZATION_CAP);
score += normalizedWaitTime * WAIT_TIME_WEIGHT;
// 关键路径评分 (权重 20%)
// 关键路径评分
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;
......@@ -921,12 +948,12 @@ public class VariableNeighborhoodSearch {
log(String.format("变邻域搜索 - 瓶颈设备ID=%d, 其上工序数=%d",
bottleneckMachineId, bottleneckMachineOps.size()));
// 输出各设备工序数量统计(前5个)
// 输出各设备工序数量统计
List<Map.Entry<Long, Long>> topMachinesByCount = machineOpCount.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue()))
.limit(5)
.limit(TOP_MACHINES_TO_LOG)
.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) {
machineCountStr.append(String.format("[%d:%d] ", entry.getKey(), entry.getValue()));
}
......@@ -967,37 +994,37 @@ public class VariableNeighborhoodSearch {
// 按成功率排序,但加入随机扰动和探索机制
strategies.sort((a, b) -> {
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) strategySuccessCount[b] / strategyTotalCount[b] : 0.3;
(double) strategySuccessCount[b] / strategyTotalCount[b] : INITIAL_SUCCESS_RATE;
// 强制轮换:给最后使用的策略降权
if (needForceRotate && a == lastUsedStrategy) {
successRateA -= 0.5; // 大幅降权
successRateA -= FORCE_ROTATE_PENALTY; // 大幅降权
}
if (needForceRotate && b == lastUsedStrategy) {
successRateB -= 0.5;
successRateB -= FORCE_ROTATE_PENALTY;
}
// 给使用次数少的策略加分,鼓励探索
if (strategyTotalCount[a] < 5) {
successRateA += 0.3;
if (strategyTotalCount[a] < MIN_USE_COUNT_FOR_EXPLORATION_BONUS) {
successRateA += EXPLORATION_BONUS_FOR_LOW_USE;
}
if (strategyTotalCount[b] < 5) {
successRateB += 0.3;
if (strategyTotalCount[b] < MIN_USE_COUNT_FOR_EXPLORATION_BONUS) {
successRateB += EXPLORATION_BONUS_FOR_LOW_USE;
}
// 如果策略被尝试次数很多但成功率很低,给它额外加分鼓励探索
if (strategyTotalCount[a] > 10 && successRateA < 0.1) {
successRateA += 0.2;
if (strategyTotalCount[a] > LOW_SUCCESS_RATE_MIN_TRIALS && successRateA < LOW_SUCCESS_RATE_THRESHOLD) {
successRateA += EXPLORATION_BONUS_FOR_LOW_SUCCESS;
}
if (strategyTotalCount[b] > 10 && successRateB < 0.1) {
successRateB += 0.2;
if (strategyTotalCount[b] > LOW_SUCCESS_RATE_MIN_TRIALS && successRateB < LOW_SUCCESS_RATE_THRESHOLD) {
successRateB += EXPLORATION_BONUS_FOR_LOW_SUCCESS;
}
// 加入少量随机扰动,避免陷入局部最优
successRateA += rnd.nextDouble() * 0.15;
successRateB += rnd.nextDouble() * 0.15;
successRateA += rnd.nextDouble() * RANDOM_NOISE_FACTOR;
successRateB += rnd.nextDouble() * RANDOM_NOISE_FACTOR;
return Double.compare(successRateB, successRateA);
});
......
......@@ -199,9 +199,14 @@ public class PlanResultService {
for (Machine machine : machines){
Long machineId=machine.getId();
if(machineId==2406)
{
int i=0;
}
if(machineIds.get(machineId)!=null&&machineIds.get(machineId)>3600*24*200) {
int day = (int) (machineIds.get(machineId) / 3600 / 24)+100;
// day=Math.min(day,400);
List<TimeSegment> segments = machineScheduler.generateTimeSegment(machine, null, day);
machineScheduler.addSegmentsWithDeduplication(machine, segments);
}
......
......@@ -163,7 +163,7 @@ public class SceneService {
sceneChromsome.getSceneDetails().add(sceneDetail);
// redisUtils.set("SceneId."+sceneId,sceneChromsome);
redisUtils.set("SceneId."+sceneId,sceneChromsome);
File file = getChromosomeFile(sceneId,sceneChromsome.getVersion().toString());
......
......@@ -39,8 +39,11 @@ public class PlanResultServiceTest {
// TestSortService sortService=new TestSortService();
// sortService.test1();
// nsgaiiUtils.Test();
planResultService.execute2("08B1D87FE1B84ECDBAC2E546DDB6FB81");//2000
// planResultService.execute2("0428340BB4F540938F1FB5599F03E8A4");//2000
planResultService.execute2("E29F2B3ADA8149F6B916B5119296A92B");//2000
// planResultService.execute2("08B1D87FE1B84ECDBAC2E546DDB6FB81");//2000
// planResultService.execute2("0428340BB4F540938F1FB5599F03E8A4");//5000
// planResultService.execute2("C8B533BD8944405B9A2F8823C575C204");//500
// planResultService.execute2("EFDD34E4B5BC434BAEAE6A84DFCD4E7B");//20
// planResultService.execute2("00E0C5D3E4AD4F36B56C39395906618D");
......
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