Commit 7c25b0fe authored by Tong Li's avatar Tong Li

优化

parent 8e292cd5
......@@ -183,9 +183,9 @@ public class HybridAlgorithm {
best = _simulatedAnnealing.search(best, _tabuSearch, _vns, sharedDecoder, machines);
best = _vns.search(best, sharedDecoder, machines);
best = _vns.search(best,_tabuSearch, sharedDecoder, machines);
best = _tabuSearch.search(best, _vns, sharedDecoder, machines);
// best = _tabuSearch.search(best, _vns, sharedDecoder, machines);
return getBestChromosome(best, param.getBaseTime(), starttime);
......@@ -260,8 +260,8 @@ public class HybridAlgorithm {
// }
// 核心融合链(工业级标准顺序:GA生成子代 → SA跳坑 → VNS扩邻域 → TS精优化)
child = _simulatedAnnealing.search(child,_tabuSearch,_vns, sharedDecoder, machines);
child = _vns.search(child, sharedDecoder, machines);
child = _tabuSearch.search(child, _vns,sharedDecoder, machines);
child = _vns.search(child,_tabuSearch, sharedDecoder, machines);
// child = _tabuSearch.search(child, _vns,sharedDecoder, machines);
newPopulation.add(child);
......
package com.aps.service.Algorithm;
import com.aps.common.util.FileHelper;
import com.aps.common.util.GlobalCacheUtil;
import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.*;
import com.aps.entity.Algorithm.IDAndChildID.GroupResult;
......@@ -9,515 +8,279 @@ import com.aps.entity.basic.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
/**
* 禁忌搜索算法
* 禁忌搜索算法:负责禁忌表的生命周期与搜索主循环。
*
* - VNS / SA 通过本类的公共方法 {@link #isTabu(String)}、{@link #addToTabuList(String)}
* 来访问禁忌表,避免各自维护一套禁忌逻辑。
* - 同时也可以作为独立阶段(由 HybridAlgorithm 调用 {@link #search}) 执行完整的禁忌搜索优化。
*
* 核心思想:
* 1) 三粒度禁忌 key:machineStr(机器分配整体) / operationStr(工序排序整体) / geneStr(完整编码)
* 2) 渴望准则:若邻居优于 best,则无论是否命中禁忌都接受
* 3) 劣解概率接受:0.45 -> 0.05,随迭代递减
* 4) 时间预算:避免在大规模问题上跑太久
*
* 作者:佟礼
*/
public class TabuSearch {
// ==================== 改进判断参数 ====================
// 注意:4000+ 工序问题中,每次 fitness 提升量级约为 1e-5~1e-7,
// 降低阈值使得"显著改进"能被真实触发。
private static final double SIGNIFICANT_IMPROVEMENT_THRESHOLD = 5e-7; // 显著改进阈值:相对 currentBestFitness 提升 5e-7 即可清零计数
private static final double MINOR_IMPROVEMENT_THRESHOLD = 1e-10; // 微小改进阈值:任何正向改进都算突破停滞
// ==================== 改进判断参数(public,供 VNS / SA 共享) ====================
public static final double SIGNIFICANT_IMPROVEMENT_THRESHOLD = 1e-11; // 显著改进阈值
public static final double MINOR_IMPROVEMENT_THRESHOLD = 0.0; // 微小改进阈值(any positive improvement)
// ==================== TS 独立的邻域/禁忌控制 ====================
// 关键:TS 不共享 VNS 的频率统计,避免与 SA/VNS 在相同搜索空间反复搜索
// ==================== 劣解接受概率(public,供 VNS 共享) ====================
public static final double WORSE_ACCEPT_PROB_START = 0.45;
public static final double WORSE_ACCEPT_PROB_MIN = 0.05;
// ==================== 时间预算(public,供 VNS 共享) ====================
public static final long TS_TIME_BUDGET_MS = 25L * 60L * 1000L;
public static final long TS_PER_ITER_BUDGET_MS = 17L * 1000L;
// ==================== 依赖项 ====================
private final Map<Long, Integer> tsBottleneckMachineFrequency = new HashMap<>();
private final Random tsRnd = new Random(20260622L);
// 连续使用同一 VNS 策略次数计数,用于强制策略多样化
private int tsConsecutiveSameStrategyCount = 0;
private int tsLastStrategyIndex = -1;
private void log(String message) {
log(message, false);
}
private final List<Entry> cachedAllOperations;
private final FitnessCalculator fitnessCalculator;
private void log(String message, boolean enableLogging) {
if (enableLogging) {
FileHelper.writeLogFile(message);
}
// ==================== 禁忌表:List + HashSet 组合 ====================
private final List<String> tabuList = new ArrayList<>();
private final Set<String> tabuSet = new HashSet<>();
private final int tabuListSize;
public TabuSearch(List<Entry> allOperations, List<Order> orders,
TreeMap<String, Material> materials, List<GroupResult> entryRel,
FitnessCalculator fitnessCalculator) {
this.fitnessCalculator = fitnessCalculator;
this.cachedAllOperations = ProductionDeepCopyUtil.deepCopyList(
new CopyOnWriteArrayList<>(allOperations), Entry.class);
// 禁忌长度随问题规模自适应,避免太小/太大
this.tabuListSize = Math.min(150, Math.max(50, allOperations.size() / 30));
}
private FitnessCalculator fitnessCalculator;
// 禁忌表:以 machineStr(机器选择)为一级粒度,辅以 operationStr 二级 key,避免完整 geneStr 几乎不重复导致禁忌失效
// 使用 List + HashSet 组合:List 维护 FIFO 顺序,Set 提供 O(1) 命中检查
private List<String> tabuList;
private Set<String> tabuSet;
private int tabuListSize = 80;
private List<Machine> cachedMachines;
private List<Order> cachedOrders;
private List<GroupResult> cachedEntryRel;
private TreeMap<String, Material> cachedMaterials;
private List<Entry> cachedAllOperations;
// 渴望准则:记录最优解的 fitness
private double[] bestFitness;
// ====================================================================
// 公共方法:供 VNS / SA 调用,用于共享禁忌表
// ====================================================================
public TabuSearch(List<Entry> allOperations, List<Order> orders,
TreeMap<String, Material> materials,List<GroupResult> entryRel, FitnessCalculator _fitnessCalculator) {
/**
* 判断 key 是否在禁忌表中。
*/
public boolean isTabu(String key) {
if (key == null) return false;
return tabuSet.contains(key);
}
this.tabuList = new ArrayList<>();
this.tabuSet = new HashSet<>();
// 工序越多,禁忌表越长;对于 4000+ 工序的问题,禁忌长度需要更大
this.tabuListSize = Math.min(120, Math.max(40, allOperations.size() / 30));
fitnessCalculator=_fitnessCalculator;
/**
* 添加 key 到禁忌表(FIFO 策略)。
*/
public void addToTabuList(String key) {
if (key == null) return;
if (tabuSet.contains(key)) return;
tabuList.add(key);
tabuSet.add(key);
while (tabuList.size() > tabuListSize) {
String removed = tabuList.remove(0);
tabuSet.remove(removed);
}
}
// 预缓存解码需要的深拷贝列表,避免重复拷贝
cachedAllOperations = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(allOperations), Entry.class);
cachedOrders = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(orders), Order.class);
cachedEntryRel = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(entryRel), GroupResult.class);
cachedMaterials = ProductionDeepCopyUtil.deepCopyTreeMap(materials, String.class, Material.class);
/**
* 批量把 Chromosome 的三层粒度 key 加入禁忌表。
*/
public void addChromosomeToTabu(Chromosome c) {
if (c == null) return;
if (c.getMachineStr() != null) addToTabuList(c.getMachineStr());
if (c.getOperationStr() != null) addToTabuList(c.getOperationStr());
if (c.getGeneStr() != null) addToTabuList(c.getGeneStr());
}
/**
* 判断 Chromosome 是否命中任意一层禁忌。
*/
public boolean isChromosomeTabu(Chromosome c) {
if (c == null) return false;
if (c.getMachineStr() != null && isTabu(c.getMachineStr())) return true;
if (c.getOperationStr() != null && isTabu(c.getOperationStr())) return true;
if (c.getGeneStr() != null && isTabu(c.getGeneStr())) return true;
return false;
}
// ====================================================================
// 搜索主循环(独立阶段)
// ====================================================================
/**
* 禁忌搜索(优化版)
* 独立的禁忌搜索阶段(从 HybridAlgorithm 调用)。
*
* @param chromosome 初始解
* @param vns 提供邻域生成、瓶颈感知策略
* @param decoder 解码与调度
* @param machines 机器列表
*/
public Chromosome search(Chromosome chromosome, VariableNeighborhoodSearch vns,
GeneticDecoder decoder, List<Machine> machines) {
log("禁忌搜索 - 开始执行",true);
FileHelper.writeLogFile("禁忌搜索(融合版) - 开始执行");
Chromosome current = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
// decoder.DelOrder(current);
Chromosome best = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
this.bestFitness = best.getFitnessLevel().clone();
writeKpi(best);
// 记录初始KPI用于计算改进率
double[] initialFitnessLevel = best.getFitnessLevel().clone();
double initialFitness = best.getFitness();
double currentBestFitness = best.getFitness();
int iterations = 0;
int improveCount = 0;
int significantImproveCount = 0;
int noImprovementCount = 0;
// 优化:从 8 提升到 15,允许 TS 有机会在接受劣解后探索新空间
int maxNoImprovement = 15;
// 优化:从 20 提升到 60,确保 TS 有足够迭代跳出 SA/VNS 后的局部最优
int maxIterations = Math.min(Math.max(60, cachedAllOperations.size() / 50), 220);
// 改进率监控(放大窗口避免误判)
int stagnantWindow = 15;
int[] recentImprovements = new int[stagnantWindow];
double improvementRateThreshold = 0.05; // 5%改进率阈值
// 记录本次 TS 的 best fitness(用于判断是否有任何正向改进)
double currentBestFitness = best.getFitness();
// 时间预算驱动的 maxIterations
long tsStartTimeMs = System.currentTimeMillis();
long remainingBudgetMs = Math.max(5L * 60L * 1000L, TS_TIME_BUDGET_MS / 2);
int timeBasedMaxIter = (int) Math.max(30, remainingBudgetMs / TS_PER_ITER_BUDGET_MS);
int sizeBasedMaxIter = Math.max(60, cachedAllOperations.size() / 50);
int maxIterations = Math.min(Math.max(60, sizeBasedMaxIter), Math.max(150, timeBasedMaxIter));
log(String.format("禁忌搜索 - 参数:最大迭代=%d, 最大无改进=%d, 禁忌长度=%d",
maxIterations, maxNoImprovement, tabuListSize));
FileHelper.writeLogFile(String.format("禁忌搜索(融合版) - 参数: 最大迭代=%d, 禁忌长度=%d, 时间预算=%.1fmin",
maxIterations, tabuListSize, (double) remainingBudgetMs / 60000.0));
for (int i = 0; i < maxIterations; i++) {
iterations++;
decoder.DelOrder(current);
// ============= 策略多样化 =============
// 若连续 3 次使用同一策略(从 VNS 日志中可看到策略1常被高频重复使用),
// 则在此次迭代中强制调用 "非策略1" 的扰动:直接执行 reorderSingleOrderOperation / shiftOperationsForBottleneck。
// 否则仍使用 VNS 标准 generateNeighbor,同时保持 20% 的概率走随机邻域路径。
Chromosome neighbor = null;
boolean tryForceRotate = (tsConsecutiveSameStrategyCount >= 3) && (tsRnd.nextDouble() < 0.6);
if (tryForceRotate) {
// 从 VNS 中获取专门的重排序策略邻居(若 VNS 提供对应方法则直接调用;
// 若未提供则回落至标准 generateNeighbor,但在 VNS 中会智能选择策略)
try {
// 先尝试策略2/3的重排操作(通过直接调用 generateNeighbor),并在其后增加工序级的随机扰动
Chromosome base = vns.generateNeighbor(current);
if (base != null) {
// 对 base 再做一次工序级随机交换,强迫策略多样化,减少策略 1 的重复
neighbor = tryShuffleOperationPart(base);
}
} catch (Exception ignored) {
neighbor = vns.generateNeighbor(current);
}
// 策略轮换后清零连续计数
tsConsecutiveSameStrategyCount = 0;
}
// ---- 1) 生成邻域 ----
Chromosome neighbor = vns.generateNeighbor(current);
if (neighbor == null) {
neighbor = vns.generateNeighbor(current);
tsConsecutiveSameStrategyCount++;
}
if (neighbor == null) {
log("禁忌搜索 - 生成邻居失败,跳过");
noImprovementCount++;
if (iterations <= stagnantWindow) {
recentImprovements[iterations - 1] = 0;
}
continue;
}
// ============= 三级粒度禁忌 key =============
// 1) machineStr : 机器分配整体(粗粒度)
// 2) operationStr: 工序排序整体(中粒度,避免反复回到相同排序)
// 3) geneStr : 完整编码(严格粒度)
// 任意一项命中禁忌表都视为已访问过的邻域,跳过解码,大幅削减 17s/次 开销
String neighborMachineStr = neighbor.getMachineStr();
String neighborOpStr = neighbor.getOperationStr();
String neighborGeneStr = neighbor.getGeneStr();
String currentMachineStr = current.getMachineStr();
String currentGeneStr = current.getGeneStr();
boolean tabuHit = isTabu(neighborMachineStr)
|| isTabu(neighborOpStr)
|| isTabu(neighborGeneStr);
// 快速跳过:完全相同的编码
if (neighborGeneStr.equals(currentGeneStr)) {
addToTabuList(neighborMachineStr);
addToTabuList(neighborOpStr);
addToTabuList(neighborGeneStr);
noImprovementCount++;
if (iterations <= stagnantWindow) {
recentImprovements[iterations - 1] = 0;
}
if (iterations <= stagnantWindow) recentImprovements[iterations - 1] = 0;
continue;
}
// ============= 精英解码启发式 =============
// 若 machineStr 未发生变化(说明 VNS 本次只做了工序排序改动),
// 且工序排序与之前 visited 的 opStr 太接近(Hamming 距离小于最小阈值),
// 则可以在不解码的情况下有把握地丢弃该邻居,节省 ~17s 开销。
boolean skipDecodeByElite = false;
if (neighborMachineStr.equals(currentMachineStr)) {
// 机器分配没动,仅检查工序排序差异
int opDist = estimateHammingDistance(neighborOpStr, current.getOperationStr());
// 变化太少(小于 3 个位置差异),直接丢弃,不解码
if (opDist < 3) {
skipDecodeByElite = true;
}
}
if (skipDecodeByElite) {
addToTabuList(neighborMachineStr);
addToTabuList(neighborOpStr);
addToTabuList(neighborGeneStr);
// ---- 2) 禁忌检查 ----
boolean tabuHit = isChromosomeTabu(neighbor);
boolean sameAsCurrent = (neighbor.getGeneStr() != null
&& neighbor.getGeneStr().equals(current.getGeneStr()));
if (sameAsCurrent) {
addChromosomeToTabu(neighbor);
noImprovementCount++;
if (iterations <= stagnantWindow) {
recentImprovements[iterations - 1] = 0;
}
if (iterations <= stagnantWindow) recentImprovements[iterations - 1] = 0;
continue;
}
// 添加到禁忌表(三粒度 key)
addToTabuList(neighborMachineStr);
addToTabuList(neighborOpStr);
addToTabuList(neighborGeneStr);
// 真正的 isTabu 状态(用于下面的接受逻辑)
boolean isTabu = tabuHit;
boolean accept = false;
boolean isBetterThanBest = false;
boolean isBetterThanCurrent = false;
// 解码
// ---- 3) 解码 ----
decode(decoder, neighbor, machines);
addChromosomeToTabu(neighbor);
isBetterThanBest = isBetter(neighbor, best);
isBetterThanCurrent = isBetter(neighbor, current);
// ---- 4) 接受逻辑 ----
boolean betterThanBest = isBetter(neighbor, best);
boolean betterThanCurrent = isBetter(neighbor, current);
// ===== 改进的接受策略:符合标准 Tabu Search 语义 =====
// 1) 非禁忌 + 比 best 好 -> 直接接受
// 2) 禁忌但比 best 好(渴望准则)-> 接受
// 3) 非禁忌 + 比 current 好 -> 接受(继续沿好的方向走)
// 4) 非禁忌 + 比 current 差,但在 early-exit 之前 -> 以一定概率接受(有助于跳出局部最优)
if (!isTabu && isBetterThanBest) {
accept = true;
} else if (isTabu && isBetterThanBest) {
// 渴望准则:超过 best 就接受,不管禁忌
log("禁忌搜索 - 触发渴望准则,接受禁忌解");
accept = true;
} else if (!isTabu && isBetterThanCurrent) {
boolean accept;
if (betterThanBest) {
// 渴望准则:优于 best 无条件接受
accept = true;
} else if (!isTabu) {
// 非禁忌但劣解:以一定概率接受(模拟退火式的跳出机制)
// 概率随迭代递减,前期更激进,后期更保守
double acceptProb = Math.max(0.05, 0.45 * (1.0 - (double) iterations / maxIterations));
if (tsRnd.nextDouble() < acceptProb) {
} else if (!tabuHit && betterThanCurrent) {
accept = true;
}
} else if (!tabuHit) {
// 非禁忌但劣解:按递减概率接受
double progress = Math.min(1.0, (double) iterations / (double) Math.max(30, maxIterations));
double acceptProb = WORSE_ACCEPT_PROB_START -
(WORSE_ACCEPT_PROB_START - WORSE_ACCEPT_PROB_MIN) * progress;
accept = tsRnd.nextDouble() < acceptProb;
} else {
accept = false;
}
boolean improvedThisIteration = false;
if (accept) {
current = ProductionDeepCopyUtil.deepCopy(neighbor, Chromosome.class);
if (isBetterThanBest) {
best = ProductionDeepCopyUtil.deepCopy(current, Chromosome.class);
this.bestFitness = best.getFitnessLevel().clone();
writeKpi(best);
if (betterThanBest) {
best = ProductionDeepCopyUtil.deepCopy(neighbor, Chromosome.class);
improveCount++;
improvedThisIteration = true;
// 关键修复:与 best 比较而非初始 chromosome,避免微小改进永远无法清零 noImprovementCount
boolean isSignificant = isSignificantImprovement(best, initialFitnessLevel, currentBestFitness);
if (isSignificant) {
double delta = best.getFitness() - currentBestFitness;
if (delta > SIGNIFICANT_IMPROVEMENT_THRESHOLD) {
noImprovementCount = 0;
significantImproveCount++;
currentBestFitness = best.getFitness();
logTabuImprovement(best, initialFitnessLevel, initialFitness, iterations);
log(String.format("禁忌搜索 - 找到更好解(显著),迭代=%d, fitness=%.8f",
iterations, best.getFitness()), true);
FileHelper.writeLogFile(String.format("禁忌搜索(融合版) - 找到更好解(显著), 迭代=%d, fitness=%.12f",
iterations, best.getFitness()));
} else {
// 微小改进:先计算 delta,再决定清零/更新 currentBestFitness
double delta = best.getFitness() - currentBestFitness;
// 只要是严格正向改进,都清零计数,避免过早退出
if (delta > MINOR_IMPROVEMENT_THRESHOLD) {
noImprovementCount = 0;
currentBestFitness = best.getFitness();
}
log(String.format("禁忌搜索 - 找到更好解(微小),迭代=%d, fitness=%.10f, delta=%.2e, sig_threshold=%.2e",
iterations, best.getFitness(), delta, SIGNIFICANT_IMPROVEMENT_THRESHOLD), true);
FileHelper.writeLogFile(String.format("禁忌搜索(融合版) - 找到更好解(微小), 迭代=%d, fitness=%.12f, delta=%.2e",
iterations, best.getFitness(), delta));
}
} else if (isBetterThanCurrent) {
// 比 current 好但未超 best,算有进展,重置计数
noImprovementCount = Math.max(0, noImprovementCount - 2);
improvedThisIteration = true;
} else {
// 接受劣解以探索;不直接清零,但也不过度累加
noImprovementCount++;
}
} else {
// 不接受,无改进计数+1
noImprovementCount++;
if (iterations <= stagnantWindow) recentImprovements[iterations - 1] = 0;
}
if (iterations <= stagnantWindow) {
recentImprovements[iterations - 1] = improvedThisIteration ? 1 : 0;
}
// 每10次迭代输出一次状态
if (iterations % 10 == 0) {
log(String.format("禁忌搜索 - 迭代%d/%d, 改进数=%d, 无改进连续=%d, 改进率=%.2f%%",
iterations, maxIterations, improveCount, noImprovementCount,
iterations > 0 ? (double)improveCount / iterations * 100 : 0));
}
// 检查提前停止条件
boolean shouldStop = false;
String stopReason = "";
// ---- 5) 提前停止检查 ----
if (noImprovementCount >= maxNoImprovement) {
shouldStop = true;
stopReason = String.format("连续%d次无改进", maxNoImprovement);
} else if (iterations >= stagnantWindow && iterations % stagnantWindow == 0) {
// 每 stagnantWindow 检查一次,避免每轮都判断导致过早停止
double recentImproveRate = calculateRecentImprovementRate(recentImprovements, stagnantWindow);
if (recentImproveRate < improvementRateThreshold) {
shouldStop = true;
stopReason = String.format("最近%d次迭代改进率过低(%.2f%%)", stagnantWindow, recentImproveRate * 100);
}
FileHelper.writeLogFile(String.format("禁忌搜索(融合版) - 提前停止: 连续%d次无改进", maxNoImprovement));
break;
}
if (shouldStop) {
log(String.format("禁忌搜索 - 提前停止:%s", stopReason));
logTabuFinalSummary(best, initialFitnessLevel, initialFitness, iterations, improveCount, significantImproveCount);
long elapsedMs = System.currentTimeMillis() - tsStartTimeMs;
if (elapsedMs > remainingBudgetMs) {
FileHelper.writeLogFile(String.format("禁忌搜索(融合版) - 提前停止: 达到时间预算(%.1fmin)",
elapsedMs / 60000.0));
break;
}
}
logTabuFinalSummary(best, initialFitnessLevel, initialFitness, iterations, improveCount, significantImproveCount);
log(String.format("禁忌搜索 - 结束,总迭代=%d", iterations),true);
FileHelper.writeLogFile(String.format("禁忌搜索(融合版) - 结束: 总迭代=%d, 改进次数=%d, 最终fitness=%.12f",
iterations, improveCount, best.getFitness()));
return 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);
if(chromosome.getMakespan()!=0) {
FileHelper.writeLogFile(String.format("禁忌搜索 - kpi-Makespan: %f", chromosome.getMakespan()));
}
if(chromosome.getDelayTime()!=0) {
FileHelper.writeLogFile(String.format("禁忌搜索 - kpi-DelayTime: %f", chromosome.getDelayTime()));
}
if(chromosome.getTotalChangeoverTime()!=0) {
FileHelper.writeLogFile(String.format("禁忌搜索 - kpi-ChangeoverTime: %f", chromosome.getTotalChangeoverTime()));
}
if(chromosome.getMachineLoadStd()!=0) {
FileHelper.writeLogFile(String.format("禁忌搜索 - kpi-MachineLoad: %f", chromosome.getMachineLoadStd()));
}
if(chromosome.getTotalFlowTime()!=0) {
FileHelper.writeLogFile(String.format("禁忌搜索 - kpi-FlowTime: %f", chromosome.getTotalFlowTime()));
}
}
/**
* 记录禁忌搜索改进详情
*/
private void logTabuImprovement(Chromosome best, double[] initialFitnessLevel, double initialFitness, int iteration) {
StringBuilder sb = new StringBuilder("禁忌搜索 - 改进详情: 迭代" + iteration + ", ");
double[] currentFitness = best.getFitnessLevel();
// 处理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));
log(sb.toString());
}
/**
* 计算最近改进率
*/
private double calculateRecentImprovementRate(int[] recentImprovements, int windowSize) {
int improveCount = 0;
for (int i = 0; i < windowSize; i++) {
improveCount += recentImprovements[i];
}
return (double) improveCount / windowSize;
}
/**
* 记录禁忌搜索最终总结
*/
private void logTabuFinalSummary(Chromosome best, double[] initialFitnessLevel, double initialFitness,
int totalIterations, int improveCount, int significantImproveCount) {
StringBuilder sb = new StringBuilder("禁忌搜索 - 最终总结: ");
double[] currentFitness = best.getFitnessLevel();
sb.append(String.format("总迭代%d次, 成功改进%d次(显著%d次), 改进率%.2f%%. ",
totalIterations, improveCount, significantImproveCount,
totalIterations > 0 ? (double)improveCount / totalIterations * 100 : 0));
// 处理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(),
initialFitness > 0 ? totalImprovement / initialFitness * 100 : 0));
log(sb.toString());
}
/**
* 检查解是否在禁忌表中
*/
public boolean isTabu(String GeneStr) {
// 使用 Set 做 O(1) 命中检查;若 Set 未初始化则回退到 List
if (tabuSet != null) {
return tabuSet.contains(GeneStr);
}
return tabuList.contains(GeneStr);
}
/**
* 添加解到禁忌表(FIFO策略)。
* 同步维护 List(顺序)与 Set(快速命中)。
*/
public void addToTabuList(String geneStr) {
if (geneStr == null) {
return;
}
// 已经在禁忌表里,不用重复插入
if (tabuSet != null && tabuSet.contains(geneStr)) {
return;
}
tabuList.add(geneStr);
if (tabuSet != null) {
tabuSet.add(geneStr);
}
// 超出长度,FIFO 移除最早元素
while (tabuList.size() > tabuListSize) {
String removed = tabuList.remove(0);
if (tabuSet != null) {
tabuSet.remove(removed);
}
}
}
// ====================================================================
// 辅助方法
// ====================================================================
private void decode(GeneticDecoder decoder, Chromosome chromosome , List<Machine> machines) {
private void decode(GeneticDecoder decoder, Chromosome chromosome, List<Machine> machines) {
chromosome.setResult(new CopyOnWriteArrayList<>());
// 缓存 Machine 列表(第一次调用时缓存)
if (cachedMachines == null) {
cachedMachines = ProductionDeepCopyUtil.deepCopyList(machines, Machine.class);
}
// 使用缓存的列表,避免重复深拷贝
chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(cachedMachines, Machine.class));
chromosome.setOrders(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(cachedOrders), Order.class));
chromosome.setOperatRel(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(cachedEntryRel), GroupResult.class));
chromosome.setMaterials(ProductionDeepCopyUtil.deepCopyTreeMap(cachedMaterials,String.class, Material.class));
chromosome.setAllOperations(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(cachedAllOperations), Entry.class));
// 加载锁定工单到ResultOld
List<GAScheduleResult> lockedOrders = GlobalCacheUtil.get("locked_orders_" + chromosome.getScenarioID());
chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(machines, Machine.class));
chromosome.setOrders(ProductionDeepCopyUtil.deepCopyList(
new CopyOnWriteArrayList<>(chromosome.getOrders()), Order.class));
chromosome.setOperatRel(ProductionDeepCopyUtil.deepCopyList(
new CopyOnWriteArrayList<>(chromosome.getOperatRel()), GroupResult.class));
chromosome.setMaterials(ProductionDeepCopyUtil.deepCopyTreeMap(
chromosome.getMaterials(), String.class, Material.class));
chromosome.setAllOperations(ProductionDeepCopyUtil.deepCopyList(
new CopyOnWriteArrayList<>(cachedAllOperations), Entry.class));
List<GAScheduleResult> lockedOrders = getLockedOrders(chromosome);
if (lockedOrders != null && !lockedOrders.isEmpty()) {
chromosome.setResultOld(ProductionDeepCopyUtil.deepCopyList(lockedOrders, GAScheduleResult.class));
} else {
chromosome.setResultOld(new CopyOnWriteArrayList<>());
}
decoder.decodeChromosomeWithCache(chromosome,false);
decoder.decodeChromosomeWithCache(chromosome, false);
}
/**
* 比较两个染色体的优劣(基于fitnessLevel多层次比较)
*/
private boolean isBetter(Chromosome c1, Chromosome c2) {
return fitnessCalculator.isBetter(c1,c2);
@SuppressWarnings("unchecked")
private List<GAScheduleResult> getLockedOrders(Chromosome chromosome) {
try {
return (List<GAScheduleResult>) com.aps.common.util.GlobalCacheUtil.get("locked_orders_" + chromosome.getScenarioID());
} catch (Exception ignored) {
return null;
}
/**
* 判断是否为显著改进(只有超过阈值的改进才重置无改进计数)
* 注意:本方法不再比较传入的初始 chromosome,而是比较传入的参考 fitness。
*/
private boolean isSignificantImprovement(Chromosome newChromo, double[] ignored, double referenceFitness) {
double newFitness = newChromo.getFitness();
return (newFitness - referenceFitness) > SIGNIFICANT_IMPROVEMENT_THRESHOLD;
}
/**
* 保留原有方法签名,避免外部调用编译失败。
*/
private boolean isSignificantImprovement(Chromosome newChromo, Chromosome oldChromo) {
if (!isBetter(newChromo, oldChromo)) {
return false;
}
double newFitness = newChromo.getFitness();
double oldFitness = oldChromo.getFitness();
return (newFitness - oldFitness) > SIGNIFICANT_IMPROVEMENT_THRESHOLD;
private boolean isBetter(Chromosome c1, Chromosome c2) {
return fitnessCalculator.isBetter(c1, c2);
}
// ========================================================================
// 以下为"进一步优化"新增的辅助方法
// ========================================================================
/**
* 估计两个 "," 分隔的 ID 列表字符串的汉明距离(位置不同的元素数)。
* 用于精英解码启发式:避免在机器分配不变、仅做少量工序换位时反复解码
* 估计两个逗号分隔字符串的汉明距离(位置不同的元素数)。
* 辅助判断"是否只是微调",供调用方使用
*/
private int estimateHammingDistance(String a, String b) {
public int estimateHammingDistance(String a, String b) {
if (a == null || b == null) return Integer.MAX_VALUE;
if (a.isEmpty() || b.isEmpty()) return Integer.MAX_VALUE;
String[] sa = a.split(",");
String[] sb = b.split(",");
int minLen = Math.min(sa.length, sb.length);
......@@ -528,48 +291,4 @@ public class TabuSearch {
diff += Math.abs(sa.length - sb.length);
return diff;
}
/**
* 对 chromosome 的 operationSequencing(工序排序片段)做一次小型随机扰动:
* - 随机选择两个下标并交换
* 这会推动 TS 主动探索 SA/VNS 不常触及的"工序排序"邻域,
* 减少对 VNS 策略 1(换设备)的重复依赖。
*/
private Chromosome tryShuffleOperationPart(Chromosome c) {
if (c == null) return null;
// Chromosome 未暴露 operationSequencing 的公共 getter,通过 operationStr 解析
String opStr = c.getOperationStr();
if (opStr == null || opStr.isEmpty()) return c;
String[] parts = opStr.split(",");
if (parts.length < 4) return c;
// 深拷贝一个新的染色体,避免污染 VNS 内部对象
Chromosome copy = ProductionDeepCopyUtil.deepCopy(c, Chromosome.class);
int n = parts.length;
// 做 1~3 次随机位置交换(数量随问题规模自适应,但保持温和)
int swaps = Math.max(1, Math.min(3, n / 1500));
for (int s = 0; s < swaps; s++) {
int i = tsRnd.nextInt(n);
int j = tsRnd.nextInt(n);
if (i != j) {
String tmp = parts[i];
parts[i] = parts[j];
parts[j] = tmp;
}
}
// 将交换后的字符串数组解析为 Integer 列表,回写到 chromosome
CopyOnWriteArrayList<Integer> newOps = new CopyOnWriteArrayList<>();
for (String p : parts) {
try {
newOps.add(Integer.parseInt(p.trim()));
} catch (NumberFormatException ignored) {
// 忽略无法解析的元素(异常保护)
}
}
if (newOps.size() >= 2) {
copy.setOperationSequencing(newOps);
}
return copy;
}
}
......@@ -304,11 +304,11 @@ public class VariableNeighborhoodSearch {
/**
* 对种群中的每个个体进行变邻域搜索
*/
public List<Chromosome> search(List<Chromosome> population, GeneticDecoder decoder, List<Machine> machines) {
public List<Chromosome> search(List<Chromosome> population,TabuSearch tabuSearch, GeneticDecoder decoder, List<Machine> machines) {
List<Chromosome> improvedPopulation = new ArrayList<>();
for (Chromosome chromosome : population) {
Chromosome improvedChromosome = search(chromosome, decoder, machines);
Chromosome improvedChromosome = search(chromosome,tabuSearch, decoder, machines);
improvedPopulation.add(improvedChromosome);
}
......@@ -328,18 +328,18 @@ public class VariableNeighborhoodSearch {
}
/**
* 对单个个体进行变邻域搜索
* 对单个个体进行变邻域搜索(通过 TabuSearch 共享禁忌表:渴望准则 + 概率劣解接受 + 时间预算)
*
* @param chromosome 初始解
* @param tabuSearch 负责禁忌表生命周期(禁忌表、渴望准则、三粒度 key 检查)
* @param decoder 解码
* @param machines 机器列表
*/
public Chromosome search(Chromosome chromosome, GeneticDecoder decoder, List<Machine> machines) {
log("变邻域搜索 - 开始执行",true);
// 注意:设备选择频率不在这里重置
// 频率在整个优化流程起点(HybridAlgorithm初始化时)调用 initMachineSelectFrequency() 初始化一次
// 这样频率可以跨模拟退火、变邻域搜索、禁忌搜索等所有算法累积,真正鼓励设备选择多样性
public Chromosome search(Chromosome chromosome, TabuSearch tabuSearch, GeneticDecoder decoder, List<Machine> machines) {
log("变邻域搜索(共用禁忌表) - 开始执行",true);
// 深拷贝当前染色体
Chromosome current = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
// geneticOperations.DelOrder(current);
Chromosome best = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
writeKpi(best);
......@@ -348,126 +348,193 @@ public class VariableNeighborhoodSearch {
double initialFitness = best.getFitness();
// 提前结束参数
int noImproveRoundCount = 0; // 无改进轮数计数
int totalRounds = 0; // 总轮数
int totalImprovements = 0; // 总改进次数
int totalSignificantImprovements = 0; // 显著改进次数
int consecutiveMinorImprovements = 0; // 连续微小改进计数
int noImproveRoundCount = 0;
int totalRounds = 0;
int totalImprovements = 0;
int totalSignificantImprovements = 0;
int consecutiveMinorImprovements = 0;
int k = 0;
// 同时使用瓶颈感知策略框架和简单邻域方法,提升搜索能力
List<NeighborhoodStructure> neighborhoods = defineNeighborhoods();
while (noImproveRoundCount < maxNoImproveRounds) {
// 用于动态计算"显著改进"的参考 fitness(每次更新 best 后更新)
double currentBestFitness = best.getFitness();
// 时间预算:从现在起最多执行 TabuSearch.TS_TIME_BUDGET_MS
long tsStartTimeMs = System.currentTimeMillis();
long remainingBudgetMs = Math.max(5L * 60L * 1000L, TabuSearch.TS_TIME_BUDGET_MS / 2);
// 估算最大迭代次数(与 TabuSearch 的融合版本保持一致)
int sizeBasedMaxIter = Math.max(60, cachedAllOperations.size() / 50);
int timeBasedMaxIter = (int) Math.max(30, remainingBudgetMs / TabuSearch.TS_PER_ITER_BUDGET_MS);
int maxIterationsCap = Math.min(Math.max(60, sizeBasedMaxIter), Math.max(150, timeBasedMaxIter));
while (noImproveRoundCount < maxNoImproveRounds && totalRounds < maxIterationsCap) {
totalRounds++;
boolean roundHadImprovement = false;
// ============= 第一阶段:瓶颈感知策略(主要搜索手段) =============
// 每轮尝试多次瓶颈感知策略(与SA/TabuSearch使用相同的框架)
// 检查时间预算
long elapsedMs = System.currentTimeMillis() - tsStartTimeMs;
if (elapsedMs > remainingBudgetMs) {
log(String.format("变邻域搜索(融合禁忌) - 达到时间预算(耗时%.1fmin),提前退出", elapsedMs / 60000.0));
break;
}
// ============ 第一阶段:瓶颈感知策略 ============
for (int strategyAttempt = 0; strategyAttempt < 3; strategyAttempt++) {
geneticOperations.DelOrder(current);
// 使用瓶颈感知的3策略框架(策略1:换设备, 策略2:工序前移, 策略3:工序交换)
Chromosome neighbor = generateNeighbor(current);
if (neighbor == null) {
if (neighbor == null) continue;
// ---- 禁忌检查:命中则直接跳过,不解码 ----
boolean tabuHit = tabuSearch.isChromosomeTabu(neighbor);
boolean sameAsCurrent = (neighbor.getGeneStr() != null &&
neighbor.getGeneStr().equals(current.getGeneStr()));
if (sameAsCurrent) {
tabuSearch.addChromosomeToTabu(neighbor);
continue;
}
// 局部搜索
Chromosome localBest = localSearch(neighbor, decoder, machines);
// 产生邻居后,加入禁忌表(无论最终是否接受,避免反复返回同样的邻居)
tabuSearch.addChromosomeToTabu(localBest);
// ============ 接受逻辑(融合 TS 渴望准则 + 概率劣解) ============
boolean betterThanBest = isBetter(localBest, best);
boolean betterThanCurrent = isBetter(localBest, current);
boolean accept;
if (betterThanBest) {
// 比 best 好:无条件接受(渴望准则),即使命中禁忌也接受
accept = true;
} else if (!tabuHit && betterThanCurrent) {
// 非禁忌且比 current 好:接受
accept = true;
} else if (!tabuHit) {
// 非禁忌但劣解:按概率接受(随迭代降低)
double progress = Math.min(1.0, (double) totalRounds / (double) Math.max(30, maxIterationsCap));
double acceptProb = TabuSearch.WORSE_ACCEPT_PROB_START - (TabuSearch.WORSE_ACCEPT_PROB_START - TabuSearch.WORSE_ACCEPT_PROB_MIN) * progress;
accept = rnd.nextDouble() < acceptProb;
} else {
// 禁忌且劣于 best:拒绝
accept = false;
}
// 检查改进
boolean success = isBetter(localBest, best);
boolean isSignificant = isSignificantImprovement(localBest, best);
if (success) {
if (accept) {
current = ProductionDeepCopyUtil.deepCopy(localBest, Chromosome.class);
if (betterThanBest) {
best = ProductionDeepCopyUtil.deepCopy(localBest, Chromosome.class);
writeKpi(best);
current = localBest;
totalImprovements++;
roundHadImprovement = true;
if (isSignificant) {
double delta = best.getFitness() - currentBestFitness;
if (delta > TabuSearch.SIGNIFICANT_IMPROVEMENT_THRESHOLD) {
noImproveRoundCount = 0;
consecutiveMinorImprovements = 0;
totalSignificantImprovements++;
logVNSImprovement(best, initialFitnessLevel, initialFitness, totalRounds, "BottleneckStrategy");
log(String.format("变邻域搜索 - 瓶颈策略成功(显著), 轮次=%d, 策略尝试=%d", totalRounds, strategyAttempt + 1), true);
break; // 找到显著改进后跳出策略尝试,进入下一轮
currentBestFitness = best.getFitness();
logVNSImprovement(best, initialFitnessLevel, initialFitness, totalRounds, "BottleneckStrategy(显著)");
log(String.format("变邻域搜索(融合禁忌) - 瓶颈策略成功(显著), 轮次=%d, fitness=%.12f",
totalRounds, best.getFitness()), true);
break;
} else {
// 微小改进
if (delta > TabuSearch.MINOR_IMPROVEMENT_THRESHOLD) {
noImproveRoundCount = 0;
currentBestFitness = best.getFitness();
}
consecutiveMinorImprovements++;
log(String.format("变邻域搜索 - 瓶颈策略成功(微小), 轮次=%d, 尝试=%d, 连续微小改进=%d",
totalRounds, strategyAttempt + 1, consecutiveMinorImprovements), true);
if (consecutiveMinorImprovements >= MAX_MINOR_IMPROVEMENTS) {
log(String.format("变邻域搜索 - 提前终止:连续%d次微小改进", MAX_MINOR_IMPROVEMENTS), true);
break;
log(String.format("变邻域搜索(融合禁忌) - 瓶颈策略成功(微小), 轮次=%d, fitness=%.12f, delta=%.2e",
totalRounds, best.getFitness(), delta), true);
}
}
}
}
// ============= 第二阶段:简单邻域补充(备选搜索手段) =============
// ============ 第二阶段:简单邻域补充(策略多样化) ============
if (!roundHadImprovement) {
geneticOperations.DelOrder(current);
NeighborhoodStructure neighborhood = neighborhoods.get(k);
// 生成邻域解(简单邻域方法)
Chromosome neighbor = generateNeighbor(current, neighborhood);
if (neighbor != null) {
// 若连续多轮无改进,额外做点工序级扰动,增加探索能力
if (noImproveRoundCount >= 2) {
Chromosome shuffled = tryShuffleOperationPart(neighbor);
if (shuffled != null) neighbor = shuffled;
}
boolean tabuHit = tabuSearch.isChromosomeTabu(neighbor);
if (!tabuHit) {
Chromosome localBest = localSearch(neighbor, decoder, machines);
tabuSearch.addChromosomeToTabu(localBest);
boolean betterThanBest = isBetter(localBest, best);
boolean betterThanCurrent = isBetter(localBest, current);
boolean acceptLocal;
if (betterThanBest) {
acceptLocal = true;
} else if (!tabuHit && betterThanCurrent) {
acceptLocal = true;
} else if (!tabuHit) {
double progress = Math.min(1.0, (double) totalRounds / (double) Math.max(30, maxIterationsCap));
double p = TabuSearch.WORSE_ACCEPT_PROB_START - (TabuSearch.WORSE_ACCEPT_PROB_START - TabuSearch.WORSE_ACCEPT_PROB_MIN) * progress;
acceptLocal = rnd.nextDouble() < p;
} else {
acceptLocal = false;
}
boolean success = isBetter(localBest, best);
boolean isSignificant = isSignificantImprovement(localBest, best);
if (success) {
if (acceptLocal) {
current = ProductionDeepCopyUtil.deepCopy(localBest, Chromosome.class);
if (betterThanBest) {
best = ProductionDeepCopyUtil.deepCopy(localBest, Chromosome.class);
writeKpi(best);
current = localBest;
totalImprovements++;
roundHadImprovement = true;
if (isSignificant) {
double delta = best.getFitness() - currentBestFitness;
if (delta > TabuSearch.SIGNIFICANT_IMPROVEMENT_THRESHOLD) {
noImproveRoundCount = 0;
consecutiveMinorImprovements = 0;
totalSignificantImprovements++;
logVNSImprovement(best, initialFitnessLevel, initialFitness, totalRounds, neighborhood.name);
log(String.format("变邻域搜索 - 邻域成功(显著): %s", neighborhood.name), true);
currentBestFitness = best.getFitness();
logVNSImprovement(best, initialFitnessLevel, initialFitness, totalRounds, neighborhood.name + "(显著)");
log(String.format("变邻域搜索(融合禁忌) - 邻域成功(显著): %s, fitness=%.12f",
neighborhood.name, best.getFitness()), true);
} else {
if (delta > TabuSearch.MINOR_IMPROVEMENT_THRESHOLD) {
noImproveRoundCount = 0;
currentBestFitness = best.getFitness();
}
consecutiveMinorImprovements++;
log(String.format("变邻域搜索 - 邻域成功(微小): %s", neighborhood.name), true);
log(String.format("变邻域搜索(融合禁忌) - 邻域成功(微小): %s, fitness=%.12f",
neighborhood.name, best.getFitness()), true);
}
}
}
}
}
k++;
if (k >= neighborhoods.size()) {
k = 0;
}
if (k >= neighborhoods.size()) k = 0;
}
// 轮次结束:若无改进则增加无改进计数
// 轮次结束:若无改进则增加计数
if (!roundHadImprovement) {
noImproveRoundCount++;
log(String.format("变邻域搜索 - 轮次%d无改进,连续无改进轮数: %d/%d",
log(String.format("变邻域搜索(融合禁忌) - 轮次%d无改进, 连续无改进=%d/%d",
totalRounds, noImproveRoundCount, maxNoImproveRounds));
} else {
// 本轮有改进,重置部分计数(显著改进已在上方重置为0)
if (noImproveRoundCount > 0) {
log(String.format("变邻域搜索 - 轮次%d有改进,继续搜索", totalRounds));
}
}
// 检查提前结束条件
if (noImproveRoundCount >= maxNoImproveRounds) {
log(String.format("变邻域搜索 - 提前结束:连续%d轮无改进,总轮次=%d", maxNoImproveRounds, totalRounds));
log(String.format("变邻域搜索(融合禁忌) - 提前结束: 连续%d轮无改进, 总轮次=%d",
maxNoImproveRounds, totalRounds));
logVNSFinalSummary(best, initialFitnessLevel, initialFitness, totalRounds, totalImprovements, totalSignificantImprovements);
break;
}
}
if (noImproveRoundCount < maxNoImproveRounds) {
logVNSFinalSummary(best, initialFitnessLevel, initialFitness, totalRounds, totalImprovements, totalSignificantImprovements);
}
log(String.format("变邻域搜索(融合禁忌) - 结束, 总轮次=%d", totalRounds), true);
return best;
}
......@@ -2558,6 +2625,63 @@ public class VariableNeighborhoodSearch {
return fitnessCalculator.isBetter(c1,c2);
}
// ==================== VNS 内部辅助工具 ====================
/**
* 估计两个逗号分隔字符串的汉明距离(位置不同的元素数)。
* 用于辅助判断"是否只是微调"。
*/
private int estimateHammingDistance(String a, String b) {
if (a == null || b == null) return Integer.MAX_VALUE;
if (a.isEmpty() || b.isEmpty()) return Integer.MAX_VALUE;
String[] sa = a.split(",");
String[] sb = b.split(",");
int minLen = Math.min(sa.length, sb.length);
int diff = 0;
for (int i = 0; i < minLen; i++) {
if (!sa[i].equals(sb[i])) diff++;
}
diff += Math.abs(sa.length - sb.length);
return diff;
}
/**
* 对 chromosome 的 operationSequencing(工序排序片段)做一次小型随机扰动,
* 用于策略多样化(连续换设备策略后强制做点工序级探索)。
*/
private Chromosome tryShuffleOperationPart(Chromosome c) {
if (c == null) return null;
String opStr = c.getOperationStr();
if (opStr == null || opStr.isEmpty()) return c;
String[] parts = opStr.split(",");
if (parts.length < 4) return c;
Chromosome copy = ProductionDeepCopyUtil.deepCopy(c, Chromosome.class);
int n = parts.length;
int swaps = Math.max(5, Math.min(15, n / 400));
for (int s = 0; s < swaps; s++) {
int i = rnd.nextInt(n);
int j = rnd.nextInt(n);
if (i != j) {
String tmp = parts[i];
parts[i] = parts[j];
parts[j] = tmp;
}
}
CopyOnWriteArrayList<Integer> newOps = new CopyOnWriteArrayList<>();
for (String p : parts) {
try {
newOps.add(Integer.parseInt(p.trim()));
} catch (NumberFormatException ignored) {
}
}
if (newOps.size() >= 2) {
copy.setOperationSequencing(newOps);
}
return copy;
}
/**
* 构建位置索引:groupId_sequence -> position
*/
......
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