Commit d4ace4ca authored by Tong Li's avatar Tong Li

遗传算法-优化

parent 4fb0ebb2
......@@ -36,27 +36,64 @@ public class ObjectiveWeights {
}
/**
* 归一化目标值(消除量纲影响)
* 优化版归一化目标值(消除量纲影响,解决WeightedObjective为0的问题)
* @param objectives 待归一化的目标值数组
* @param minValues 各维度目标值的最小值
* @param maxValues 各维度目标值的最大值
* @param isMinimize 各维度是否为最小化目标(true:值越小越优;false:值越大越优)
* @return 归一化后目标值(范围:[0,1],无0值兜底)
*/
public double[] normalizeObjectives(double[] objectives, double[] minValues, double[] maxValues) {
public double[] normalizeObjectives(double[] objectives, double[] minValues, double[] maxValues, boolean[] isMinimize) {
if (objectives == null || objectives.length == 0) {
return new double[0];
}
double[] normalized = new double[objectives.length];
if(minValues==null||minValues.length==0)
{
// 浮点精度容错(避免因精度误差误判分母为0)
final double EPS = 1e-10;
if (minValues == null || minValues.length == 0 || maxValues == null || maxValues.length != objectives.length) {
// 分支1:无min/max时,采用线性归一化(统一范围[0,1],避免趋近0)
for (int i = 0; i < objectives.length; i++) {
normalized[i]= 1/ (1 +(double) objectives[i]);
double obj = objectives[i];
// 防御:处理负数(目标值应为非负,若为负则兜底为0)
obj = Math.max(0, obj);
// 优化:动态缩放,避免大数值趋近0(可根据业务调整缩放系数)
double scale = Math.max(1, obj); // 缩放系数≥1
if (isMinimize != null && isMinimize.length > i && isMinimize[i]) {
// 最小化目标:值越小越优,归一化后值越小越优(范围[0,1])
normalized[i] = 1.0 / (1.0 + obj / scale);
} else {
// 最大化目标:值越大越优,归一化后值越大越优(范围[0,1])
normalized[i] = obj / (1.0 + obj);
}
// 兜底:避免极端情况归一化值为0(最小设为1e-6)
normalized[i] = Math.max(normalized[i], 1e-6);
}
}else {
} else {
// 分支2:有min/max时,标准线性归一化(增加浮点容错)
for (int i = 0; i < objectives.length; i++) {
if (maxValues[i] - minValues[i] == 0)
normalized[i] = 0;
else
normalized[i] = (objectives[i] - minValues[i]) / (maxValues[i] - minValues[i]);
double min = minValues[i];
double max = maxValues[i];
double obj = objectives[i];
// 浮点精度容错:判断分母是否接近0(而非硬相等)
if (Math.abs(max - min) < EPS) {
// 分母接近0时:归一化值设为1(而非0),保证加权后不为0
normalized[i] = 1.0;
} else {
// 标准归一化公式
normalized[i] = (obj - min) / (max - min);
// 若为最小化目标,反转归一化值(值越小,归一化后越大)
if (isMinimize != null && isMinimize.length > i && isMinimize[i]) {
normalized[i] = 1.0 - normalized[i];
}
}
// 兜底:限制范围[1e-6,1],彻底避免0值
normalized[i] = Math.max(Math.min(normalized[i], 1.0), 1e-6);
}
}
return normalized;
}
/**
* 计算加权目标值
*/
......
......@@ -77,6 +77,7 @@ public class GeneticAlgorithm {
// 预生成全局工序列表(所有初始化方法共享同一顺序)
List<GlobalOperationInfo> globalOpList = initialization.generateGlobalOpList();
FileHelper.writeLogFile("初始化种群-----------开始-------");
Machine ms= machines.stream().filter(t->t.getName().equals("铲车-2")).findFirst().orElse(null);
// 步骤1:初始化种群
List<Chromosome> population = initialization.generateInitialPopulation(param, globalOpList);
......@@ -94,13 +95,12 @@ public class GeneticAlgorithm {
// best=fronts.get(0).stream()
// .max(Comparator.comparingDouble(Chromosome::getFitness))
// .orElse(null);
WriteKpis(fronts,"初始");
best = fronts.get(0).stream()
.sorted((c1, c2) -> Double.compare(c2.getWeightedObjective(), c1.getWeightedObjective()))
.sorted((c1, c2) -> Double.compare(c1.getWeightedObjective(), c2.getWeightedObjective()))
.findFirst()
.orElse(null);
FileHelper.writeLogFile(String.format("KPI---%f-----初始-----%s----",best.getWeightedObjective(),best.getID()));
WriteKpi(best);
double bestFitness=best.getWeightedObjective();
int Iteration=0;
// 步骤2:迭代进化
......@@ -182,12 +182,13 @@ public class GeneticAlgorithm {
fronts = _nsgaIIUtils.parallelFastNonDominatedSort(population);
WriteKpis(fronts,String.valueOf(iter));
best = fronts.get(0).stream()
.sorted((c1, c2) -> Double.compare(c2.getWeightedObjective(), c1.getWeightedObjective()))
.sorted((c1, c2) -> Double.compare(c1.getWeightedObjective(), c2.getWeightedObjective()))
.findFirst()
.orElse(null);
FileHelper.writeLogFile(String.format("KPI---%f-----"+iter+"-----%s----",best.getWeightedObjective(),best.getID()));
if(bestFitness<best.getWeightedObjective())
WriteKpi(best);
if(bestFitness>best.getWeightedObjective())
{
bestFitness=best.getWeightedObjective();
......@@ -220,6 +221,26 @@ public class GeneticAlgorithm {
return best;
}
private void WriteKpis(List<List<Chromosome>> fronts,String index) {
for (int i=0;i<fronts.size();i++)
{
FileHelper.writeLogFile(String.format("KPI---%s--------%d----",index,i));
for (Chromosome chromosome:fronts.get(i)) {
WriteKpi(chromosome);
}
}
}
private void WriteKpi(Chromosome chromosome)
{
FileHelper.writeLogFile(String.format("KPI---%f--------%s----",chromosome.getWeightedObjective(),chromosome.getID()));
for (double d: chromosome.getObjectives()) {
FileHelper.writeLogFile(String.format(" KPI---%f-------",d));
}
}
private void Chromosomedecode(ScheduleParams param, List<Entry> allOperations,List<GlobalOperationInfo> globalOpList,List<Chromosome> population)
{
GeneticDecoder decoder = new GeneticDecoder(_GlobalParam, param.getBaseTime(), machines, orders, materials, machineScheduler,orderMaterials);
......
......@@ -372,7 +372,7 @@ if(finishedOrder==null||finishedOrder.size()==0)
int setupTime = calculateSetupTime(chromosome.getResult(), operation, machine, machineOption);
FileHelper.writeLogFile (" "+operation.getGroupId()+" : "+operation.getId()+",处理时间: " + processingTime + ", 后处理: " + teardownTime +
", 前处理: " + preTime + ", 换型: " + setupTime+ ", 数量: " + operation.getQuantity());
", 前处理: " + preTime + ", 换型: " + setupTime+ ", 数量: " + operation.getQuantity()+ ", 设备: "+machine.getId());
// 确定任务的最早开始时间(基于前一道工序的完整结束时间,包含后处理)
......
......@@ -33,42 +33,54 @@ public class GeneticOperations {
* 锦标赛选择
*/
public List<Chromosome> tournamentSelection(List<Chromosome> population, int tournamentSize) {
List<Chromosome> selected = new ArrayList<>();
while (selected.size() < population.size()) {
// 随机选择k个个体
// List<Chromosome> candidates = population.stream()
// .sorted((a, b) -> rnd.nextInt())
// .limit(tournamentSize)
// .collect(Collectors.toList());
// 步骤1: 创建一个临时列表用于存放候选者
List<Chromosome> candidates = new ArrayList<>(population);
// 步骤2: 随机打乱候选者列表
Collections.shuffle(candidates, rnd);
// 步骤3: 截取前 tournamentSize 个元素作为本次锦标赛的参与者
if (tournamentSize < candidates.size()) {
candidates = candidates.subList(0, tournamentSize);
int populationSize = population.size();
List<Chromosome> selected = new ArrayList<>(populationSize); // 预初始化容量
// 边界值处理:锦标赛规模不能超过种群规模,且至少为1
int effectiveTournamentSize = Math.max(1, Math.min(tournamentSize, populationSize));
while (selected.size() < populationSize) {
// 优化1:不复制整个种群,直接随机抽取有效锦标赛规模的个体,避免大量内存拷贝和shuffle开销
Chromosome bestCandidate = null;
int bestRank = Integer.MAX_VALUE;
double bestCrowdingDistance = -Double.MAX_VALUE;
// 优化2:单次循环随机抽取+实时比较,无需创建候选列表、无需排序,O(k)时间复杂度(k为锦标赛规模)
for (int i = 0; i < effectiveTournamentSize; i++) {
// 随机获取一个种群个体(直接通过索引访问,无需打乱整个列表)
int randomIndex = rnd.nextInt(populationSize);
Chromosome current = population.get(randomIndex);
// 优化3:手动实现排序逻辑的比较,避免Stream API的额外开销
if (bestCandidate == null) {
bestCandidate = current;
bestRank = current.getRank();
bestCrowdingDistance = current.getCrowdingDistance();
} else {
// 先比较Rank(升序:Rank越小越优)
if (current.getRank() < bestRank) {
updateBestCandidate(current, bestCandidate, bestRank, bestCrowdingDistance);
} else if (current.getRank() == bestRank) {
// Rank相同时,比较拥挤距离(降序:值越大越优)
if (current.getCrowdingDistance() > bestCrowdingDistance) {
updateBestCandidate(current, bestCandidate, bestRank, bestCrowdingDistance);
}
}
}
}
// 步骤4: 从参与者中选择适应度最高的个体
// Chromosome best = candidates.stream()
// .max(Comparator.comparingDouble(Chromosome::getFitness))
// .orElseThrow(() -> new IllegalStateException("候选列表为空,无法选择最佳个体。"));
// // 复制个体(假设Chromosome有复制方法或通过构造函数复制)
Chromosome best = candidates.stream()
// 第一步:按Rank升序排序(与OrderBy对应)
.sorted(Comparator.comparingInt(Chromosome::getRank)
// 第二步:按拥挤距离降序排序(与ThenByDescending对应)
.thenComparing(Comparator.comparingDouble(Chromosome::getCrowdingDistance).reversed()))
// 取第一个元素(与First()对应)
.findFirst()
// 处理空列表情况(避免NoSuchElementException)
.orElse(null);
selected.add(ProductionDeepCopyUtil.deepCopy(best,Chromosome.class));
// 深拷贝最优个体并加入选中列表(确保线程安全和种群独立性)
if (bestCandidate != null) {
selected.add(ProductionDeepCopyUtil.deepCopy(bestCandidate, Chromosome.class));
}
}
return selected;
}
private void updateBestCandidate(Chromosome current, Chromosome bestCandidate,
int bestRank, double bestCrowdingDistance) {
bestRank = current.getRank();
bestCrowdingDistance = current.getCrowdingDistance();
bestCandidate = current;
}
// 重载,使用默认锦标赛大小3
public List<Chromosome> tournamentSelection(List<Chromosome> population) {
return tournamentSelection(population, param.getTournamentSize());
......
......@@ -197,16 +197,23 @@ public class MachineCalculator {
private TimeSegment GetCurrentOrNextShift(Machine machine, LocalDateTime time, String prevtime, boolean checkprevtime) {
TimeSegment start = null;
if(machine.getAvailability()!=null)
{
start = machine.getAvailability().stream()
.filter(slot -> !slot.isUsed() && slot.getType() != SegmentType.MAINTENANCE)
.filter(slot -> slot.getStart().isAfter(time) || slot.getEnd().isAfter(time))
.findFirst()
.orElse(null);
}else {
machine.setAvailability(new ArrayList<>());
}
// 查找有效班次
start = machine.getAvailability().stream()
.filter(slot -> !slot.isUsed() && slot.getType() != SegmentType.MAINTENANCE)
.filter(slot -> slot.getStart().isAfter(time) || slot.getEnd().isAfter(time))
.findFirst()
.orElse(null);
if (start == null) {
// 生成新时间段
List<TimeSegment> timeSegments = machineScheduler.generateTimeSegment(machine, time.plusDays(1));
machine.getAvailability().addAll(timeSegments);
// 更新设备时间线
......
......@@ -40,10 +40,11 @@ public class NSGAIIUtils {
*/
public List<List<Chromosome>> parallelFastNonDominatedSort(List<Chromosome> population) {
int popSize = population.size();
// 步骤1:预处理 - 计算归一化目标值和加权目标值
preprocessObjectives(population);
if (popSize <= taskCount * 10) { // 小规模种群直接串行
// 步骤1:预处理 - 计算归一化目标值和加权目标值
preprocessObjectives(population);
return fastNonDominatedSort(population);
}
......@@ -110,6 +111,8 @@ public class NSGAIIUtils {
return fronts;
}
/**
* 串行快速非支配排序(兼容小规模种群)
*/
......@@ -129,7 +132,7 @@ public class NSGAIIUtils {
for (Chromosome p : population) {
for (Chromosome q : population) {
if (p == q) continue;
// p.getWeightedObjective() < q.getWeightedObjective();
if (dominates(p, q)) {
// p支配q,将q加入p的被支配集合S
S.get(p).add(q);
......@@ -167,8 +170,6 @@ public class NSGAIIUtils {
return fronts;
}
/**
* 预处理目标值(归一化+加权)
*/
......@@ -183,10 +184,10 @@ public class NSGAIIUtils {
minValues[i] = population.stream().mapToDouble(c -> c.getObjectives()[idx]).min().orElse(0);
maxValues[i] = population.stream().mapToDouble(c -> c.getObjectives()[idx]).max().orElse(0);
}
boolean[] isMinimize = {true, true, true, false, true};
// 并行计算归一化和加权目标值
population.parallelStream().forEach(chromo -> {
double[] normalized = objectiveWeights.normalizeObjectives(chromo.getObjectives(), minValues, maxValues);
double[] normalized = objectiveWeights.normalizeObjectives(chromo.getObjectives(), minValues, maxValues,isMinimize);
if (objectiveWeights.isPureNSGAIIMode()) {
chromo.setWeightedObjective(objectiveWeights.calculateObjective(normalized));
}else {
......
......@@ -534,10 +534,7 @@ public class RoutingDataService {
machine.setHolidays(Holidays1);
List<MaintenanceWindow> maintenanceWindows=new ArrayList<>();
if(machine.getName().contains("铲车"))
{
int i=1+2;
}
List<EquipMaintainTask> EquipMaintainTasks1 = EquipMaintainTasks.stream()
.filter(t -> t.getEquipId().equals(resource.getReferenceId()) )
.collect(Collectors.toList());
......@@ -560,9 +557,12 @@ if(machine.getName().contains("铲车"))
// 4. 初始化机器时间线
for (Machine machine : machines) {
MachineTimeline timeline = machineScheduler.getOrCreateTimeline(machine);
machine.setAvailability(timeline.getSegments());
}
return machines;
......@@ -579,6 +579,13 @@ if(machine.getName().contains("铲车"))
.list();
//设备能力
//维修记录
LambdaQueryWrapper<EquipMaintainTask> EquipMaintainTaskWrapper = new LambdaQueryWrapper<>();
EquipMaintainTaskWrapper.eq(EquipMaintainTask::getIsdeleted, 0);
EquipMaintainTaskWrapper.eq(EquipMaintainTask::getStatus, 0);
EquipMaintainTaskWrapper.ge(EquipMaintainTask::getPlanFinishTime, baseTime);
List<EquipMaintainTask> EquipMaintainTasks = _equipMaintainTaskService.list(EquipMaintainTaskWrapper);
for (PlanResource resource : PlanResources) {
Machine machine = new Machine();
......@@ -606,6 +613,23 @@ if(machine.getName().contains("铲车"))
shifts1.add(shift);
machine.setShifts(shifts1);
List<MaintenanceWindow> maintenanceWindows=new ArrayList<>();
List<EquipMaintainTask> EquipMaintainTasks1 = EquipMaintainTasks.stream()
.filter(t -> t.getEquipId().equals(resource.getReferenceId()) )
.collect(Collectors.toList());
for (EquipMaintainTask equipMaintainTask : EquipMaintainTasks1) {
MaintenanceWindow maintenanceWindow=new MaintenanceWindow();
maintenanceWindow.setId(equipMaintainTask.getId().toString());
maintenanceWindow.setStartTime(equipMaintainTask.getPlanStartTime());
maintenanceWindow.setEndTime(equipMaintainTask.getPlanFinishTime());
maintenanceWindow.setReason("");
maintenanceWindows.add(maintenanceWindow);
}
machine.setMaintenanceWindows(maintenanceWindows);
machines.add(machine);
}
......
......@@ -158,6 +158,11 @@ Integer newMachineId1=newMachineId.intValue();
maintenanceWindow.setId(UUID.randomUUID().toString());
machine.getMaintenanceWindows().add(maintenanceWindow);
MachineSchedulerService machineScheduler = new MachineSchedulerService(
chromosome.getBaseTime());
MachineTimeline machineTimeline = machineScheduler.getOrCreateTimeline(machine);
machine.setAvailability(machineTimeline.getSegments());
}else {
throw new RuntimeException("未找到设备");
......@@ -207,6 +212,11 @@ Integer newMachineId1=newMachineId.intValue();
.findFirst();
machine.getMaintenanceWindows().remove(index);
MachineSchedulerService machineScheduler = new MachineSchedulerService(
chromosome.getBaseTime());
MachineTimeline machineTimeline = machineScheduler.getOrCreateTimeline(machine);
machine.setAvailability(machineTimeline.getSegments());
}else {
throw new RuntimeException("未找到设备");
......
......@@ -44,7 +44,9 @@ public class MachineSchedulerService {
// 创建新时间线(60天范围)
MachineTimeline newTimeline = generateTimeline(machine);
// timelineCache.put(machineId, newTimeline);
newTimeline.setSegments(mergeSegments(newTimeline.getSegments()));
// timelineCache.put(machineId, newTimeline);
return newTimeline;
}
......@@ -76,18 +78,31 @@ public class MachineSchedulerService {
timeline.setSegments(mergeSegments(allSegments));
// 处理维护窗口
if (machine.getMaintenanceWindows() != null && !timeline.getSegments().isEmpty()) {
if (machine.getMaintenanceWindows() != null&&machine.getMaintenanceWindows().size()>0 && !timeline.getSegments().isEmpty()) {
LocalDateTime maxEnd = timeline.getSegments().stream()
.map(TimeSegment::getEnd)
.max(LocalDateTime::compareTo)
.orElse(LocalDateTime.MIN);
for (MaintenanceWindow maintenanceWindow : machine.getMaintenanceWindows()) {
if (maintenanceWindow.getStartTime().isAfter(maxEnd)) {
List<MaintenanceWindow> MaintenanceWindow= mergeMaintenanceWindows(machine.getMaintenanceWindows());
for (MaintenanceWindow maintenanceWindow : MaintenanceWindow) {
if (maintenanceWindow.getStartTime().isAfter(maxEnd) ||
maintenanceWindow.getEndTime().isBefore(currentTime)) {
continue;
}
updateSegmentsForMaintenance(timeline.getSegments(),
maintenanceWindow.getStartTime(), maintenanceWindow.getEndTime());
if(maintenanceWindow.getStartTime().isBefore(currentTime)&&maintenanceWindow.getEndTime().isAfter(maxEnd))
{
timeline.setSegments(new ArrayList<>());
currentTime=maintenanceWindow.getEndTime();
timeline= generateTimeline(machine);
break;
}else {
updateSegmentsForMaintenance(timeline.getSegments(),
maintenanceWindow.getStartTime(), maintenanceWindow.getEndTime());
}
}
}
......@@ -128,18 +143,26 @@ public class MachineSchedulerService {
}
// 处理维护窗口
if (machine.getMaintenanceWindows() != null && !segments.isEmpty()) {
if (machine.getMaintenanceWindows() != null&&machine.getMaintenanceWindows().size()>0 && !segments.isEmpty()) {
LocalDateTime maxEnd = segments.stream()
.map(TimeSegment::getEnd)
.max(LocalDateTime::compareTo)
.orElse(LocalDateTime.MIN);
for (MaintenanceWindow maintenanceWindow : machine.getMaintenanceWindows()) {
List<MaintenanceWindow> MaintenanceWindow= mergeMaintenanceWindows(machine.getMaintenanceWindows());
for (MaintenanceWindow maintenanceWindow : MaintenanceWindow) {
if (maintenanceWindow.getStartTime().isAfter(maxEnd) ||
maintenanceWindow.getStartTime().isBefore(currentTime)) {
maintenanceWindow.getEndTime().isBefore(currentTime)) {
continue;
}
updateSegmentsForMaintenance(segments, maintenanceWindow.getStartTime(), maintenanceWindow.getEndTime());
if(maintenanceWindow.getStartTime().isBefore(currentTime)&&maintenanceWindow.getEndTime().isAfter(maxEnd))
{
currentTime=maintenanceWindow.getEndTime();
segments= generateTimeSegment(machine,currentTime);
break;
}else {
updateSegmentsForMaintenance(segments, maintenanceWindow.getStartTime(), maintenanceWindow.getEndTime());
}
}
}
......@@ -302,6 +325,39 @@ if(shifts==null||shifts.size()==0) {
return result;
}
private List<MaintenanceWindow> mergeMaintenanceWindows(List<MaintenanceWindow> segments1) {
List<MaintenanceWindow> segments= ProductionDeepCopyUtil.deepCopyList(segments1,MaintenanceWindow.class);
if(segments==null||segments.size()==0)
{
return null;
}
// 按开始时间排序
segments.sort(Comparator.comparing(MaintenanceWindow::getStartTime));
Stack<MaintenanceWindow> merged = new Stack<>();
for (MaintenanceWindow seg : segments) {
if (!merged.isEmpty() && !merged.peek().getEndTime().isBefore(seg.getStartTime())) {
MaintenanceWindow top = merged.peek();
if (top.getEndTime().isBefore(seg.getEndTime())) {
top.setEndTime(seg.getEndTime());
}
} else {
merged.push(seg);
}
}
List<MaintenanceWindow> result = new ArrayList<>(merged);
// 按开始时间排序
result.sort(Comparator.comparing(MaintenanceWindow::getStartTime));
return result;
}
private void updateSegmentsForMaintenance(List<TimeSegment> segments, LocalDateTime maintenanceStart, LocalDateTime maintenanceEnd) {
if (segments.isEmpty()) return;
......@@ -356,12 +412,12 @@ if(shifts==null||shifts.size()==0) {
}
}
segments.add(new TimeSegment(
maintenanceStart,
maintenanceEnd,
SegmentType.MAINTENANCE,
false
));
// segments.add(new TimeSegment(
// maintenanceStart,
// maintenanceEnd,
// SegmentType.MAINTENANCE,
// false
// ));
}
private int binarySearchSegment(List<TimeSegment> segments, LocalDateTime maintenanceStart) {
......
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