Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
H
HYH.APSJ
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
佟礼
HYH.APSJ
Commits
41cab0d7
Commit
41cab0d7
authored
Jul 06, 2026
by
Tong Li
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
优化
parent
7c25b0fe
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
2534 additions
and
477 deletions
+2534
-477
FileHelper.java
src/main/java/com/aps/common/util/FileHelper.java
+26
-0
Chromosome.java
src/main/java/com/aps/entity/Algorithm/Chromosome.java
+55
-5
Entry.java
src/main/java/com/aps/entity/basic/Entry.java
+3
-0
AdaptiveLargeNeighborhoodSearch.java
...ps/service/Algorithm/AdaptiveLargeNeighborhoodSearch.java
+1003
-0
CpSatFjspModel.java
src/main/java/com/aps/service/Algorithm/CpSatFjspModel.java
+97
-14
CpSatInitializer.java
...main/java/com/aps/service/Algorithm/CpSatInitializer.java
+3
-3
CpSatLnsNeighborhood.java
.../java/com/aps/service/Algorithm/CpSatLnsNeighborhood.java
+512
-0
GeneticDecoder.java
src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
+14
-6
HillClimbing.java
src/main/java/com/aps/service/Algorithm/HillClimbing.java
+2
-2
HybridAlgorithm.java
src/main/java/com/aps/service/Algorithm/HybridAlgorithm.java
+88
-2
Initialization.java
src/main/java/com/aps/service/Algorithm/Initialization.java
+8
-2
KpiLowerBoundCalculator.java
...va/com/aps/service/Algorithm/KpiLowerBoundCalculator.java
+271
-0
RoutingDataService.java
...in/java/com/aps/service/Algorithm/RoutingDataService.java
+3
-1
SimulatedAnnealing.java
...in/java/com/aps/service/Algorithm/SimulatedAnnealing.java
+274
-392
VariableNeighborhoodSearch.java
...com/aps/service/Algorithm/VariableNeighborhoodSearch.java
+173
-48
PlanResultServiceTest.java
src/test/java/com/aps/demo/PlanResultServiceTest.java
+2
-2
No files found.
src/main/java/com/aps/common/util/FileHelper.java
View file @
41cab0d7
...
...
@@ -10,6 +10,32 @@ public class FileHelper {
private
static
final
String
LOG_FILE
=
"schedule_log.txt"
;
private
static
final
String
LOG_FILE_PATH
=
"log/"
;
// 日志级别
private
static
final
int
LOG_LEVEL_DEBUG
=
0
;
private
static
final
int
LOG_LEVEL_INFO
=
1
;
private
static
final
int
LOG_LEVEL_WARN
=
2
;
private
static
int
currentLogLevel
=
LOG_LEVEL_INFO
;
// 局部搜索优化
public
static
void
log
(
String
message
)
{
log
(
message
,
LOG_LEVEL_INFO
,
true
);
}
public
static
void
log
(
String
message
,
boolean
enableLogging
)
{
log
(
message
,
LOG_LEVEL_INFO
,
enableLogging
);
}
public
static
void
log
(
String
message
,
int
level
)
{
log
(
message
,
level
,
false
);
}
public
static
void
log
(
String
message
,
int
level
,
boolean
enableLogging
)
{
if
(
enableLogging
&&
level
>=
currentLogLevel
)
{
writeLogFile
(
message
);
}
}
public
static
void
writeLogFile
(
String
message
)
{
String
date
=
LocalDateTime
.
now
().
format
(
DateTimeFormatter
.
ofPattern
(
"yyyyMMdd"
))+
"-"
;
...
...
src/main/java/com/aps/entity/Algorithm/Chromosome.java
View file @
41cab0d7
...
...
@@ -133,7 +133,7 @@ public class Chromosome {
private
TreeMap
<
String
,
Material
>
materials
=
new
TreeMap
<>();
// private List<Material> materials = new ArrayList<>();
// private List<Material> materials = new ArrayList<>();
private
List
<
String
>
materialIds
=
new
ArrayList
<>();
/*
...
...
@@ -142,6 +142,12 @@ public class Chromosome {
private
double
[]
Objectives
=
new
double
[
0
];
// 多目标值:[Makespan, TotalFlowTime, TotalChangeover, LoadStd, Delay]
private
double
[]
MaxObjectives
=
new
double
[
0
];
//
private
double
[]
MinObjectives
=
new
double
[
0
];
//
private
double
[]
weights
=
new
double
[
0
];
/**
* 各目标维度的理论下界(用于计算 Gap = (current - lowerBound) / lowerBound)。
* 由 KpiLowerBoundCalculator 在 decode 后计算并填入。
*/
private
double
[]
LowerBoundObjectives
=
new
double
[
0
];
private
int
Rank
;
// 非支配排序等级(1最优)
private
double
CrowdingDistance
=
0
;
// 拥挤距离 越小越优
/*
...
...
@@ -255,7 +261,7 @@ public class Chromosome {
*
* @return 不在 allOperations 中的 GAScheduleResult 列表;若均存在则返回空列表
*/
public
List
<
GAScheduleResult
>
getResultsNotInAllOperations
()
{
public
List
<
GAScheduleResult
>
checkResultsNotInAllOperations1
()
{
if
(
Result
==
null
||
Result
.
isEmpty
())
{
return
Collections
.
emptyList
();
}
...
...
@@ -265,7 +271,7 @@ public class Chromosome {
Set
<
Integer
>
allOpIds
=
allOperations
.
stream
()
.
map
(
com
.
aps
.
entity
.
basic
.
Entry
::
getId
)
.
collect
(
Collectors
.
toSet
());
List
<
GAScheduleResult
>
NotInAllOperations
=
Result
.
stream
()
List
<
GAScheduleResult
>
NotInAllOperations
=
Result
.
stream
()
.
filter
(
r
->
!
allOpIds
.
contains
(
r
.
getOperationId
()))
.
collect
(
Collectors
.
toList
());
...
...
@@ -281,8 +287,52 @@ public class Chromosome {
*
* @return 存在则返回 true,否则返回 false
*/
public
boolean
hasResultNotInAllOperations
()
{
return
!
getResultsNotInAllOperations
().
isEmpty
();
// public boolean hasResultNotInAllOperations1() {
// / return !getResultsNotInAllOperations1().isEmpty();
// }
/**
* LNS 需要:创建染色体的深拷贝(machineSelection 和 operationSequencing 独立副本)
* 大对象(globalOpList / allOperations / orders / materials)共享引用
*/
public
Chromosome
deepCopy
()
{
Chromosome
copy
=
new
Chromosome
();
if
(
machineSelection
!=
null
)
{
copy
.
machineSelection
=
new
CopyOnWriteArrayList
<>(
machineSelection
);
copy
.
machineStrDirty
=
true
;
}
if
(
operationSequencing
!=
null
)
{
copy
.
operationSequencing
=
new
CopyOnWriteArrayList
<>(
operationSequencing
);
copy
.
operationStrDirty
=
true
;
}
copy
.
geneStrDirty
=
true
;
if
(
Objectives
!=
null
)
copy
.
Objectives
=
Arrays
.
copyOf
(
Objectives
,
Objectives
.
length
);
if
(
WeightedObjectives
!=
null
)
copy
.
WeightedObjectives
=
Arrays
.
copyOf
(
WeightedObjectives
,
WeightedObjectives
.
length
);
if
(
fitnessLevel
!=
null
)
copy
.
fitnessLevel
=
Arrays
.
copyOf
(
fitnessLevel
,
fitnessLevel
.
length
);
if
(
LowerBoundObjectives
!=
null
)
copy
.
LowerBoundObjectives
=
Arrays
.
copyOf
(
LowerBoundObjectives
,
LowerBoundObjectives
.
length
);
copy
.
WeightedObjective
=
WeightedObjective
;
copy
.
Makespan
=
Makespan
;
copy
.
TotalFlowTime
=
TotalFlowTime
;
copy
.
TotalChangeoverTime
=
TotalChangeoverTime
;
copy
.
MachineLoadStd
=
MachineLoadStd
;
copy
.
Fitness
=
Fitness
;
copy
.
Rank
=
Rank
;
copy
.
CrowdingDistance
=
CrowdingDistance
;
copy
.
gsOrls
=
gsOrls
;
copy
.
generateType
=
generateType
+
"_copy"
;
copy
.
globalParamSnapshot
=
globalParamSnapshot
;
copy
.
objectiveWeights
=
objectiveWeights
;
copy
.
globalOpList
=
globalOpList
;
copy
.
allOperations
=
allOperations
;
copy
.
orders
=
orders
;
copy
.
InitMachines
=
InitMachines
;
copy
.
materials
=
materials
;
copy
.
materialIds
=
materialIds
;
copy
.
OperatRel
=
OperatRel
;
copy
.
orderMaterials
=
orderMaterials
;
copy
.
Machines
=
Machines
;
if
(
Result
!=
null
)
copy
.
Result
=
new
CopyOnWriteArrayList
<>(
Result
);
return
copy
;
}
}
src/main/java/com/aps/entity/basic/Entry.java
View file @
41cab0d7
...
...
@@ -85,6 +85,9 @@ public class Entry {
* 工序顺序
*/
private
int
sequence
;
private
double
minProcessingTime
;
// 加工时间 (秒)
/**
* 可选设备列表
*/
...
...
src/main/java/com/aps/service/Algorithm/AdaptiveLargeNeighborhoodSearch.java
0 → 100644
View file @
41cab0d7
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
;
import
com.aps.entity.basic.*
;
import
java.util.*
;
import
java.util.concurrent.CopyOnWriteArrayList
;
import
java.util.stream.Collectors
;
/**
* 自适应大邻域搜索(ALNS)算法。
*
* <p>核心思想:
* <ol>
* <li>多个 Destroy 算子(破坏解的一部分) + 多个 Repair 算子(修复被破坏的解)</li>
* <li>自适应权重:根据算子表现动态调整选择概率</li>
* <li>模拟退火接受准则:以一定概率接受劣解,概率随迭代递减</li>
* </ol>
*
* <p>与 VNS 的区别:
* <ul>
* <li>VNS 系统性地切换邻域结构(换设备 → 工序前移 → 工序交换)</li>
* <li>ALNS 随机选择 destroy/repair 组合,按权重进行 roulette wheel 选择</li>
* </ul>
*
* <p>架构:
* <pre>
* ALNS.search(chromosome, tabuSearch, vns, decoder, machines)
* │
* ├─ 每个迭代:
* │ ├─ roulette select destroy operator
* │ ├─ roulette select repair operator
* │ ├─ destroy(chromosome) → partial solution
* │ ├─ repair(partial solution) → candidate
* │ ├─ tabu check
* │ ├─ localSearch(candidate)
* │ ├─ SA acceptance
* │ └─ update operator weights
* │
* └─ return best
* </pre>
*
* 作者:佟礼
*/
public
class
AdaptiveLargeNeighborhoodSearch
{
// ==================== 随机数 ====================
private
final
Random
rnd
=
new
Random
();
// ==================== ALNS 核心参数 ====================
private
static
final
int
MAX_ITERATIONS
=
120
;
// 最大迭代次数
private
static
final
int
MAX_NO_IMPROVE_ITERATIONS
=
25
;
// 最大连续无改进迭代
private
static
final
int
SEGMENT_SIZE
=
10
;
// 权重更新段大小(每 SEGMENT_SIZE 迭代更新一次权重)
private
static
final
double
DESTROY_RATIO_MIN
=
0.10
;
// 最小破坏比例(提高以增强探索)
private
static
final
double
DESTROY_RATIO_MAX
=
0.40
;
// 最大破坏比例
private
static
final
int
MAX_RETRY_ATTEMPTS
=
2
;
// 最多重试次数(首次失败后换组合)
// ==================== 自适应权重参数 ====================
private
static
final
double
INITIAL_WEIGHT
=
1.0
;
// 初始权重
private
static
final
double
REACTION_FACTOR
=
0.7
;
// 反应因子(历史权重与新分的比例)
private
static
final
double
MIN_WEIGHT
=
0.1
;
// 最小权重(防止算子完全熄灭)
// ==================== 分数增量(越大越好) ====================
private
static
final
double
SCORE_NEW_BEST
=
3.0
;
// 发现新的全局最优解
private
static
final
double
SCORE_BETTER
=
1.5
;
// 比当前解更好
private
static
final
double
SCORE_ACCEPTED
=
0.8
;
// 劣解被接受
private
static
final
double
SCORE_REJECTED
=
0.1
;
// 劣解被拒绝
// ==================== 模拟退火参数 ====================
private
static
final
double
INITIAL_TEMPERATURE
=
0.5
;
// 初始温度
private
static
final
double
COOLING_RATE
=
0.98
;
// 冷却速率(加快冷却,使后期更聚焦)
private
static
final
double
FINAL_TEMPERATURE
=
0.01
;
// 最终温度
// ==================== 改进判断参数 ====================
private
static
final
double
SIGNIFICANT_IMPROVEMENT_THRESHOLD
=
1
e
-
11
;
private
static
final
double
MINOR_IMPROVEMENT_THRESHOLD
=
0.0
;
// ==================== 多轮修复参数 ====================
private
static
final
int
MULTI_PASS_COUNT
=
3
;
// 多轮修复尝试次数
// ==================== 时间预算 ====================
private
static
final
long
ALNS_TIME_BUDGET_MS
=
15L
*
60L
*
1000L
;
private
static
final
long
ALNS_PER_ITER_BUDGET_MS
=
17L
*
1000L
;
// ==================== 依赖项 ====================
private
final
List
<
Entry
>
allOperations
;
private
final
FitnessCalculator
fitnessCalculator
;
private
final
List
<
Order
>
orders
;
private
final
TreeMap
<
String
,
Material
>
materials
;
private
final
List
<
GroupResult
>
entryRel
;
private
final
Map
<
Integer
,
Entry
>
entryByIds
;
// ==================== 缓存解码数据 ====================
private
List
<
Machine
>
cachedMachines
;
private
List
<
Order
>
cachedOrders
;
private
List
<
GroupResult
>
cachedEntryRel
;
private
TreeMap
<
String
,
Material
>
cachedMaterials
;
private
List
<
Entry
>
cachedAllOperations
;
// ==================== 算子注册表 ====================
private
final
List
<
DestroyOperator
>
destroyOperators
=
new
ArrayList
<>();
private
final
List
<
RepairOperator
>
repairOperators
=
new
ArrayList
<>();
private
double
[]
destroyWeights
;
private
double
[]
destroyScores
;
private
int
[]
destroyUseCount
;
private
double
[]
repairWeights
;
private
double
[]
repairScores
;
private
int
[]
repairUseCount
;
// ==================== 构造函数 ====================
public
AdaptiveLargeNeighborhoodSearch
(
List
<
Entry
>
allOperations
,
List
<
Order
>
orders
,
TreeMap
<
String
,
Material
>
materials
,
List
<
GroupResult
>
entryRel
,
FitnessCalculator
fitnessCalculator
)
{
this
.
allOperations
=
allOperations
;
this
.
fitnessCalculator
=
fitnessCalculator
;
this
.
orders
=
orders
;
this
.
materials
=
materials
;
this
.
entryRel
=
entryRel
;
Map
<
Integer
,
Object
>
mp
=
buildEntryKey
();
this
.
entryByIds
=
(
Map
<
Integer
,
Entry
>)
mp
.
get
(
1
);
// 预缓存解码数据
this
.
cachedAllOperations
=
ProductionDeepCopyUtil
.
deepCopyList
(
new
CopyOnWriteArrayList
<>(
allOperations
),
Entry
.
class
);
this
.
cachedOrders
=
ProductionDeepCopyUtil
.
deepCopyList
(
new
CopyOnWriteArrayList
<>(
orders
),
Order
.
class
);
this
.
cachedEntryRel
=
ProductionDeepCopyUtil
.
deepCopyList
(
new
CopyOnWriteArrayList
<>(
entryRel
),
GroupResult
.
class
);
this
.
cachedMaterials
=
ProductionDeepCopyUtil
.
deepCopyTreeMap
(
materials
,
String
.
class
,
Material
.
class
);
// 注册 destroy 算子
registerDestroyOperators
();
// 注册 repair 算子
registerRepairOperators
();
// 初始化权重
initWeights
();
}
// ==================== 算子注册 ====================
private
void
registerDestroyOperators
()
{
// 1. 随机换设备:随机选择部分工序更换机器
destroyOperators
.
add
(
new
DestroyOperator
(
"RandomMachineChange"
,
this
::
destroyRandomMachineChange
));
// 2. 瓶颈工序移除:移除瓶颈设备上的工序
destroyOperators
.
add
(
new
DestroyOperator
(
"BottleneckOpRemoval"
,
this
::
destroyBottleneckOps
));
// 3. 延迟工序打乱:打乱导致延迟的工序
destroyOperators
.
add
(
new
DestroyOperator
(
"DelayOpShuffle"
,
this
::
destroyDelayOps
));
// 4. 随机区间打乱:随机打乱一段连续的工序序列
destroyOperators
.
add
(
new
DestroyOperator
(
"RandomSegmentShuffle"
,
this
::
destroyRandomSegment
));
// 5. 负载均衡迁移:将超载机器上的工序迁移到低负载机器
destroyOperators
.
add
(
new
DestroyOperator
(
"LoadBalanceTransfer"
,
this
::
destroyLoadBalanceTransfer
));
}
private
void
registerRepairOperators
()
{
// 1. 多轮修复:多次调用 VNS 邻域生成,选最优结果
repairOperators
.
add
(
new
RepairOperator
(
"MultiPassRepair"
,
this
::
repairMultiPass
));
// 2. 贪婪换设备:对打乱的工序重新选择最优机器
repairOperators
.
add
(
new
RepairOperator
(
"GreedyMachineReassign"
,
this
::
repairGreedyMachine
));
// 3. 局部搜索修复:解码后通过 VNS 生成邻域
repairOperators
.
add
(
new
RepairOperator
(
"LocalSearchRepair"
,
this
::
repairLocalSearch
));
}
private
void
initWeights
()
{
destroyWeights
=
new
double
[
destroyOperators
.
size
()];
destroyScores
=
new
double
[
destroyOperators
.
size
()];
destroyUseCount
=
new
int
[
destroyOperators
.
size
()];
Arrays
.
fill
(
destroyWeights
,
INITIAL_WEIGHT
);
repairWeights
=
new
double
[
repairOperators
.
size
()];
repairScores
=
new
double
[
repairOperators
.
size
()];
repairUseCount
=
new
int
[
repairOperators
.
size
()];
Arrays
.
fill
(
repairWeights
,
INITIAL_WEIGHT
);
}
// ====================================================================
// 搜索主循环
// ====================================================================
/**
* ALNS 搜索主循环。
*
* @param chromosome 初始解
* @param tabuSearch 禁忌表(多算法共享)
* @param vns 提供邻域生成与局部搜索
* @param decoder 解码器
* @param machines 机器列表
* @return 优化后的最优解
*/
public
Chromosome
search
(
Chromosome
chromosome
,
TabuSearch
tabuSearch
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
FileHelper
.
writeLogFile
(
"ALNS - 开始执行"
);
Chromosome
current
=
ProductionDeepCopyUtil
.
deepCopy
(
chromosome
,
Chromosome
.
class
);
Chromosome
best
=
ProductionDeepCopyUtil
.
deepCopy
(
chromosome
,
Chromosome
.
class
);
double
currentBestFitness
=
best
.
getFitness
();
int
iterations
=
0
;
int
improveCount
=
0
;
int
noImprovementCount
=
0
;
double
temperature
=
INITIAL_TEMPERATURE
;
// 时间预算
long
startTimeMs
=
System
.
currentTimeMillis
();
long
remainingBudgetMs
=
Math
.
max
(
5L
*
60L
*
1000L
,
ALNS_TIME_BUDGET_MS
/
2
);
int
timeBasedMaxIter
=
(
int
)
Math
.
max
(
20
,
remainingBudgetMs
/
ALNS_PER_ITER_BUDGET_MS
);
int
maxIterations
=
Math
.
min
(
MAX_ITERATIONS
,
Math
.
max
(
40
,
timeBasedMaxIter
));
FileHelper
.
writeLogFile
(
String
.
format
(
"ALNS - 参数: 最大迭代=%d, 初始温度=%.3f, 冷却率=%.3f, 破坏比例=%.0f%%-%.0f%%, 时间预算=%.1fmin"
,
maxIterations
,
INITIAL_TEMPERATURE
,
COOLING_RATE
,
DESTROY_RATIO_MIN
*
100
,
DESTROY_RATIO_MAX
*
100
,
(
double
)
remainingBudgetMs
/
60000.0
));
for
(
int
iter
=
0
;
iter
<
maxIterations
;
iter
++)
{
iterations
++;
decoder
.
DelOrder
(
current
);
// ---- 1. 自适应选择 destroy + repair 算子 ----
int
destroyIdx
=
rouletteSelect
(
destroyWeights
);
int
repairIdx
=
rouletteSelect
(
repairWeights
);
DestroyOperator
destroyOp
=
destroyOperators
.
get
(
destroyIdx
);
RepairOperator
repairOp
=
repairOperators
.
get
(
repairIdx
);
Chromosome
repaired
=
null
;
int
repairAttempt
=
0
;
// ---- 重试机制:最多尝试 MAX_RETRY_ATTEMPTS 次不同的 destroy+repair 组合 ----
for
(;
repairAttempt
<
MAX_RETRY_ATTEMPTS
;
repairAttempt
++)
{
// ---- 2. Destroy:破坏当前解 ----
Chromosome
destroyed
=
destroyOp
.
apply
(
current
,
DESTROY_RATIO_MIN
,
DESTROY_RATIO_MAX
);
if
(
destroyed
==
null
)
{
// 换其他 destroy 算子重试
int
newDestroyIdx
=
(
destroyIdx
+
1
+
rnd
.
nextInt
(
destroyOperators
.
size
()
-
1
))
%
destroyOperators
.
size
();
destroyIdx
=
newDestroyIdx
;
destroyOp
=
destroyOperators
.
get
(
destroyIdx
);
continue
;
}
// ---- 3. Repair:修复被破坏的解 ----
repaired
=
repairOp
.
apply
(
destroyed
,
vns
,
decoder
,
machines
);
if
(
repaired
!=
null
)
{
break
;
// 修复成功,退出重试
}
// 修复失败,换算子重试
int
newRepairIdx
=
(
repairIdx
+
1
+
rnd
.
nextInt
(
repairOperators
.
size
()
-
1
))
%
repairOperators
.
size
();
repairIdx
=
newRepairIdx
;
repairOp
=
repairOperators
.
get
(
repairIdx
);
}
if
(
repaired
==
null
)
{
noImprovementCount
++;
updateOperatorScores
(
destroyIdx
,
repairIdx
,
SCORE_REJECTED
,
false
);
continue
;
}
// ---- 4. 禁忌检查 ----
boolean
tabuHit
=
tabuSearch
.
isChromosomeTabu
(
repaired
);
// ---- 5. 解码 ----
decode
(
decoder
,
repaired
,
machines
);
tabuSearch
.
addChromosomeToTabu
(
repaired
);
// ---- 6. 接受准则 ----
boolean
betterThanBest
=
isBetter
(
repaired
,
best
);
boolean
betterThanCurrent
=
isBetter
(
repaired
,
current
);
boolean
accept
;
double
score
;
if
(
betterThanBest
)
{
// 渴望准则:无条件接受
accept
=
true
;
score
=
SCORE_NEW_BEST
;
}
else
if
(
betterThanCurrent
)
{
accept
=
true
;
score
=
SCORE_BETTER
;
}
else
if
(!
tabuHit
)
{
// 模拟退火:接受劣解
double
delta
=
current
.
getFitness
()
-
repaired
.
getFitness
();
// 负值表示更差
double
acceptProb
=
Math
.
exp
(
delta
/
temperature
);
accept
=
rnd
.
nextDouble
()
<
acceptProb
;
score
=
accept
?
SCORE_ACCEPTED
:
SCORE_REJECTED
;
}
else
{
accept
=
false
;
score
=
SCORE_REJECTED
;
}
updateOperatorScores
(
destroyIdx
,
repairIdx
,
score
,
true
);
if
(
accept
)
{
current
=
lightCopy
(
repaired
);
if
(
betterThanBest
)
{
best
=
lightCopy
(
repaired
);
improveCount
++;
double
delta
=
best
.
getFitness
()
-
currentBestFitness
;
if
(
delta
>
MINOR_IMPROVEMENT_THRESHOLD
)
{
noImprovementCount
=
0
;
currentBestFitness
=
best
.
getFitness
();
if
(
delta
>
SIGNIFICANT_IMPROVEMENT_THRESHOLD
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"ALNS - 找到更好解(显著), 迭代=%d, fitness=%.12f, destroy=%s, repair=%s"
,
iterations
,
best
.
getFitness
(),
destroyOp
.
name
,
repairOp
.
name
));
}
else
{
FileHelper
.
writeLogFile
(
String
.
format
(
"ALNS - 找到更好解(微小), 迭代=%d, fitness=%.12f, delta=%.2e, destroy=%s, repair=%s"
,
iterations
,
best
.
getFitness
(),
delta
,
destroyOp
.
name
,
repairOp
.
name
));
}
}
}
}
else
{
noImprovementCount
++;
}
// ---- 7. 温度冷却 ----
temperature
*=
COOLING_RATE
;
if
(
temperature
<
FINAL_TEMPERATURE
)
{
temperature
=
FINAL_TEMPERATURE
;
}
// ---- 8. 段更新权重 ----
if
((
iter
+
1
)
%
SEGMENT_SIZE
==
0
)
{
updateWeights
();
}
// ---- 9. 提前停止 ----
if
(
noImprovementCount
>=
MAX_NO_IMPROVE_ITERATIONS
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"ALNS - 提前停止: 连续%d次无改进"
,
MAX_NO_IMPROVE_ITERATIONS
));
break
;
}
long
elapsedMs
=
System
.
currentTimeMillis
()
-
startTimeMs
;
if
(
elapsedMs
>
remainingBudgetMs
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"ALNS - 提前停止: 达到时间预算(%.1fmin)"
,
elapsedMs
/
60000.0
));
break
;
}
// 每 10 次迭代输出一次状态
if
((
iter
+
1
)
%
10
==
0
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"ALNS - 迭代%d/%d, 改进=%d, 无改进连续=%d, T=%.4f, fitness=%.12f"
,
iterations
,
maxIterations
,
improveCount
,
noImprovementCount
,
temperature
,
best
.
getFitness
()));
}
}
// 最终权重更新
updateWeights
();
FileHelper
.
writeLogFile
(
String
.
format
(
"ALNS - 结束: 总迭代=%d, 改进次数=%d, 最终fitness=%.12f, 最终温度=%.4f"
,
iterations
,
improveCount
,
best
.
getFitness
(),
temperature
));
FileHelper
.
writeLogFile
(
KpiLowerBoundCalculator
.
generateGapReport
(
best
));
logOperatorStats
();
return
best
;
}
/**
* 轻量拷贝:只复制 generateNeighbor/DelOrder 需要的字段,避免全量 JSON 深拷贝导致 OOM。
* result/machines/operatRel 等重型数据共享引用(generateNeighbor 只读,不修改)。
*/
private
Chromosome
lightCopy
(
Chromosome
source
)
{
Chromosome
copy
=
new
Chromosome
();
copy
.
setOperationSequencing
(
new
CopyOnWriteArrayList
<>(
source
.
getOperationSequencing
()));
copy
.
setMachineSelection
(
new
CopyOnWriteArrayList
<>(
source
.
getMachineSelection
()));
copy
.
setGlobalOpList
(
new
CopyOnWriteArrayList
<>(
source
.
getGlobalOpList
()));
copy
.
setOrders
(
new
CopyOnWriteArrayList
<>(
source
.
getOrders
()));
copy
.
setAllOperations
(
new
CopyOnWriteArrayList
<>(
source
.
getAllOperations
()));
copy
.
setResult
(
source
.
getResult
());
copy
.
setMachines
(
source
.
getMachines
());
copy
.
setOperatRel
(
new
CopyOnWriteArrayList
<>(
source
.
getOperatRel
()));
copy
.
setScenarioID
(
source
.
getScenarioID
());
copy
.
setBaseTime
(
source
.
getBaseTime
());
copy
.
setGenerateType
(
source
.
getGenerateType
());
copy
.
setFitnessLevel
(
source
.
getFitnessLevel
());
copy
.
setFitness
(
source
.
getFitness
());
return
copy
;
}
// ====================================================================
// Destroy 算子(破坏部分解)
// ====================================================================
/**
* Destroy 1: 随机换设备。
* 随机选择 destroyRatio 比例的工序,为其更换机器(如果有多个机器选项)。
*/
private
Chromosome
destroyRandomMachineChange
(
Chromosome
c
,
double
destroyRatioMin
,
double
destroyRatioMax
)
{
List
<
GAScheduleResult
>
results
=
c
.
getResult
();
if
(
results
==
null
||
results
.
isEmpty
())
return
c
;
// 只选择有多个机器选项的工序
List
<
GAScheduleResult
>
candidates
=
results
.
stream
()
.
filter
(
r
->
{
Entry
e
=
entryByIds
.
get
(
r
.
getOperationId
());
return
e
!=
null
&&
e
.
getMachineOptions
()
!=
null
&&
e
.
getMachineOptions
().
size
()
>
1
;
})
.
collect
(
Collectors
.
toList
());
if
(
candidates
.
isEmpty
())
return
c
;
double
ratio
=
destroyRatioMin
+
rnd
.
nextDouble
()
*
(
destroyRatioMax
-
destroyRatioMin
);
int
destroyCount
=
Math
.
max
(
1
,
(
int
)
(
candidates
.
size
()
*
ratio
));
Collections
.
shuffle
(
candidates
,
rnd
);
Chromosome
copy
=
copyChromosome
(
c
);
CopyOnWriteArrayList
<
Integer
>
machineSel
=
copy
.
getMachineSelection
();
if
(
machineSel
==
null
)
return
copy
;
// 构建正确的 machineSelection 位置索引:groupId_sequence → globalOpList 索引
Map
<
String
,
Integer
>
machinePosIndex
=
new
HashMap
<>();
List
<
GlobalOperationInfo
>
globalOpList
=
c
.
getGlobalOpList
();
for
(
int
i
=
0
;
i
<
globalOpList
.
size
();
i
++)
{
Entry
op
=
globalOpList
.
get
(
i
).
getOp
();
machinePosIndex
.
put
(
op
.
getGroupId
()
+
"_"
+
op
.
getSequence
(),
i
);
}
for
(
int
i
=
0
;
i
<
Math
.
min
(
destroyCount
,
candidates
.
size
());
i
++)
{
GAScheduleResult
sr
=
candidates
.
get
(
i
);
Entry
entry
=
entryByIds
.
get
(
sr
.
getOperationId
());
if
(
entry
==
null
||
entry
.
getMachineOptions
()
==
null
)
continue
;
List
<
MachineOption
>
options
=
entry
.
getMachineOptions
();
if
(
options
.
size
()
<=
1
)
continue
;
String
key
=
entry
.
getGroupId
()
+
"_"
+
entry
.
getSequence
();
Integer
pos
=
machinePosIndex
.
get
(
key
);
if
(
pos
==
null
||
pos
>=
machineSel
.
size
())
continue
;
// 随机选一个不同的机器(machineSelection 存的是 1-based 序号,不是 machineId)
int
currentIdx
=
machineSel
.
get
(
pos
)
-
1
;
// 转为 0-based
int
newIdx
;
if
(
options
.
size
()
==
2
)
{
newIdx
=
(
currentIdx
==
0
)
?
1
:
0
;
}
else
{
do
{
newIdx
=
rnd
.
nextInt
(
options
.
size
());
}
while
(
newIdx
==
currentIdx
);
}
machineSel
.
set
(
pos
,
newIdx
+
1
);
}
return
copy
;
}
/**
* Destroy 2: 瓶颈工序移除。
* 找到瓶颈设备上的工序,打乱它们在 operationSequencing 中的位置。
*/
private
Chromosome
destroyBottleneckOps
(
Chromosome
c
,
double
destroyRatioMin
,
double
destroyRatioMax
)
{
List
<
GAScheduleResult
>
results
=
c
.
getResult
();
if
(
results
==
null
||
results
.
isEmpty
())
return
c
;
// 简单瓶颈识别:利用率最高的机器
Map
<
Long
,
Double
>
utilMap
=
new
HashMap
<>();
Map
<
Long
,
Long
>
timeMap
=
new
HashMap
<>();
for
(
GAScheduleResult
r
:
results
)
{
Long
mid
=
r
.
getMachineId
();
utilMap
.
merge
(
mid
,
r
.
getProcessingTime
(),
Double:
:
sum
);
timeMap
.
merge
(
mid
,
(
long
)
(
r
.
getEndTime
()
-
r
.
getStartTime
()),
Long:
:
sum
);
}
Long
bottleneckMachineId
=
utilMap
.
entrySet
().
stream
()
.
max
(
Map
.
Entry
.
comparingByValue
())
.
map
(
Map
.
Entry
::
getKey
)
.
orElse
(
null
);
if
(
bottleneckMachineId
==
null
)
return
c
;
List
<
GAScheduleResult
>
bottleneckOps
=
results
.
stream
()
.
filter
(
r
->
bottleneckMachineId
.
equals
(
r
.
getMachineId
()))
.
collect
(
Collectors
.
toList
());
if
(
bottleneckOps
.
size
()
<
2
)
return
c
;
double
ratio
=
destroyRatioMin
+
rnd
.
nextDouble
()
*
(
destroyRatioMax
-
destroyRatioMin
);
int
destroyCount
=
Math
.
max
(
2
,
(
int
)
(
bottleneckOps
.
size
()
*
ratio
));
Chromosome
copy
=
copyChromosome
(
c
);
CopyOnWriteArrayList
<
Integer
>
opSeq
=
copy
.
getOperationSequencing
();
if
(
opSeq
==
null
)
return
copy
;
// 找到瓶颈工序在 opSeq 中的位置
Set
<
Integer
>
bottleneckEntryIds
=
bottleneckOps
.
stream
()
.
map
(
GAScheduleResult:
:
getOperationId
)
.
collect
(
Collectors
.
toSet
());
List
<
Integer
>
bottleneckPositions
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
opSeq
.
size
();
i
++)
{
if
(
bottleneckEntryIds
.
contains
(
opSeq
.
get
(
i
)))
{
bottleneckPositions
.
add
(
i
);
}
}
if
(
bottleneckPositions
.
size
()
<
2
)
return
copy
;
// 随机选择 destroyCount 个位置,打乱它们的值
Collections
.
shuffle
(
bottleneckPositions
,
rnd
);
List
<
Integer
>
selectedPositions
=
bottleneckPositions
.
subList
(
0
,
Math
.
min
(
destroyCount
,
bottleneckPositions
.
size
()));
List
<
Integer
>
values
=
new
ArrayList
<>();
for
(
int
pos
:
selectedPositions
)
{
values
.
add
(
opSeq
.
get
(
pos
));
}
Collections
.
shuffle
(
values
,
rnd
);
for
(
int
j
=
0
;
j
<
selectedPositions
.
size
();
j
++)
{
opSeq
.
set
(
selectedPositions
.
get
(
j
),
values
.
get
(
j
));
}
return
copy
;
}
/**
* Destroy 3: 延迟工序打乱。
* 找到导致延迟的工序,打乱它们的机器选择。
*/
private
Chromosome
destroyDelayOps
(
Chromosome
c
,
double
destroyRatioMin
,
double
destroyRatioMax
)
{
List
<
Order
>
orderList
=
c
.
getOrders
();
if
(
orderList
==
null
||
orderList
.
isEmpty
())
return
c
;
// 找到延迟的订单
Set
<
String
>
delayedOrderIds
=
orderList
.
stream
()
.
filter
(
o
->
o
.
getDelayHours
()
>
0
)
.
map
(
Order:
:
getOrderId
)
.
collect
(
Collectors
.
toSet
());
if
(
delayedOrderIds
.
isEmpty
())
return
c
;
List
<
GAScheduleResult
>
results
=
c
.
getResult
();
if
(
results
==
null
||
results
.
isEmpty
())
return
c
;
List
<
GAScheduleResult
>
delayedResults
=
results
.
stream
()
.
filter
(
r
->
delayedOrderIds
.
contains
(
r
.
getOrderId
()))
.
collect
(
Collectors
.
toList
());
if
(
delayedResults
.
isEmpty
())
return
c
;
double
ratio
=
destroyRatioMin
+
rnd
.
nextDouble
()
*
(
destroyRatioMax
-
destroyRatioMin
);
int
destroyCount
=
Math
.
max
(
1
,
(
int
)
(
delayedResults
.
size
()
*
ratio
));
Collections
.
shuffle
(
delayedResults
,
rnd
);
Chromosome
copy
=
copyChromosome
(
c
);
CopyOnWriteArrayList
<
Integer
>
machineSel
=
copy
.
getMachineSelection
();
if
(
machineSel
==
null
)
return
copy
;
// 构建正确的 machineSelection 位置索引:groupId_sequence → globalOpList 索引
Map
<
String
,
Integer
>
machinePosIndex
=
new
HashMap
<>();
List
<
GlobalOperationInfo
>
globalOpList
=
c
.
getGlobalOpList
();
for
(
int
i
=
0
;
i
<
globalOpList
.
size
();
i
++)
{
Entry
op
=
globalOpList
.
get
(
i
).
getOp
();
machinePosIndex
.
put
(
op
.
getGroupId
()
+
"_"
+
op
.
getSequence
(),
i
);
}
for
(
int
i
=
0
;
i
<
Math
.
min
(
destroyCount
,
delayedResults
.
size
());
i
++)
{
GAScheduleResult
sr
=
delayedResults
.
get
(
i
);
Entry
entry
=
entryByIds
.
get
(
sr
.
getOperationId
());
if
(
entry
==
null
||
entry
.
getMachineOptions
()
==
null
||
entry
.
getMachineOptions
().
isEmpty
())
continue
;
List
<
MachineOption
>
options
=
entry
.
getMachineOptions
();
if
(
options
.
size
()
<=
1
)
continue
;
String
key
=
entry
.
getGroupId
()
+
"_"
+
entry
.
getSequence
();
Integer
pos
=
machinePosIndex
.
get
(
key
);
if
(
pos
==
null
||
pos
>=
machineSel
.
size
())
continue
;
// 随机选一个不同的机器(machineSelection 存的是 1-based 序号,不是 machineId)
int
currentIdx
=
machineSel
.
get
(
pos
)
-
1
;
int
newIdx
;
if
(
options
.
size
()
==
2
)
{
newIdx
=
(
currentIdx
==
0
)
?
1
:
0
;
}
else
{
do
{
newIdx
=
rnd
.
nextInt
(
options
.
size
());
}
while
(
newIdx
==
currentIdx
);
}
machineSel
.
set
(
pos
,
newIdx
+
1
);
}
return
copy
;
}
/**
* Destroy 4: 随机区间打乱。
* 在 operationSequencing 中随机选一段连续区间,打乱其内部顺序。
*/
private
Chromosome
destroyRandomSegment
(
Chromosome
c
,
double
destroyRatioMin
,
double
destroyRatioMax
)
{
CopyOnWriteArrayList
<
Integer
>
opSeq
=
c
.
getOperationSequencing
();
if
(
opSeq
==
null
||
opSeq
.
size
()
<
4
)
return
c
;
Chromosome
copy
=
copyChromosome
(
c
);
CopyOnWriteArrayList
<
Integer
>
seq
=
copy
.
getOperationSequencing
();
if
(
seq
==
null
)
return
copy
;
double
ratio
=
destroyRatioMin
+
rnd
.
nextDouble
()
*
(
destroyRatioMax
-
destroyRatioMin
);
int
segmentLen
=
Math
.
max
(
2
,
(
int
)
(
seq
.
size
()
*
ratio
));
int
start
=
rnd
.
nextInt
(
Math
.
max
(
1
,
seq
.
size
()
-
segmentLen
));
int
end
=
Math
.
min
(
seq
.
size
(),
start
+
segmentLen
);
// 打乱 [start, end) 区间
List
<
Integer
>
subList
=
new
ArrayList
<>(
seq
.
subList
(
start
,
end
));
Collections
.
shuffle
(
subList
,
rnd
);
for
(
int
i
=
start
;
i
<
end
;
i
++)
{
seq
.
set
(
i
,
subList
.
get
(
i
-
start
));
}
return
copy
;
}
/**
* Destroy 5: 负载均衡迁移。
* 利用解码后的调度数据,找出利用率最高和最低的机器,
* 将超载机器上有多机器选项的工序迁移到低负载机器上。
*/
private
Chromosome
destroyLoadBalanceTransfer
(
Chromosome
c
,
double
destroyRatioMin
,
double
destroyRatioMax
)
{
List
<
GAScheduleResult
>
results
=
c
.
getResult
();
if
(
results
==
null
||
results
.
isEmpty
())
return
c
;
// 构建每台机器的利用率数据
Map
<
Long
,
MachineUtilInfo
>
machineUtilMap
=
buildMachineUtilization
(
results
);
if
(
machineUtilMap
.
size
()
<
2
)
return
c
;
// 找出超载机器(利用率 > 70%)和低负载机器(利用率 < 40%)
List
<
Long
>
overloadedMachines
=
new
ArrayList
<>();
List
<
Long
>
underloadedMachines
=
new
ArrayList
<>();
double
totalSpan
=
0
;
for
(
MachineUtilInfo
info
:
machineUtilMap
.
values
())
{
totalSpan
=
Math
.
max
(
totalSpan
,
info
.
span
);
}
for
(
Map
.
Entry
<
Long
,
MachineUtilInfo
>
entry
:
machineUtilMap
.
entrySet
())
{
double
util
=
totalSpan
>
0
?
entry
.
getValue
().
span
/
totalSpan
*
100
:
0
;
if
(
util
>
70
)
overloadedMachines
.
add
(
entry
.
getKey
());
if
(
util
<
40
)
underloadedMachines
.
add
(
entry
.
getKey
());
}
if
(
overloadedMachines
.
isEmpty
()
||
underloadedMachines
.
isEmpty
())
return
c
;
// 收集超载机器上有多个机器选项的工序
Set
<
Long
>
overloadedSet
=
new
HashSet
<>(
overloadedMachines
);
List
<
GAScheduleResult
>
candidates
=
results
.
stream
()
.
filter
(
r
->
overloadedSet
.
contains
(
r
.
getMachineId
()))
.
filter
(
r
->
{
Entry
e
=
entryByIds
.
get
(
r
.
getOperationId
());
return
e
!=
null
&&
e
.
getMachineOptions
()
!=
null
&&
e
.
getMachineOptions
().
size
()
>
1
;
})
.
collect
(
Collectors
.
toList
());
if
(
candidates
.
isEmpty
())
return
c
;
double
ratio
=
destroyRatioMin
+
rnd
.
nextDouble
()
*
(
destroyRatioMax
-
destroyRatioMin
);
int
destroyCount
=
Math
.
max
(
1
,
(
int
)
(
candidates
.
size
()
*
ratio
));
Collections
.
shuffle
(
candidates
,
rnd
);
Chromosome
copy
=
copyChromosome
(
c
);
CopyOnWriteArrayList
<
Integer
>
machineSel
=
copy
.
getMachineSelection
();
if
(
machineSel
==
null
)
return
copy
;
// 构建 machineSelection 位置索引
Map
<
String
,
Integer
>
machinePosIndex
=
new
HashMap
<>();
List
<
GlobalOperationInfo
>
globalOpList
=
c
.
getGlobalOpList
();
for
(
int
i
=
0
;
i
<
globalOpList
.
size
();
i
++)
{
Entry
op
=
globalOpList
.
get
(
i
).
getOp
();
machinePosIndex
.
put
(
op
.
getGroupId
()
+
"_"
+
op
.
getSequence
(),
i
);
}
// 构建低负载机器可用的 machineId 集合
Set
<
Long
>
underloadedMachineIds
=
new
HashSet
<>(
underloadedMachines
);
int
transferred
=
0
;
for
(
int
i
=
0
;
i
<
Math
.
min
(
destroyCount
,
candidates
.
size
());
i
++)
{
GAScheduleResult
sr
=
candidates
.
get
(
i
);
Entry
entry
=
entryByIds
.
get
(
sr
.
getOperationId
());
if
(
entry
==
null
||
entry
.
getMachineOptions
()
==
null
)
continue
;
List
<
MachineOption
>
options
=
entry
.
getMachineOptions
();
if
(
options
.
size
()
<=
1
)
continue
;
// 找到该工序可用的低负载机器选项
List
<
Integer
>
lowLoadIndices
=
new
ArrayList
<>();
for
(
int
j
=
0
;
j
<
options
.
size
();
j
++)
{
if
(
underloadedMachineIds
.
contains
(
options
.
get
(
j
).
getMachineId
()))
{
lowLoadIndices
.
add
(
j
);
}
}
if
(
lowLoadIndices
.
isEmpty
())
continue
;
String
key
=
entry
.
getGroupId
()
+
"_"
+
entry
.
getSequence
();
Integer
pos
=
machinePosIndex
.
get
(
key
);
if
(
pos
==
null
||
pos
>=
machineSel
.
size
())
continue
;
// 随机选一个低负载机器
int
newIdx
=
lowLoadIndices
.
get
(
rnd
.
nextInt
(
lowLoadIndices
.
size
()));
machineSel
.
set
(
pos
,
newIdx
+
1
);
transferred
++;
}
if
(
transferred
>
0
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"ALNS-负载均衡迁移: 超载机器=%d台, 低负载机器=%d台, 转移工序=%d/%d"
,
overloadedMachines
.
size
(),
underloadedMachines
.
size
(),
transferred
,
destroyCount
));
}
return
copy
;
}
// ====================================================================
// Repair 算子(修复被破坏的解)
// ====================================================================
/**
* Repair 1: 多轮修复。
* 多次调用 VNS 的 generateNeighbor,每次解码并比较,选最优结果。
* 这比单次 generateNeighbor 更有可能找到改进。
*/
private
Chromosome
repairMultiPass
(
Chromosome
c
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
Chromosome
base
=
copyChromosome
(
c
);
decode
(
decoder
,
base
,
machines
);
Chromosome
bestRepair
=
base
;
double
bestRepairFitness
=
base
.
getFitness
();
for
(
int
pass
=
0
;
pass
<
MULTI_PASS_COUNT
;
pass
++)
{
// 基于当前最优修复结果生成邻域
Chromosome
neighbor
=
vns
.
generateNeighbor
(
bestRepair
);
if
(
neighbor
==
null
)
continue
;
// 解码邻域
Chromosome
neighborCopy
=
copyChromosome
(
neighbor
);
neighborCopy
.
setResult
(
new
CopyOnWriteArrayList
<>());
decode
(
decoder
,
neighborCopy
,
machines
);
if
(
isBetter
(
neighborCopy
,
bestRepair
))
{
bestRepair
=
neighborCopy
;
bestRepairFitness
=
bestRepair
.
getFitness
();
}
}
return
bestRepair
;
}
/**
* Repair 2: 贪婪换设备。
* 通过 VNS 的 generateNeighbor 生成邻居后,再调用局部搜索修复。
*/
private
Chromosome
repairGreedyMachine
(
Chromosome
c
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
// 通过 VNS 的瓶颈感知策略生成一个邻居
Chromosome
neighbor
=
vns
.
generateNeighbor
(
c
);
if
(
neighbor
==
null
)
return
c
;
return
neighbor
;
}
/**
* Repair 3: 局部搜索修复。
* 通过 VNS 的局部搜索对解进行修复优化。
*/
private
Chromosome
repairLocalSearch
(
Chromosome
c
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
// 深拷贝后解码,再通过 VNS 的 generateNeighbor 生成一个邻居
Chromosome
copy
=
copyChromosome
(
c
);
decode
(
decoder
,
copy
,
machines
);
Chromosome
neighbor
=
vns
.
generateNeighbor
(
copy
);
return
neighbor
!=
null
?
neighbor
:
copy
;
}
// ====================================================================
// 自适应权重管理
// ====================================================================
/**
* 轮盘赌选择算子。
*/
private
int
rouletteSelect
(
double
[]
weights
)
{
double
total
=
0.0
;
for
(
double
w
:
weights
)
total
+=
w
;
if
(
total
<=
0
)
return
rnd
.
nextInt
(
weights
.
length
);
double
rand
=
rnd
.
nextDouble
()
*
total
;
double
cumulative
=
0.0
;
for
(
int
i
=
0
;
i
<
weights
.
length
;
i
++)
{
cumulative
+=
weights
[
i
];
if
(
rand
<=
cumulative
)
return
i
;
}
return
weights
.
length
-
1
;
}
/**
* 更新算子分数(累加到此段的 score 中)。
*/
private
void
updateOperatorScores
(
int
destroyIdx
,
int
repairIdx
,
double
score
,
boolean
used
)
{
destroyScores
[
destroyIdx
]
+=
score
;
repairScores
[
repairIdx
]
+=
score
;
if
(
used
)
{
destroyUseCount
[
destroyIdx
]++;
repairUseCount
[
repairIdx
]++;
}
}
/**
* 段结束时更新权重。
* 权重 = reactionFactor * 旧权重 + (1 - reactionFactor) * (段分数 / 使用次数)
*/
private
void
updateWeights
()
{
for
(
int
i
=
0
;
i
<
destroyWeights
.
length
;
i
++)
{
double
avgScore
=
destroyUseCount
[
i
]
>
0
?
destroyScores
[
i
]
/
destroyUseCount
[
i
]
:
0.0
;
destroyWeights
[
i
]
=
Math
.
max
(
MIN_WEIGHT
,
REACTION_FACTOR
*
destroyWeights
[
i
]
+
(
1
-
REACTION_FACTOR
)
*
avgScore
);
destroyScores
[
i
]
=
0.0
;
destroyUseCount
[
i
]
=
0
;
}
for
(
int
i
=
0
;
i
<
repairWeights
.
length
;
i
++)
{
double
avgScore
=
repairUseCount
[
i
]
>
0
?
repairScores
[
i
]
/
repairUseCount
[
i
]
:
0.0
;
repairWeights
[
i
]
=
Math
.
max
(
MIN_WEIGHT
,
REACTION_FACTOR
*
repairWeights
[
i
]
+
(
1
-
REACTION_FACTOR
)
*
avgScore
);
repairScores
[
i
]
=
0.0
;
repairUseCount
[
i
]
=
0
;
}
}
/**
* 输出算子权重统计。
*/
private
void
logOperatorStats
()
{
StringBuilder
sb
=
new
StringBuilder
(
"ALNS - 算子权重: "
);
sb
.
append
(
"Destroy["
);
for
(
int
i
=
0
;
i
<
destroyOperators
.
size
();
i
++)
{
sb
.
append
(
String
.
format
(
"%s=%.3f"
,
destroyOperators
.
get
(
i
).
name
,
destroyWeights
[
i
]));
if
(
i
<
destroyOperators
.
size
()
-
1
)
sb
.
append
(
", "
);
}
sb
.
append
(
"] Repair["
);
for
(
int
i
=
0
;
i
<
repairOperators
.
size
();
i
++)
{
sb
.
append
(
String
.
format
(
"%s=%.3f"
,
repairOperators
.
get
(
i
).
name
,
repairWeights
[
i
]));
if
(
i
<
repairOperators
.
size
()
-
1
)
sb
.
append
(
", "
);
}
sb
.
append
(
"]"
);
FileHelper
.
writeLogFile
(
sb
.
toString
());
}
// ====================================================================
// 辅助方法(复用 VNS 风格)
// ====================================================================
/**
* 从调度结果构建每台机器的利用率数据。
*/
private
Map
<
Long
,
MachineUtilInfo
>
buildMachineUtilization
(
List
<
GAScheduleResult
>
results
)
{
Map
<
Long
,
MachineUtilInfo
>
map
=
new
HashMap
<>();
for
(
GAScheduleResult
r
:
results
)
{
Long
mid
=
r
.
getMachineId
();
MachineUtilInfo
info
=
map
.
get
(
mid
);
if
(
info
==
null
)
{
info
=
new
MachineUtilInfo
();
info
.
machineId
=
mid
;
info
.
minStart
=
r
.
getStartTime
();
info
.
maxEnd
=
r
.
getEndTime
();
info
.
opCount
=
1
;
info
.
totalWork
=
r
.
getProcessingTime
();
map
.
put
(
mid
,
info
);
}
else
{
info
.
minStart
=
Math
.
min
(
info
.
minStart
,
r
.
getStartTime
());
info
.
maxEnd
=
Math
.
max
(
info
.
maxEnd
,
r
.
getEndTime
());
info
.
opCount
++;
info
.
totalWork
+=
r
.
getProcessingTime
();
}
}
// 计算每台机器的 span
for
(
MachineUtilInfo
info
:
map
.
values
())
{
info
.
span
=
info
.
maxEnd
-
info
.
minStart
;
}
return
map
;
}
private
void
decode
(
GeneticDecoder
decoder
,
Chromosome
chromosome
,
List
<
Machine
>
machines
)
{
chromosome
.
setResult
(
new
CopyOnWriteArrayList
<>());
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
));
List
<
GAScheduleResult
>
lockedOrders
=
GlobalCacheUtil
.
get
(
"locked_orders_"
+
chromosome
.
getScenarioID
());
if
(
lockedOrders
!=
null
&&
!
lockedOrders
.
isEmpty
())
{
chromosome
.
setResultOld
(
ProductionDeepCopyUtil
.
deepCopyList
(
lockedOrders
,
GAScheduleResult
.
class
));
}
else
{
chromosome
.
setResultOld
(
new
CopyOnWriteArrayList
<>());
}
decoder
.
decodeChromosomeWithCache
(
chromosome
,
false
);
}
private
Chromosome
copyChromosome
(
Chromosome
c
)
{
Chromosome
copy
=
new
Chromosome
();
if
(
c
.
getMachineSelection
()
!=
null
)
{
copy
.
setMachineSelection
(
new
CopyOnWriteArrayList
<>(
c
.
getMachineSelection
()));
}
if
(
c
.
getOperationSequencing
()
!=
null
)
{
copy
.
setOperationSequencing
(
new
CopyOnWriteArrayList
<>(
c
.
getOperationSequencing
()));
}
if
(
c
.
getObjectives
()
!=
null
)
{
copy
.
setObjectives
(
Arrays
.
copyOf
(
c
.
getObjectives
(),
c
.
getObjectives
().
length
));
}
if
(
c
.
getGlobalOpList
()
!=
null
)
{
copy
.
setGlobalOpList
(
new
CopyOnWriteArrayList
<>(
c
.
getGlobalOpList
()));
}
copy
.
setMachines
(
c
.
getMachines
());
copy
.
setOrders
(
c
.
getOrders
());
copy
.
setOperatRel
(
c
.
getOperatRel
());
copy
.
setMaterials
(
c
.
getMaterials
());
copy
.
setAllOperations
(
c
.
getAllOperations
());
copy
.
setFitness
(
c
.
getFitness
());
copy
.
setFitnessLevel
(
c
.
getFitnessLevel
());
copy
.
setResult
(
c
.
getResult
());
copy
.
setScenarioID
(
c
.
getScenarioID
());
copy
.
setBaseTime
(
c
.
getBaseTime
());
return
copy
;
}
private
boolean
isBetter
(
Chromosome
c1
,
Chromosome
c2
)
{
return
fitnessCalculator
.
isBetter
(
c1
,
c2
);
}
private
Map
<
Integer
,
Object
>
buildEntryKey
()
{
Map
<
String
,
Entry
>
entryMap
=
new
HashMap
<>();
Map
<
Integer
,
Entry
>
entryByIdMap
=
new
HashMap
<>();
for
(
Entry
entry
:
allOperations
)
{
entryByIdMap
.
put
(
entry
.
getId
(),
entry
);
}
Map
<
Integer
,
Object
>
result
=
new
HashMap
<>();
result
.
put
(
1
,
entryByIdMap
);
return
result
;
}
// ====================================================================
// 内部类
// ====================================================================
/**
* 机器利用率信息(用于负载均衡计算)。
*/
private
static
class
MachineUtilInfo
{
long
machineId
;
double
minStart
;
double
maxEnd
;
double
span
;
int
opCount
;
double
totalWork
;
}
/**
* Destroy 算子:破坏解的一部分。
*/
private
static
class
DestroyOperator
{
final
String
name
;
final
DestroyFunction
function
;
DestroyOperator
(
String
name
,
DestroyFunction
function
)
{
this
.
name
=
name
;
this
.
function
=
function
;
}
Chromosome
apply
(
Chromosome
c
,
double
ratioMin
,
double
ratioMax
)
{
try
{
return
function
.
apply
(
c
,
ratioMin
,
ratioMax
);
}
catch
(
Exception
e
)
{
FileHelper
.
writeLogFile
(
"ALNS - Destroy算子 "
+
name
+
" 异常: "
+
e
.
getMessage
());
return
null
;
}
}
}
@FunctionalInterface
private
interface
DestroyFunction
{
Chromosome
apply
(
Chromosome
c
,
double
ratioMin
,
double
ratioMax
);
}
/**
* Repair 算子:修复被破坏的解。
*/
private
static
class
RepairOperator
{
final
String
name
;
final
RepairFunction
function
;
RepairOperator
(
String
name
,
RepairFunction
function
)
{
this
.
name
=
name
;
this
.
function
=
function
;
}
Chromosome
apply
(
Chromosome
c
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
try
{
return
function
.
apply
(
c
,
vns
,
decoder
,
machines
);
}
catch
(
Exception
e
)
{
FileHelper
.
writeLogFile
(
"ALNS - Repair算子 "
+
name
+
" 异常: "
+
e
.
getMessage
());
return
null
;
}
}
}
@FunctionalInterface
private
interface
RepairFunction
{
Chromosome
apply
(
Chromosome
c
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
);
}
}
\ No newline at end of file
src/main/java/com/aps/service/Algorithm/CpSatFjspModel.java
View file @
41cab0d7
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.common.util.FileHelper
;
import
com.aps.entity.Algorithm.Chromosome
;
import
com.aps.entity.Algorithm.GlobalOperationInfo
;
import
com.aps.entity.basic.Entry
;
import
com.aps.entity.basic.Machine
;
import
com.aps.entity.basic.MachineOption
;
import
com.aps.entity.basic.Order
;
import
com.google.ortools.Loader
;
import
com.google.ortools.sat.CpModel
;
import
com.google.ortools.sat.CpSolver
;
...
...
@@ -16,6 +18,7 @@ import com.google.ortools.sat.LinearExpr;
import
com.google.ortools.sat.Literal
;
import
java.time.LocalDateTime
;
import
java.time.temporal.ChronoUnit
;
import
java.util.*
;
import
java.util.concurrent.CopyOnWriteArrayList
;
...
...
@@ -48,6 +51,9 @@ public class CpSatFjspModel {
private
final
List
<
Machine
>
machines
;
private
final
List
<
GlobalOperationInfo
>
globalOpList
;
private
final
List
<
Entry
>
allOperations
;
private
final
LocalDateTime
baseTime
;
private
final
List
<
Order
>
orders
;
private
final
int
operationCount
;
private
CpModel
model
;
...
...
@@ -61,11 +67,14 @@ public class CpSatFjspModel {
public
CpSatFjspModel
(
List
<
GlobalOperationInfo
>
globalOpList
,
List
<
Entry
>
allOperations
,
List
<
Machine
>
machines
,
List
<
Order
>
orders
,
LocalDateTime
baseTime
)
{
this
.
globalOpList
=
globalOpList
;
this
.
allOperations
=
allOperations
;
this
.
machines
=
machines
;
this
.
operationCount
=
globalOpList
.
size
();
this
.
orders
=
orders
;
this
.
baseTime
=
baseTime
;
}
private
int
estimateHorizon
()
{
...
...
@@ -221,28 +230,90 @@ public class CpSatFjspModel {
globalOpList
.
get
(
i
).
getOp
().
getPriority
());
}
int
priorityTerms
=
0
;
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
if
(
globalOpList
.
get
(
i
).
getOp
().
getPriority
()
>
0
)
priorityTerms
++;
}
List
<
LinearArgument
>
objTerms
=
new
ArrayList
<>();
List
<
Long
>
objWeights
=
new
ArrayList
<>();
LinearArgument
[]
objVars
=
new
LinearArgument
[
1
+
priorityTerms
];
long
[]
objCoeffs
=
new
long
[
1
+
priorityTerms
];
objVars
[
0
]
=
makespanVar
;
objCoeffs
[
0
]
=
100
;
objTerms
.
add
(
makespanVar
);
objWeights
.
add
(
100L
);
int
t
=
1
;
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
double
priority
=
globalOpList
.
get
(
i
).
getOp
().
getPriority
();
if
(
priority
>
0
)
{
objVars
[
t
]
=
endVars
.
get
(
i
);
objCoeffs
[
t
]
=
(
long
)(
maxPriority
-
priority
+
1
);
t
++;
objTerms
.
add
(
endVars
.
get
(
i
));
objWeights
.
add
((
long
)(
maxPriority
-
priority
+
1
));
}
}
model
.
minimize
(
LinearExpr
.
weightedSum
(
objVars
,
objCoeffs
));
}
// ==================== 新增目标1:Tardiness(延迟时间) ====================
// 按订单分组,找到每个订单最后一道工序的end_time
Map
<
Integer
,
List
<
Integer
>>
groupOps
=
new
LinkedHashMap
<>();
Map
<
Integer
,
Long
>
groupDueDates
=
new
HashMap
<>();
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
GlobalOperationInfo
info
=
globalOpList
.
get
(
i
);
int
groupId
=
info
.
getGroupId
();
groupOps
.
computeIfAbsent
(
groupId
,
k
->
new
ArrayList
<>()).
add
(
i
);
if
(!
groupDueDates
.
containsKey
(
groupId
))
{
Order
order
=
orders
.
stream
()
.
filter
(
o
->
info
.
getOp
().
getOrderId
().
equals
(
o
.
getOrderId
()))
.
findFirst
().
orElse
(
null
);
if
(
order
!=
null
&&
order
.
getDueDate
()
!=
null
)
{
long
dueMin
=
ChronoUnit
.
MINUTES
.
between
(
baseTime
,
order
.
getDueDate
());
groupDueDates
.
put
(
groupId
,
dueMin
*
60
);
}
}
}
long
totalTardinessWeight
=
80L
;
for
(
Map
.
Entry
<
Integer
,
List
<
Integer
>>
entry
:
groupOps
.
entrySet
())
{
int
groupId
=
entry
.
getKey
();
List
<
Integer
>
opIndices
=
entry
.
getValue
();
// 找到该订单最后完成的工序
IntVar
lastEnd
=
model
.
newIntVar
(
0
,
horizonSeconds
,
"lastEnd_g"
+
groupId
);
List
<
LinearArgument
>
endCandidates
=
new
ArrayList
<>();
for
(
int
idx
:
opIndices
)
{
endCandidates
.
add
(
endVars
.
get
(
idx
));
}
model
.
addMaxEquality
(
lastEnd
,
endCandidates
.
toArray
(
new
LinearArgument
[
0
]));
Long
dueDateSec
=
groupDueDates
.
get
(
groupId
);
if
(
dueDateSec
!=
null
)
{
IntVar
tardiness
=
model
.
newIntVar
(
0
,
horizonSeconds
,
"tardiness_g"
+
groupId
);
LinearArgument
diff
=
LinearExpr
.
sum
(
new
LinearArgument
[]{
lastEnd
,
LinearExpr
.
constant
(-
dueDateSec
)});
model
.
addMaxEquality
(
tardiness
,
new
LinearArgument
[]{
diff
,
LinearExpr
.
constant
(
0
)});
objTerms
.
add
(
tardiness
);
objWeights
.
add
(
totalTardinessWeight
);
}
}
// ==================== 新增目标2:Machine Load Balance(负载均衡) ====================
// 简化版本:取每台机器上所有工序的最大end_time,加入目标函数
// 这样 CP-SAT 会倾向于让各机器的完工时间更均匀
Map
<
Long
,
List
<
LinearArgument
>>
machineEndTimes
=
new
HashMap
<>();
for
(
int
i
=
0
;
i
<
operationCount
;
i
++)
{
GlobalOperationInfo
info
=
globalOpList
.
get
(
i
);
List
<
MachineOption
>
options
=
info
.
getOp
().
getMachineOptions
();
if
(
options
==
null
||
options
.
isEmpty
())
continue
;
for
(
MachineOption
mo
:
options
)
{
machineEndTimes
.
computeIfAbsent
(
mo
.
getMachineId
(),
k
->
new
ArrayList
<>()).
add
(
endVars
.
get
(
i
));
}
}
long
loadBalanceWeight
=
30L
;
for
(
Map
.
Entry
<
Long
,
List
<
LinearArgument
>>
entry
:
machineEndTimes
.
entrySet
())
{
IntVar
machineMakespan
=
model
.
newIntVar
(
0
,
horizonSeconds
,
"mk_m"
+
entry
.
getKey
());
model
.
addMaxEquality
(
machineMakespan
,
entry
.
getValue
().
toArray
(
new
LinearArgument
[
0
]));
objTerms
.
add
(
machineMakespan
);
objWeights
.
add
(
loadBalanceWeight
);
}
model
.
minimize
(
LinearExpr
.
weightedSum
(
objTerms
.
toArray
(
new
LinearArgument
[
0
]),
objWeights
.
stream
().
mapToLong
(
Long:
:
longValue
).
toArray
()));
}
private
boolean
enableLogging
=
true
;
/**
* 单次求解,返回一个 Chromosome
*/
...
...
@@ -258,8 +329,13 @@ public class CpSatFjspModel {
CpSolverStatus
status
=
solver
.
solve
(
model
);
if
(
status
==
CpSolverStatus
.
OPTIMAL
||
status
==
CpSolverStatus
.
FEASIBLE
)
{
long
makespanSec
=
(
long
)
solver
.
value
(
makespanVar
);
FileHelper
.
log
(
"[CpSatFjsp] 单次求解 状态="
+
status
+
" CP-SAT目标值="
+
String
.
format
(
"%,d"
,
(
long
)
solver
.
objectiveValue
())
+
" makespan="
+
String
.
format
(
"%,d秒 (%.1f小时)"
,
makespanSec
,
makespanSec
/
3600.0
),
enableLogging
);
return
extractChromosome
(
solver
);
}
FileHelper
.
log
(
"[CpSatFjsp] 单次求解 状态="
+
status
+
"(无解)"
,
enableLogging
);
return
null
;
}
...
...
@@ -328,15 +404,22 @@ public class CpSatFjspModel {
CpSolverStatus
status
=
solver
.
solve
(
model
);
if
(
status
==
CpSolverStatus
.
OPTIMAL
||
status
==
CpSolverStatus
.
FEASIBLE
)
{
long
msSec
=
(
long
)
solver
.
value
(
makespanVar
);
FileHelper
.
log
(
"[CpSatFjsp] 第"
+
(
round
+
1
)
+
"轮 状态="
+
status
+
" CP-SAT目标="
+
String
.
format
(
"%,d"
,
(
long
)
solver
.
objectiveValue
())
+
" makespan="
+
String
.
format
(
"%,d秒(%.1f小时)"
,
msSec
,
msSec
/
3600.0
),
enableLogging
);
Chromosome
chromo
=
extractChromosome
(
solver
);
if
(
chromo
!=
null
&&
!
containsDuplicate
(
results
,
chromo
))
{
chromo
.
setGsOrls
(
4
);
chromo
.
setGenerateType
(
"CP-SAT"
);
results
.
add
(
chromo
);
}
}
else
{
FileHelper
.
log
(
"[CpSatFjsp] 第"
+
(
round
+
1
)
+
"轮 状态="
+
status
+
"(无解)"
,
enableLogging
);
}
}
FileHelper
.
log
(
"[CpSatFjsp] 多样性解生成完成,共"
+
results
.
size
()
+
"个解"
,
enableLogging
);
return
results
;
}
...
...
src/main/java/com/aps/service/Algorithm/CpSatInitializer.java
View file @
41cab0d7
...
...
@@ -93,7 +93,7 @@ public class CpSatInitializer {
*/
private
List
<
Chromosome
>
smallScaleSolve
(
List
<
GlobalOperationInfo
>
globalOpList
,
int
targetCount
,
int
timeBudgetSec
)
{
CpSatFjspModel
model
=
new
CpSatFjspModel
(
globalOpList
,
allOperations
,
machines
,
baseTime
);
CpSatFjspModel
model
=
new
CpSatFjspModel
(
globalOpList
,
allOperations
,
machines
,
orders
,
baseTime
);
return
model
.
generateDiverseSolutions
(
Math
.
min
(
targetCount
,
8
),
timeBudgetSec
,
true
);
}
...
...
@@ -103,7 +103,7 @@ public class CpSatInitializer {
*/
private
List
<
Chromosome
>
mediumScaleSolve
(
List
<
GlobalOperationInfo
>
globalOpList
,
int
targetCount
,
int
timeBudgetSec
)
{
CpSatFjspModel
model
=
new
CpSatFjspModel
(
globalOpList
,
allOperations
,
machines
,
baseTime
);
CpSatFjspModel
model
=
new
CpSatFjspModel
(
globalOpList
,
allOperations
,
machines
,
orders
,
baseTime
);
int
effectiveTarget
=
Math
.
min
(
targetCount
,
5
);
int
perSolveTime
=
Math
.
max
(
timeBudgetSec
,
15
);
return
model
.
generateDiverseSolutions
(
effectiveTarget
,
perSolveTime
,
true
);
...
...
@@ -158,7 +158,7 @@ public class CpSatInitializer {
}
CpSatFjspModel
model
=
new
CpSatFjspModel
(
bottleneckOps
,
allOperations
,
bottleneckMachines
,
baseTime
);
bottleneckOps
,
allOperations
,
bottleneckMachines
,
orders
,
baseTime
);
List
<
Chromosome
>
cpSatResults
=
model
.
generateDiverseSolutions
(
effectiveTarget
,
timeBudgetSec
,
true
);
...
...
src/main/java/com/aps/service/Algorithm/CpSatLnsNeighborhood.java
0 → 100644
View file @
41cab0d7
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.Chromosome
;
import
com.aps.entity.Algorithm.GAScheduleResult
;
import
com.aps.entity.Algorithm.GlobalOperationInfo
;
import
com.aps.entity.Algorithm.IDAndChildID.GroupResult
;
import
com.aps.entity.Algorithm.ObjectiveWeights
;
import
com.aps.entity.basic.*
;
import
com.google.ortools.Loader
;
import
com.google.ortools.sat.CpModel
;
import
com.google.ortools.sat.CpSolver
;
import
com.google.ortools.sat.CpSolverStatus
;
import
com.google.ortools.sat.IntVar
;
import
com.google.ortools.sat.IntervalVar
;
import
com.google.ortools.sat.LinearArgument
;
import
com.google.ortools.sat.LinearExpr
;
import
com.google.ortools.sat.Literal
;
import
java.util.*
;
import
java.util.concurrent.CopyOnWriteArrayList
;
import
java.util.stream.Collectors
;
import
java.util.stream.IntStream
;
/**
* LNS (Large Neighborhood Search) + OR-Tools CP-SAT
*
* 从 NSGA-II 帕累托前沿取一个染色体:
* 1. 随机"释放" 10%~30% 的工序(让 CP-SAT 重排)
* 2. 其余工序"冻结"(保持机器和时间不变)
* 3. CP-SAT 在 5~15 秒内给出一个更好的调度
* 4. decode 后若目标改进则接受,否则回退
*
* 循环多轮。
*/
public
class
CpSatLnsNeighborhood
{
private
static
boolean
NATIVE_LIBRARY_LOADED
=
false
;
static
{
try
{
Loader
.
loadNativeLibraries
();
Class
.
forName
(
"com.google.ortools.sat.CpModel"
);
NATIVE_LIBRARY_LOADED
=
true
;
FileHelper
.
writeLogFile
(
"[CpSatLns] OR-Tools 已加载"
);
}
catch
(
Throwable
t
)
{
FileHelper
.
writeLogFile
(
"[CpSatLns] OR-Tools 本机库不可用,LNS 被禁用:"
+
t
.
getMessage
());
}
}
private
final
List
<
Entry
>
allOperations
;
private
final
List
<
Machine
>
machines
;
private
final
FitnessCalculator
fitnessCalculator
;
private
GeneticOperations
geneticOperations
;
private
List
<
Machine
>
cachedMachines
;
private
List
<
Order
>
cachedOrders
;
private
List
<
GroupResult
>
cachedEntryRel
;
private
TreeMap
<
String
,
Material
>
cachedMaterials
;
private
List
<
Entry
>
cachedAllOperations
;
private
final
Random
random
=
new
Random
(
20260618L
);
/**
* @param allOperations 全工序列表
* @param machines 全机器列表
* @param fitnessCalculator 适应度计算器(与 HybridAlgorithm 一致)
*/
public
CpSatLnsNeighborhood
(
List
<
Entry
>
allOperations
,
List
<
Machine
>
machines
,
List
<
Order
>
orders
,
TreeMap
<
String
,
Material
>
materials
,
List
<
GroupResult
>
entryRel
,
FitnessCalculator
fitnessCalculator
)
{
this
.
allOperations
=
allOperations
;
this
.
machines
=
machines
;
this
.
fitnessCalculator
=
fitnessCalculator
;
geneticOperations
=
new
GeneticOperations
();
// 预缓存解码需要的深拷贝列表,避免重复拷贝
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
);
}
/**
* 对帕累托前沿做 LNS 重优化。
* 返回的 list 包含:改进后的前沿 + 原始前沿(让下游的帕累托归并去重)
*/
public
List
<
Chromosome
>
runLnsOnParetoFront
(
List
<
Chromosome
>
paretoFront
,
GeneticDecoder
sharedDecoder
)
{
if
(!
NATIVE_LIBRARY_LOADED
||
paretoFront
==
null
||
paretoFront
.
isEmpty
())
return
paretoFront
;
int
totalOps
=
allOperations
.
size
();
int
lnsOuterRounds
=
Math
.
min
(
paretoFront
.
size
(),
8
);
int
lnsInnerRounds
=
Math
.
max
(
3
,
Math
.
min
(
15
,
8000
/
Math
.
max
(
100
,
totalOps
)));
int
releasedOps
=
Math
.
max
(
50
,
(
int
)(
totalOps
*
0.15
));
int
cpsatTimeSeconds
=
Math
.
max
(
5
,
Math
.
min
(
15
,
12000
/
Math
.
max
(
100
,
totalOps
)));
if
(
totalOps
<
50
)
{
return
null
;
}
FileHelper
.
writeLogFile
(
"[CpSatLns] 开始 LNS 重优化:工序="
+
totalOps
+
",每轮释放="
+
releasedOps
+
",CP-SAT="
+
cpsatTimeSeconds
+
"s,"
+
"前沿解数="
+
lnsOuterRounds
+
",每解迭代="
+
lnsInnerRounds
);
List
<
Chromosome
>
newFront
=
new
ArrayList
<>();
int
totalAttempts
=
0
,
totalImproves
=
0
;
for
(
int
i
=
0
;
i
<
lnsOuterRounds
&&
i
<
paretoFront
.
size
();
i
++)
{
Chromosome
current
=
lightCopy
(
paretoFront
.
get
(
i
));
Chromosome
beat
=
ProductionDeepCopyUtil
.
deepCopy
(
paretoFront
.
get
(
i
),
Chromosome
.
class
);
// sharedDecoder.serialDecode(current);
double
currentFitness
=
current
.
getFitness
();
double
[]
curObj
=
current
.
getObjectives
();
FileHelper
.
log
(
"[CpSatLns] 帕累托前沿第"
+
(
i
+
1
)
+
"个解 → 适应度="
+
String
.
format
(
"%.4f"
,
currentFitness
)
+
(
curObj
!=
null
?
" makespan="
+
String
.
format
(
"%.1f"
,
curObj
[
0
])
:
""
),
true
);
for
(
int
round
=
0
;
round
<
lnsInnerRounds
;
round
++)
{
totalAttempts
++;
Chromosome
neighbor
=
optimizeNeighborhood
(
current
,
releasedOps
,
cpsatTimeSeconds
);
if
(
neighbor
==
null
)
break
;
decode
(
sharedDecoder
,
neighbor
,
machines
);
if
(
fitnessCalculator
.
isBetter
(
neighbor
,
current
))
{
double
[]
nObj
=
neighbor
.
getObjectives
();
FileHelper
.
log
(
"[CpSatLns] 第"
+
(
round
+
1
)
+
"轮 ✅接受"
+
" 新适应度="
+
String
.
format
(
"%.4f"
,
neighbor
.
getFitness
())
+
" 旧适应度="
+
String
.
format
(
"%.4f"
,
currentFitness
)
+
(
nObj
!=
null
?
" 新makespan="
+
String
.
format
(
"%.1f"
,
nObj
[
0
])
:
""
),
true
);
current
=
neighbor
;
beat
=
ProductionDeepCopyUtil
.
deepCopy
(
neighbor
,
Chromosome
.
class
);
currentFitness
=
neighbor
.
getFitness
();
totalImproves
++;
}
}
newFront
.
add
(
beat
);
}
newFront
.
addAll
(
paretoFront
);
FileHelper
.
log
(
"[CpSatLns] 结束:尝试="
+
totalAttempts
+
",改进="
+
totalImproves
+
",合并后解数="
+
newFront
.
size
(),
true
);
return
newFront
;
}
// ----------------------------------------------------------------
// 单轮邻域重优化
// ----------------------------------------------------------------
public
Chromosome
optimizeNeighborhood
(
Chromosome
base
,
int
releaseCount
,
int
timeLimitSec
)
{
List
<
GlobalOperationInfo
>
globalOpList
=
base
.
getGlobalOpList
();
if
(
globalOpList
==
null
||
globalOpList
.
isEmpty
())
return
null
;
CopyOnWriteArrayList
<
Integer
>
ms
=
base
.
getMachineSelection
();
if
(
ms
==
null
)
return
null
;
int
totalCount
=
globalOpList
.
size
();
releaseCount
=
Math
.
min
(
releaseCount
,
totalCount
);
long
horizon
=
30L
*
24
*
60
*
60
;
// 30 天(秒)
// ================== 1. 从 Result 中读取每道工序的机器+时间 ==================
Map
<
Integer
,
GAScheduleResult
>
scheduleMap
=
new
HashMap
<>();
if
(
base
.
getResult
()
!=
null
)
{
for
(
GAScheduleResult
r
:
base
.
getResult
())
{
int
key
=
(
r
.
getGroupId
()
*
1000000
)
+
r
.
getSeq
();
scheduleMap
.
put
(
key
,
r
);
}
}
long
[]
opStartSec
=
new
long
[
totalCount
];
long
[]
opDurationSec
=
new
long
[
totalCount
];
long
[]
opMachineId
=
new
long
[
totalCount
];
for
(
int
i
=
0
;
i
<
totalCount
;
i
++)
{
GlobalOperationInfo
info
=
globalOpList
.
get
(
i
);
Entry
op
=
info
.
getOp
();
int
key
=
(
info
.
getGroupId
()
*
1000000
)
+
info
.
getSequence
();
GAScheduleResult
res
=
scheduleMap
.
get
(
key
);
if
(
res
!=
null
)
{
opStartSec
[
i
]
=
res
.
getStartTime
();
opMachineId
[
i
]
=
res
.
getMachineId
();
opDurationSec
[
i
]
=
Math
.
max
(
60L
,
(
long
)(
res
.
getProcessingTime
()
*
60
));
}
else
{
opDurationSec
[
i
]
=
Math
.
max
(
60L
,
(
long
)(
op
.
getMinProcessingTime
()
*
60
));
opStartSec
[
i
]
=
0
;
List
<
MachineOption
>
options
=
op
.
getMachineOptions
();
opMachineId
[
i
]
=
(
options
!=
null
&&
!
options
.
isEmpty
())
?
options
.
get
(
0
).
getMachineId
()
:
-
1
;
}
}
// ================== 2. 随机选"释放"的工序 ==================
BitSet
released
=
new
BitSet
(
totalCount
);
if
(
releaseCount
>=
totalCount
)
{
released
.
set
(
0
,
totalCount
);
}
else
{
List
<
Integer
>
indices
=
IntStream
.
range
(
0
,
totalCount
)
.
boxed
().
collect
(
Collectors
.
toList
());
Collections
.
shuffle
(
indices
,
random
);
for
(
int
k
=
0
;
k
<
releaseCount
;
k
++)
released
.
set
(
indices
.
get
(
k
));
}
// ================== 3. 构建 CP-SAT 模型 ==================
try
{
CpModel
model
=
new
CpModel
();
Map
<
Long
,
Integer
>
machineIdx
=
new
HashMap
<>();
for
(
int
m
=
0
;
m
<
machines
.
size
();
m
++)
machineIdx
.
put
(
machines
.
get
(
m
).
getId
(),
m
);
List
<
List
<
IntervalVar
>>
machineIntervals
=
new
ArrayList
<>();
for
(
int
m
=
0
;
m
<
machines
.
size
();
m
++)
machineIntervals
.
add
(
new
ArrayList
<>());
IntVar
[]
startVars
=
new
IntVar
[
totalCount
];
// presence[i][j] = 第 i 个释放工序选第 j 台机器的布尔
List
<
Literal
[]>
presenceMatrix
=
new
ArrayList
<>();
// 处理释放工序
for
(
int
i
=
0
;
i
<
totalCount
;
i
++)
{
List
<
MachineOption
>
options
=
globalOpList
.
get
(
i
).
getOp
().
getMachineOptions
();
presenceMatrix
.
add
(
null
);
if
(!
released
.
get
(
i
))
continue
;
if
(
options
==
null
||
options
.
isEmpty
())
continue
;
long
baseStart
=
Math
.
max
(
0
,
opStartSec
[
i
]);
long
lower
=
Math
.
max
(
0
,
baseStart
-
3600
);
long
upper
=
Math
.
min
(
horizon
,
baseStart
+
10800
);
if
(
upper
<=
lower
)
upper
=
lower
+
3600
;
startVars
[
i
]
=
model
.
newIntVar
(
lower
,
upper
,
"start_"
+
i
);
List
<
Literal
>
presList
=
new
ArrayList
<>();
for
(
int
j
=
0
;
j
<
options
.
size
();
j
++)
{
MachineOption
mo
=
options
.
get
(
j
);
long
procSec
=
Math
.
max
(
60L
,
(
long
)(
mo
.
getProcessingTime
()
*
60
));
Literal
pres
=
model
.
newBoolVar
(
"pres_"
+
i
+
"_"
+
j
);
presList
.
add
(
pres
);
IntVar
endVar
=
model
.
newIntVar
(
lower
+
procSec
,
upper
+
procSec
,
"end_"
+
i
+
"_"
+
j
);
model
.
addEquality
(
LinearExpr
.
sum
(
new
LinearArgument
[]{
startVars
[
i
],
LinearExpr
.
constant
(
procSec
)}),
endVar
);
IntervalVar
iv
=
model
.
newOptionalIntervalVar
(
startVars
[
i
],
model
.
newConstant
(
procSec
),
endVar
,
pres
,
"iv_"
+
i
+
"_m"
+
mo
.
getMachineId
());
int
mIdx
=
machineIdx
.
getOrDefault
(
mo
.
getMachineId
(),
-
1
);
if
(
mIdx
>=
0
)
machineIntervals
.
get
(
mIdx
).
add
(
iv
);
}
Literal
[]
arr
=
presList
.
toArray
(
new
Literal
[
0
]);
if
(
arr
.
length
>
0
)
{
model
.
addExactlyOne
(
arr
);
presenceMatrix
.
set
(
i
,
arr
);
}
}
// 冻结工序:固定 interval
for
(
int
i
=
0
;
i
<
totalCount
;
i
++)
{
if
(
released
.
get
(
i
))
continue
;
long
start
=
Math
.
max
(
0
,
opStartSec
[
i
]);
long
dur
=
Math
.
max
(
60L
,
opDurationSec
[
i
]);
long
mId
=
opMachineId
[
i
];
int
mIdx
=
machineIdx
.
getOrDefault
(
mId
,
-
1
);
if
(
mIdx
>=
0
)
{
IntervalVar
iv
=
model
.
newFixedInterval
(
start
,
dur
,
"frozen_"
+
i
);
machineIntervals
.
get
(
mIdx
).
add
(
iv
);
}
}
// ================== 4. 同订单工序顺序 ==================
Map
<
Integer
,
List
<
Integer
>>
orderOpsMap
=
new
LinkedHashMap
<>();
for
(
int
i
=
0
;
i
<
totalCount
;
i
++)
{
orderOpsMap
.
computeIfAbsent
(
globalOpList
.
get
(
i
).
getGroupId
(),
k
->
new
ArrayList
<>()).
add
(
i
);
}
for
(
List
<
Integer
>
ops
:
orderOpsMap
.
values
())
{
ops
.
sort
(
Comparator
.
comparingInt
(
idx
->
globalOpList
.
get
(
idx
).
getSequence
()));
for
(
int
k
=
0
;
k
<
ops
.
size
()
-
1
;
k
++)
{
int
prev
=
ops
.
get
(
k
);
int
next
=
ops
.
get
(
k
+
1
);
long
prevDuration
=
Math
.
max
(
60L
,
opDurationSec
[
prev
]);
LinearArgument
prevEnd
;
LinearArgument
nextStart
;
if
(
released
.
get
(
prev
))
{
prevEnd
=
LinearExpr
.
sum
(
new
LinearArgument
[]{
startVars
[
prev
],
LinearExpr
.
constant
(
prevDuration
)});
}
else
{
prevEnd
=
LinearExpr
.
constant
(
opStartSec
[
prev
]
+
prevDuration
);
}
if
(
released
.
get
(
next
))
{
nextStart
=
startVars
[
next
];
}
else
{
nextStart
=
LinearExpr
.
constant
(
opStartSec
[
next
]);
}
model
.
addLessOrEqual
(
prevEnd
,
nextStart
);
}
}
// ================== 5. 每台机器 no_overlap ==================
for
(
List
<
IntervalVar
>
list
:
machineIntervals
)
{
if
(
list
.
size
()
>
1
)
{
model
.
addNoOverlap
(
list
.
toArray
(
new
IntervalVar
[
0
]));
}
}
// ================== 6. 目标:makespan + 优先级加权 + 延迟 + 负载均衡 ==================
IntVar
makespan
=
model
.
newIntVar
(
0
,
horizon
,
"makespan"
);
List
<
LinearArgument
>
objTerms
=
new
ArrayList
<>();
List
<
Long
>
objWeights
=
new
ArrayList
<>();
objTerms
.
add
(
makespan
);
objWeights
.
add
(
100L
);
double
maxPriority
=
1.0
;
for
(
int
i
=
0
;
i
<
totalCount
;
i
++)
{
maxPriority
=
Math
.
max
(
maxPriority
,
globalOpList
.
get
(
i
).
getOp
().
getPriority
());
}
for
(
int
i
=
0
;
i
<
totalCount
;
i
++)
{
long
dur
=
Math
.
max
(
60L
,
opDurationSec
[
i
]);
LinearArgument
endVar
;
if
(
released
.
get
(
i
))
{
endVar
=
LinearExpr
.
sum
(
new
LinearArgument
[]{
startVars
[
i
],
LinearExpr
.
constant
(
dur
)});
}
else
{
endVar
=
LinearExpr
.
constant
(
opStartSec
[
i
]
+
dur
);
}
model
.
addLessOrEqual
(
endVar
,
makespan
);
if
(
released
.
get
(
i
))
{
double
priority
=
globalOpList
.
get
(
i
).
getOp
().
getPriority
();
long
weight
=
(
long
)(
maxPriority
-
priority
+
1
);
objTerms
.
add
(
endVar
);
objWeights
.
add
(
weight
);
}
}
// 延迟时间(只对释放的工序所属订单)
long
tardinessWeight
=
80L
;
Map
<
Integer
,
List
<
LinearArgument
>>
groupEnds
=
new
HashMap
<>();
for
(
int
i
=
0
;
i
<
totalCount
;
i
++)
{
if
(!
released
.
get
(
i
))
continue
;
GlobalOperationInfo
info
=
globalOpList
.
get
(
i
);
groupEnds
.
computeIfAbsent
(
info
.
getGroupId
(),
k
->
new
ArrayList
<>())
.
add
(
LinearExpr
.
sum
(
new
LinearArgument
[]{
startVars
[
i
],
LinearExpr
.
constant
(
Math
.
max
(
60L
,
opDurationSec
[
i
]))}));
}
for
(
Map
.
Entry
<
Integer
,
List
<
LinearArgument
>>
entry
:
groupEnds
.
entrySet
())
{
IntVar
lastEnd
=
model
.
newIntVar
(
0
,
horizon
,
"lastEnd_g"
+
entry
.
getKey
());
model
.
addMaxEquality
(
lastEnd
,
entry
.
getValue
().
toArray
(
new
LinearArgument
[
0
]));
// 简化:延迟作为惩罚项加入(实际可加 due_date 判断)
objTerms
.
add
(
lastEnd
);
objWeights
.
add
(
tardinessWeight
);
}
// 机器负载均衡
long
loadWeight
=
30L
;
Map
<
Long
,
List
<
LinearArgument
>>
machineEnds
=
new
HashMap
<>();
for
(
int
i
=
0
;
i
<
totalCount
;
i
++)
{
if
(!
released
.
get
(
i
))
continue
;
GlobalOperationInfo
info
=
globalOpList
.
get
(
i
);
List
<
com
.
aps
.
entity
.
basic
.
MachineOption
>
options
=
info
.
getOp
().
getMachineOptions
();
if
(
options
==
null
||
options
.
isEmpty
())
continue
;
LinearArgument
endVar
=
LinearExpr
.
sum
(
new
LinearArgument
[]{
startVars
[
i
],
LinearExpr
.
constant
(
Math
.
max
(
60L
,
opDurationSec
[
i
]))});
for
(
com
.
aps
.
entity
.
basic
.
MachineOption
mo
:
options
)
{
machineEnds
.
computeIfAbsent
(
mo
.
getMachineId
(),
k
->
new
ArrayList
<>()).
add
(
endVar
);
}
}
for
(
Map
.
Entry
<
Long
,
List
<
LinearArgument
>>
entry
:
machineEnds
.
entrySet
())
{
IntVar
machineMk
=
model
.
newIntVar
(
0
,
horizon
,
"mk_m"
+
entry
.
getKey
());
model
.
addMaxEquality
(
machineMk
,
entry
.
getValue
().
toArray
(
new
LinearArgument
[
0
]));
objTerms
.
add
(
machineMk
);
objWeights
.
add
(
loadWeight
);
}
model
.
minimize
(
LinearExpr
.
weightedSum
(
objTerms
.
toArray
(
new
LinearArgument
[
0
]),
objWeights
.
stream
().
mapToLong
(
Long:
:
longValue
).
toArray
()));
// ================== 7. 求解 ==================
CpSolver
solver
=
new
CpSolver
();
solver
.
getParameters
().
setMaxTimeInSeconds
(
timeLimitSec
);
solver
.
getParameters
().
setNumSearchWorkers
(
4
);
solver
.
getParameters
().
setLogSearchProgress
(
false
);
CpSolverStatus
status
=
solver
.
solve
(
model
);
if
(
status
!=
CpSolverStatus
.
OPTIMAL
&&
status
!=
CpSolverStatus
.
FEASIBLE
)
{
FileHelper
.
writeLogFile
(
"[CpSatLns] 邻域求解 状态="
+
status
+
"(无解),释放="
+
releaseCount
+
"道工序"
);
return
null
;
}
long
cpObjective
=
(
long
)
solver
.
objectiveValue
();
long
cpMakespan
=
(
long
)
solver
.
value
(
makespan
);
FileHelper
.
writeLogFile
(
"[CpSatLns] 邻域求解 状态="
+
status
+
" 释放工序="
+
releaseCount
+
" CP-SAT目标="
+
String
.
format
(
"%,d"
,
cpObjective
)
+
" makespan="
+
String
.
format
(
"%,d秒(%.1f小时)"
,
cpMakespan
,
cpMakespan
/
3600.0
));
// ================== 8. 写回新染色体的 machineSelection ==================
Chromosome
neighbor
=
base
.
deepCopy
();
CopyOnWriteArrayList
<
Integer
>
newMs
=
new
CopyOnWriteArrayList
<>(
neighbor
.
getMachineSelection
()
!=
null
?
neighbor
.
getMachineSelection
()
:
new
ArrayList
<>()
);
while
(
newMs
.
size
()
<
totalCount
)
newMs
.
add
(
1
);
for
(
int
i
=
0
;
i
<
totalCount
;
i
++)
{
if
(!
released
.
get
(
i
))
continue
;
Literal
[]
arr
=
presenceMatrix
.
get
(
i
);
if
(
arr
==
null
)
continue
;
for
(
int
j
=
0
;
j
<
arr
.
length
;
j
++)
{
if
(
solver
.
value
(
arr
[
j
])
==
1L
)
{
newMs
.
set
(
i
,
j
+
1
);
// 1-based
break
;
}
}
}
neighbor
.
setMachineSelection
(
newMs
);
neighbor
.
setGenerateType
(
"LNS-CPSAT"
);
neighbor
.
setGsOrls
(
5
);
return
neighbor
;
}
catch
(
Exception
e
)
{
FileHelper
.
writeLogFile
(
"[CpSatLns] 邻域重优化异常:"
+
e
.
getMessage
());
return
null
;
}
}
/**
* 轻量拷贝:只复制 generateNeighbor/DelOrder 需要的字段,避免全量 JSON 深拷贝导致 OOM。
* result/machines/operatRel 等重型数据共享引用(generateNeighbor 只读,不修改)。
*/
private
Chromosome
lightCopy
(
Chromosome
source
)
{
Chromosome
copy
=
new
Chromosome
();
copy
.
setOperationSequencing
(
new
CopyOnWriteArrayList
<>(
source
.
getOperationSequencing
()));
copy
.
setMachineSelection
(
new
CopyOnWriteArrayList
<>(
source
.
getMachineSelection
()));
copy
.
setGlobalOpList
(
new
CopyOnWriteArrayList
<>(
source
.
getGlobalOpList
()));
copy
.
setOrders
(
new
CopyOnWriteArrayList
<>(
source
.
getOrders
()));
copy
.
setAllOperations
(
new
CopyOnWriteArrayList
<>(
source
.
getAllOperations
()));
copy
.
setResult
(
source
.
getResult
());
copy
.
setMachines
(
source
.
getMachines
());
copy
.
setOperatRel
(
new
CopyOnWriteArrayList
<>(
source
.
getOperatRel
()));
copy
.
setScenarioID
(
source
.
getScenarioID
());
copy
.
setBaseTime
(
source
.
getBaseTime
());
copy
.
setGenerateType
(
source
.
getGenerateType
());
copy
.
setFitnessLevel
(
source
.
getFitnessLevel
());
copy
.
setFitness
(
source
.
getFitness
());
copy
.
setObjectives
(
source
.
getObjectives
());
geneticOperations
.
DelOrder
(
copy
);
return
copy
;
}
/**
* 解码染色体
*/
private
void
decode
(
GeneticDecoder
decoder
,
Chromosome
chromosome
,
List
<
Machine
>
machines
)
{
// MS 校验:解码前检查 machineSelection 与 machineOptions 是否匹配
List
<
GlobalOperationInfo
>
gops
=
chromosome
.
getGlobalOpList
();
List
<
Integer
>
msCheck
=
chromosome
.
getMachineSelection
();
if
(
gops
!=
null
&&
msCheck
!=
null
)
{
int
msErrors
=
0
;
StringBuilder
sb
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
Math
.
min
(
gops
.
size
(),
msCheck
.
size
());
i
++)
{
Entry
op
=
gops
.
get
(
i
).
getOp
();
int
msVal
=
msCheck
.
get
(
i
);
if
(
op
!=
null
&&
op
.
getMachineOptions
()
!=
null
&&
(
msVal
<
1
||
msVal
>
op
.
getMachineOptions
().
size
()))
{
msErrors
++;
if
(
msErrors
<=
3
)
{
sb
.
append
(
String
.
format
(
" [idx=%d 订单%d工序%d ms=%d range=1-%d]"
,
i
,
op
.
getGroupId
(),
op
.
getSequence
(),
msVal
,
op
.
getMachineOptions
().
size
()));
}
}
}
if
(
msErrors
>
0
)
{
FileHelper
.
log
(
String
.
format
(
"decode-MS校验失败: 共%d处越界 %s"
,
msErrors
,
sb
.
toString
()));
}
}
chromosome
.
setResult
(
new
CopyOnWriteArrayList
<>());
// 使用缓存的列表,避免重复深拷贝
chromosome
.
setMachines
(
ProductionDeepCopyUtil
.
deepCopyList
(
machines
,
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
<>(
allOperations
),
Entry
.
class
));
// 加载锁定工单到ResultOld
List
<
GAScheduleResult
>
lockedOrders
=
GlobalCacheUtil
.
get
(
"locked_orders_"
+
chromosome
.
getScenarioID
());
if
(
lockedOrders
!=
null
&&
!
lockedOrders
.
isEmpty
())
{
chromosome
.
setResultOld
(
ProductionDeepCopyUtil
.
deepCopyList
(
lockedOrders
,
GAScheduleResult
.
class
));
}
else
{
chromosome
.
setResultOld
(
new
CopyOnWriteArrayList
<>());
}
decoder
.
decodeChromosomeWithCache
(
chromosome
,
false
);
}
}
src/main/java/com/aps/service/Algorithm/GeneticDecoder.java
View file @
41cab0d7
...
...
@@ -695,11 +695,6 @@ public class GeneticDecoder {
int
scheduledCount
=
orderProcessCounter
.
get
(
groupId
);
if
(
groupId
==
7
)
{
int
k
=
0
;
}
List
<
Entry
>
orderOps
=
new
ArrayList
<>();
boolean
orderIsJit
=
orderDueDate
.
get
(
groupId
)>
0
;
...
...
@@ -743,7 +738,7 @@ public class GeneticDecoder {
}
else
{
orderAnchor
=
bom
.
computeSemiFinishedAnchor
(
this
,
groupId
,
entrysBygroupId
,
opMachineKeyMap
,
chromosome
,
scheduleIndexById
,
machineTasksCache
,
machineIdMap
,
entryIndexById
,
_globalParam
.
isIsCheckMp
());
scheduleIndexById
,
machineTasksCache
,
machineIdMap
,
entryIndexById
,
_globalParam
.
isIsCheckMp
()
,
null
);
if
(
orderAnchor
<
0
)
{
orderIsJit
=
false
;
orderSchedulingInfo
.
put
(
groupId
,
...
...
@@ -3524,6 +3519,7 @@ if(geneDetails!=null&&geneDetails.size()>0)
private
void
calculateScheduleResult
(
Chromosome
chromosome
)
{
double
[]
Objectives
=
new
double
[
_globalParam
.
getObjectiveWeights
().
size
()];
double
[]
weights
=
new
double
[
_globalParam
.
getObjectiveWeights
().
size
()];
int
i
=
0
;
for
(
ObjectiveConfig
config
:
_globalParam
.
getObjectiveConfigs
())
{
...
...
@@ -3535,7 +3531,9 @@ if(geneDetails!=null&&geneDetails.size()>0)
.
max
()
.
orElse
(
0
);
Objectives
[
i
]
=
makespan
;
weights
[
i
]
=
config
.
getWeight
();
chromosome
.
setMakespan
(
makespan
);
}
if
(
GlobalParam
.
OBJECTIVE_TARDINESS
.
equals
(
config
.
getName
()))
{
// 2. 交付期满足情况(最小化延迟)
...
...
@@ -3604,6 +3602,16 @@ if(geneDetails!=null&&geneDetails.size()>0)
}
chromosome
.
setObjectives
(
Objectives
);
// 计算各 KPI 的理论下界(用于计算 Gap = (current - lowerBound) / lowerBound)
try
{
KpiLowerBoundCalculator
.
computeAndSetLowerBounds
(
chromosome
,
_globalParam
);
}
catch
(
Exception
e
)
{
// 下界计算异常不应影响主流程
com
.
aps
.
common
.
util
.
FileHelper
.
writeLogFile
(
"KPI 下界计算异常: "
+
e
.
getClass
().
getSimpleName
()
+
" - "
+
e
.
getMessage
());
}
FitnessCalculator
fitnessCalculator
=
new
FitnessCalculator
();
chromosome
.
setFitnessLevel
(
fitnessCalculator
.
calculateFitness
(
chromosome
,
_globalParam
));
...
...
src/main/java/com/aps/service/Algorithm/HillClimbing.java
View file @
41cab0d7
...
...
@@ -76,7 +76,7 @@ public class HillClimbing {
Chromosome
current
=
ProductionDeepCopyUtil
.
deepCopy
(
chromosome
,
Chromosome
.
class
);
Chromosome
best
=
ProductionDeepCopyUtil
.
deepCopy
(
chromosome
,
Chromosome
.
class
);
decoder
.
DelOrder
(
current
);
// 构建位置索引映射:groupId_sequence -> position
Map
<
String
,
Integer
>
positionIndex
=
buildPositionIndex
(
current
);
...
...
@@ -119,7 +119,7 @@ public class HillClimbing {
positionIndex
=
buildPositionIndex
(
current
);
entryIndex
=
buildEntryIndex
(
current
,
entrys
);
MachinePositionIndex
=
buildEntryMachinePositionIndex
(
current
);
decoder
.
DelOrder
(
current
);
//
decoder.DelOrder(current);
break
;
}
}
...
...
src/main/java/com/aps/service/Algorithm/HybridAlgorithm.java
View file @
41cab0d7
...
...
@@ -49,6 +49,8 @@ public class HybridAlgorithm {
private
String
sceneId
;
private
VariableNeighborhoodSearch
_vns
;
private
AdaptiveLargeNeighborhoodSearch
_ALNS
;
// 初始化算法实例
private
HillClimbing
_hillClimbing
;
private
SimulatedAnnealing
_simulatedAnnealing
;
...
...
@@ -116,6 +118,7 @@ public class HybridAlgorithm {
// 初始化变邻域搜索
_vns
=
new
VariableNeighborhoodSearch
(
allOperations
,
orders
,
materials
,
_entryRel
,
_fitnessCalculator
);
_vns
.
initMachineSelectFrequency
();
_ALNS
=
new
AdaptiveLargeNeighborhoodSearch
(
allOperations
,
orders
,
materials
,
_entryRel
,
_fitnessCalculator
);
_hillClimbing
=
new
HillClimbing
(
allOperations
,
orders
,
materials
,
_entryRel
,
_fitnessCalculator
);
_simulatedAnnealing
=
new
SimulatedAnnealing
(
allOperations
,
orders
,
materials
,
_entryRel
,
_fitnessCalculator
);
_tabuSearch
=
new
TabuSearch
(
allOperations
,
orders
,
materials
,
_entryRel
,
_fitnessCalculator
);
...
...
@@ -148,6 +151,26 @@ public class HybridAlgorithm {
if
(
population
==
null
||
population
.
isEmpty
())
{
throw
new
RuntimeException
(
"初始种群为空,请检查 populationSize、种群初始化和解码结果"
);
}
if
(
_GlobalParam
.
isOptimizer
())
{
FileHelper
.
writeLogFile
(
"LNS-CPSAT 邻域重优化-----------开始-------种群="
+
population
.
size
());
try
{
CpSatLnsNeighborhood
lns
=
new
CpSatLnsNeighborhood
(
allOperations
,
machines
,
orders
,
materials
,
_entryRel
,
_fitnessCalculator
);
List
<
Chromosome
>
lnsResult
=
lns
.
runLnsOnParetoFront
(
population
,
sharedDecoder
);
if
(
lnsResult
!=
null
&&
!
lnsResult
.
isEmpty
())
{
// Chromosomedecode(sharedDecoder, param, allOperations, globalOpList, lnsResult);
population
=
chromosomeDistinctByObjectives
(
lnsResult
);
if
(
population
==
null
||
population
.
isEmpty
())
{
population
=
lnsResult
;
}
}
}
catch
(
Throwable
t
)
{
FileHelper
.
writeLogFile
(
"LNS-CPSAT 失败,跳过:"
+
t
.
getMessage
());
}
FileHelper
.
writeLogFile
(
"LNS-CPSAT 邻域重优化-----------结束-------种群="
+
(
population
==
null
?
0
:
population
.
size
()));
}
// if(1==1)
// return getBestChromosome(population.get(0), param.getBaseTime(), starttime);
// 步骤2:对初始种群进行爬山法局部优化
...
...
@@ -178,9 +201,9 @@ public class HybridAlgorithm {
return
getBestChromosome
(
saHcOptimized
,
param
.
getBaseTime
(),
starttime
);
}
if
(
opcount
>
=
8
00
)
{
if
(
opcount
>
800
&&
opcount
<
20
00
)
{
Chromosome
best
=
population
.
get
(
0
);
best
=
_ALNS
.
search
(
best
,
_tabuSearch
,
_vns
,
sharedDecoder
,
machines
);
best
=
_simulatedAnnealing
.
search
(
best
,
_tabuSearch
,
_vns
,
sharedDecoder
,
machines
);
best
=
_vns
.
search
(
best
,
_tabuSearch
,
sharedDecoder
,
machines
);
...
...
@@ -189,6 +212,29 @@ public class HybridAlgorithm {
return
getBestChromosome
(
best
,
param
.
getBaseTime
(),
starttime
);
}
else
{
Chromosome
best
=
population
.
get
(
0
);
int
topN
=
Math
.
min
(
3
,
population
.
size
());
for
(
int
iter
=
0
;
iter
<
topN
;
iter
++)
{
FileHelper
.
writeLogFile
(
"迭代进化------"
+
iter
+
"-----开始-------"
);
System
.
gc
();
Chromosome
chromosome
=
population
.
get
(
iter
);
chromosome
=
_ALNS
.
search
(
chromosome
,
_tabuSearch
,
_vns
,
sharedDecoder
,
machines
);
chromosome
=
_simulatedAnnealing
.
search
(
chromosome
,
_tabuSearch
,
_vns
,
sharedDecoder
,
machines
);
chromosome
=
_vns
.
search
(
chromosome
,
_tabuSearch
,
sharedDecoder
,
machines
);
if
(
_fitnessCalculator
.
isBetter
(
chromosome
,
best
))
{
FileHelper
.
writeLogFile
(
"迭代进化------发现更优解-----------"
);
writeKpi
(
chromosome
);
best
=
chromosome
;
}
FileHelper
.
writeLogFile
(
"迭代进化------"
+
iter
+
"-----结束-------"
);
// 周期性 GC 释放内存
System
.
gc
();
}
}
...
...
@@ -514,7 +560,47 @@ public class HybridAlgorithm {
// }
}
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
)
{
log
(
String
.
format
(
"变邻域搜索 - kpi-Makespan: %f"
,
chromosome
.
getMakespan
()));
}
if
(
chromosome
.
getDelayTime
()!=
0
)
{
log
(
String
.
format
(
"变邻域搜索 - kpi-DelayTime: %f"
,
chromosome
.
getDelayTime
()));
}
if
(
chromosome
.
getTotalChangeoverTime
()!=
0
)
{
log
(
String
.
format
(
"变邻域搜索 - kpi-ChangeoverTime: %f"
,
chromosome
.
getTotalChangeoverTime
()));
}
if
(
chromosome
.
getMachineLoadStd
()!=
0
)
{
log
(
String
.
format
(
"变邻域搜索 - kpi-MachineLoad: %f"
,
chromosome
.
getMachineLoadStd
()));
}
if
(
chromosome
.
getTotalFlowTime
()!=
0
)
{
log
(
String
.
format
(
"变邻域搜索 - kpi-FlowTime: %f"
,
chromosome
.
getTotalFlowTime
()));
}
// ==================== 打印各 KPI 的 Gap ====================
log
(
KpiLowerBoundCalculator
.
generateGapReport
(
chromosome
));
}
private
void
log
(
String
message
)
{
log
(
message
,
true
);
}
private
void
log
(
String
message
,
boolean
enableLogging
)
{
if
(
enableLogging
)
{
FileHelper
.
writeLogFile
(
message
);
}
}
}
src/main/java/com/aps/service/Algorithm/Initialization.java
View file @
41cab0d7
...
...
@@ -1137,9 +1137,15 @@ public class Initialization {
List
<
Chromosome
>
heuristicPopulation
=
generateHeuristicInitialPopulation
(
subParam
,
remaining
);
for
(
Chromosome
chromo
:
heuristicPopulation
)
{
chromo
.
setOrders
(
new
CopyOnWriteArrayList
<>(
orders
));
if
(
chromo
!=
null
)
{
chromo
.
setOrders
(
new
CopyOnWriteArrayList
<>(
orders
));
population
.
add
(
chromo
);
}
}
population
.
addAll
(
heuristicPopulation
);
}
long
cpSatCount
=
population
.
stream
()
...
...
src/main/java/com/aps/service/Algorithm/KpiLowerBoundCalculator.java
0 → 100644
View file @
41cab0d7
package
com
.
aps
.
service
.
Algorithm
;
import
com.aps.entity.Algorithm.Chromosome
;
import
com.aps.entity.Algorithm.GAScheduleResult
;
import
com.aps.entity.Algorithm.IDAndChildID.GroupResult
;
import
com.aps.entity.Algorithm.IDAndChildID.NodeInfo
;
import
com.aps.entity.basic.Entry
;
import
com.aps.entity.basic.GlobalParam
;
import
com.aps.entity.basic.ObjectiveConfig
;
import
java.util.*
;
import
java.util.stream.Collectors
;
/**
* 计算各 KPI 的理论下界(Lower Bound)。
*
* <p>Gap = (current - lowerBound) / lowerBound(lowerBound > 0 时)
* <p>当 lowerBound = 0 时(如 Tardiness),Gap = current(表示偏离 0 的绝对量)
*
* <p>各维度的理论下界:
* <ul>
* <li>Makespan(最大完工时间):DAG 关键路径长度(拓扑序 + 动态规划)</li>
* <li>FlowTime(总流程时间):所有工序加工时间之和(∑ processingTime)</li>
* <li>SetupTime(总换型时间):若换型时间只由产品类型决定则为 0;若必须换型则为换型时间之和</li>
* <li>MachineLoad(机器负载均衡标准差):完美均衡下为 0</li>
* <li>Tardiness(总延迟时间):0(可全部按时交付)</li>
* </ul>
*
* 作者:佟礼
*/
public
class
KpiLowerBoundCalculator
{
/**
* 对已解码的 chromosome 计算各维度的理论下界,并存入 chromosome.LowerBoundObjectives。
*
* @param chromosome 已完成 decode 的染色体
* @param globalParam 全局参数(用于确定 objectives 数组的顺序与配置)
*/
public
static
void
computeAndSetLowerBounds
(
Chromosome
chromosome
,
GlobalParam
globalParam
)
{
double
[]
objectives
=
chromosome
.
getObjectives
();
if
(
objectives
==
null
||
objectives
.
length
==
0
)
return
;
double
[]
lowerBounds
=
new
double
[
objectives
.
length
];
// 按 GlobalParam.objectiveConfigs 的顺序(与 objectives 数组一一对应)
// 注意:globalParam.objectiveConfigs 已按 level 排序
List
<
ObjectiveConfig
>
configs
=
globalParam
.
getObjectiveConfigs
();
for
(
int
i
=
0
;
i
<
objectives
.
length
&&
i
<
configs
.
size
();
i
++)
{
ObjectiveConfig
config
=
configs
.
get
(
i
);
if
(!
config
.
isEnabled
())
{
lowerBounds
[
i
]
=
0.0
;
continue
;
}
String
name
=
config
.
getName
();
if
(
GlobalParam
.
OBJECTIVE_MAKESPAN
.
equals
(
name
))
{
lowerBounds
[
i
]
=
computeMakespanLowerBound
(
chromosome
);
}
else
if
(
GlobalParam
.
OBJECTIVE_TARDINESS
.
equals
(
name
))
{
// 延迟理论上界为 0(全部按时)
lowerBounds
[
i
]
=
0.0
;
}
else
if
(
GlobalParam
.
OBJECTIVE_SETUP_TIME
.
equals
(
name
))
{
// 换型时间下界:若换型只由产品类型决定则为 0
lowerBounds
[
i
]
=
0.0
;
}
else
if
(
GlobalParam
.
OBJECTIVE_FLOW_TIME
.
equals
(
name
))
{
lowerBounds
[
i
]
=
computeFlowTimeLowerBound
(
chromosome
);
}
else
if
(
GlobalParam
.
OBJECTIVE_MACHINE_LOAD
.
equals
(
name
))
{
// 负载均衡标准差理论上界为 0
lowerBounds
[
i
]
=
0.0
;
}
else
{
lowerBounds
[
i
]
=
0.0
;
}
}
chromosome
.
setLowerBoundObjectives
(
lowerBounds
);
}
/**
* 计算 Makespan(最大完工时间)的理论下界 = DAG 关键路径长度。
*
* <p>思路:对 job shop 问题,完工时间下界 = 所有工序的最长无冲突调度路径长度,
* 即在不考虑机器冲突的条件下,从根节点到终点的最长加权和路径(边权重 = 加工时间)。
* 通过拓扑排序 + 动态规划实现(时间复杂度 O(V+E))。
*/
private
static
double
computeMakespanLowerBound
(
Chromosome
chromosome
)
{
List
<
GAScheduleResult
>
result
=
chromosome
.
getResult
();
List
<
GroupResult
>
operatRel
=
chromosome
.
getOperatRel
();
List
<
Entry
>
allOperations
=
chromosome
.
getAllOperations
();
if
(
result
==
null
||
result
.
isEmpty
())
return
0.0
;
// ---- 1. 构建 nodeId -> processingTime 映射 ----
Map
<
Integer
,
Double
>
entryProcessingTime
=
new
HashMap
<>();
if
(
allOperations
!=
null
)
{
for
(
Entry
e
:
allOperations
)
{
entryProcessingTime
.
put
(
e
.
getId
(),
(
double
)
e
.
getMinProcessingTime
());
}
}
// 如果 allOperations 缺失,从 result 推算(使用 flowTime 作为加工时间近似)
if
(
entryProcessingTime
.
isEmpty
())
{
for
(
GAScheduleResult
r
:
result
)
{
entryProcessingTime
.
put
(
r
.
getOperationId
(),
Math
.
max
(
1.0
,
r
.
getProcessingTime
()));
}
}
// ---- 2. 构建 DAG:nodeId -> [childNodeIds] ----
Map
<
Integer
,
List
<
Integer
>>
dag
=
new
HashMap
<>();
Set
<
Integer
>
allNodeIds
=
new
HashSet
<>();
// 初始化所有节点
for
(
GAScheduleResult
r
:
result
)
{
int
eid
=
r
.
getOperationId
();
dag
.
putIfAbsent
(
eid
,
new
ArrayList
<>());
allNodeIds
.
add
(
eid
);
}
// 添加边:parent -> child(基于 GroupResult.newParentIds)
if
(
operatRel
!=
null
)
{
for
(
GroupResult
gr
:
operatRel
)
{
List
<
NodeInfo
>
nodes
=
gr
.
getNodeInfoList
();
if
(
nodes
==
null
)
continue
;
for
(
NodeInfo
node
:
nodes
)
{
Integer
nodeId
=
node
.
getGlobalSerial
();
dag
.
putIfAbsent
(
nodeId
,
new
ArrayList
<>());
allNodeIds
.
add
(
nodeId
);
List
<
Integer
>
childIds
=
node
.
getNewChildIds
();
if
(
childIds
!=
null
)
{
for
(
Integer
childId
:
childIds
)
{
dag
.
get
(
nodeId
).
add
(
childId
);
allNodeIds
.
add
(
childId
);
}
}
}
}
}
// ---- 3. 计算入度 ----
Map
<
Integer
,
Integer
>
inDegree
=
new
HashMap
<>();
for
(
Integer
nodeId
:
allNodeIds
)
{
inDegree
.
put
(
nodeId
,
0
);
}
for
(
Integer
parent
:
dag
.
keySet
())
{
for
(
Integer
child
:
dag
.
get
(
parent
))
{
inDegree
.
merge
(
child
,
1
,
Integer:
:
sum
);
}
}
// ---- 4. 拓扑排序 + 动态规划找最长路径 ----
// earliest[nodeId] = 从任意根节点到 nodeId 的最长路径
Map
<
Integer
,
Double
>
earliest
=
new
HashMap
<>();
Queue
<
Integer
>
queue
=
new
LinkedList
<>();
// 初始化:入度为 0 的节点 earliest = processingTime
for
(
Integer
nodeId
:
allNodeIds
)
{
if
(
inDegree
.
get
(
nodeId
)
==
0
)
{
double
pt
=
entryProcessingTime
.
getOrDefault
(
nodeId
,
0.0
);
earliest
.
put
(
nodeId
,
pt
);
queue
.
offer
(
nodeId
);
}
}
while
(!
queue
.
isEmpty
())
{
Integer
parent
=
queue
.
poll
();
double
parentEarliest
=
earliest
.
getOrDefault
(
parent
,
0.0
);
double
parentPt
=
entryProcessingTime
.
getOrDefault
(
parent
,
0.0
);
double
parentFinish
=
parentEarliest
;
// finish = start + pt,这里 start = parentEarliest - pt
for
(
Integer
child
:
dag
.
getOrDefault
(
parent
,
Collections
.
emptyList
()))
{
double
childPt
=
entryProcessingTime
.
getOrDefault
(
child
,
0.0
);
double
childEarliestCandidate
=
parentEarliest
+
childPt
;
earliest
.
put
(
child
,
Math
.
max
(
earliest
.
getOrDefault
(
child
,
0.0
),
childEarliestCandidate
));
inDegree
.
merge
(
child
,
-
1
,
Integer:
:
sum
);
if
(
inDegree
.
get
(
child
)
==
0
)
{
queue
.
offer
(
child
);
}
}
}
// ---- 5. 关键路径 = 所有节点 earliest 的最大值 ----
double
criticalPath
=
0.0
;
for
(
double
val
:
earliest
.
values
())
{
if
(
val
>
criticalPath
)
criticalPath
=
val
;
}
return
criticalPath
;
}
/**
* 计算 FlowTime(总流程时间)的理论下界 = 所有工序的加工时间之和。
*/
private
static
double
computeFlowTimeLowerBound
(
Chromosome
chromosome
)
{
List
<
GAScheduleResult
>
result
=
chromosome
.
getResult
();
if
(
result
==
null
||
result
.
isEmpty
())
return
0.0
;
return
result
.
stream
()
.
mapToDouble
(
r
->
Math
.
max
(
1.0
,
r
.
getProcessingTime
()))
.
sum
();
}
/**
* 计算总 Tardiness 的 Gap。
* lowerBound = 0,所以 Gap = current(即延迟小时数的绝对值)。
* 结果为 (current - 0) / 1 = current,与延迟同量纲。
*/
public
static
double
computeTardinessGap
(
double
tardiness
)
{
if
(
tardiness
<=
0
)
return
0.0
;
return
tardiness
;
// lowerBound=0,用 1 做分母
}
/**
* 计算 Makespan Gap。
*/
public
static
double
computeMakespanGap
(
double
makespan
,
double
lowerBound
)
{
if
(
lowerBound
<=
0
)
return
0.0
;
return
(
makespan
-
lowerBound
)
/
lowerBound
;
}
/**
* 计算 FlowTime Gap。
*/
public
static
double
computeFlowTimeGap
(
double
flowTime
,
double
lowerBound
)
{
if
(
lowerBound
<=
0
)
return
0.0
;
return
(
flowTime
-
lowerBound
)
/
lowerBound
;
}
/**
* 计算 MachineLoad Gap(标准差的下界为 0)。
*/
public
static
double
computeMachineLoadGap
(
double
machineLoadStd
)
{
if
(
machineLoadStd
<=
0
)
return
0.0
;
return
machineLoadStd
;
// lowerBound=0
}
/**
* 格式化 Gap 为百分比字符串。
*/
public
static
String
formatGap
(
double
gap
)
{
if
(
gap
==
0.0
)
return
"0.00%"
;
return
String
.
format
(
"%.2f%%"
,
gap
*
100.0
);
}
/**
* 生成 KPI Gap 报告字符串(供调用方直接打印)。
* 格式:`KPI Gap 报告 (越小越好): [Makespan: cur / LB → Gap=x%] [FlowTime: ...] ...`
*/
public
static
String
generateGapReport
(
Chromosome
chromosome
)
{
double
[]
objectives
=
chromosome
.
getObjectives
();
double
[]
lowerBounds
=
chromosome
.
getLowerBoundObjectives
();
if
(
objectives
==
null
||
objectives
.
length
==
0
)
{
return
"KPI Gap 报告: 无 objectives 数据"
;
}
String
[]
names
=
{
"Makespan"
,
"FlowTime"
,
"SetupTime"
,
"MachineLoad"
,
"Tardiness"
};
StringBuilder
sb
=
new
StringBuilder
(
"KPI Gap 报告 (越小越好): "
);
for
(
int
i
=
0
;
i
<
objectives
.
length
;
i
++)
{
double
current
=
objectives
[
i
];
double
lb
=
(
lowerBounds
!=
null
&&
i
<
lowerBounds
.
length
)
?
lowerBounds
[
i
]
:
0.0
;
double
gap
;
if
(
lb
>
0
)
{
gap
=
(
current
-
lb
)
/
lb
;
}
else
if
(
current
>
0
)
{
gap
=
current
;
// lowerBound=0 时 gap = current 本身(绝对偏离量)
}
else
{
gap
=
0.0
;
}
String
name
=
i
<
names
.
length
?
names
[
i
]
:
(
"Obj["
+
i
+
"]"
);
sb
.
append
(
String
.
format
(
"[%s: %.4f / LB=%.4f → Gap=%s] "
,
name
,
current
,
lb
,
formatGap
(
gap
)));
}
return
sb
.
toString
();
}
}
src/main/java/com/aps/service/Algorithm/RoutingDataService.java
View file @
41cab0d7
...
...
@@ -265,6 +265,7 @@ public class RoutingDataService {
List
<
ProdEquipment
>
Equipments
=
ProdEquipments
.
stream
()
.
filter
(
t
->
t
.
getExecId
().
equals
(
op
.
getExecId
()))
.
collect
(
Collectors
.
toList
());
double
minProcessingTime
=
999999999
;
if
(
Equipments
!=
null
&&
Equipments
.
size
()
>
0
)
{
List
<
MachineOption
>
mos
=
new
ArrayList
<>();
for
(
ProdEquipment
e
:
Equipments
)
{
...
...
@@ -277,7 +278,7 @@ public class RoutingDataService {
totalprocessTime
=
e
.
getSpeed
()/
e
.
getSingleOut
().
doubleValue
()*
entry
.
getQuantity
();
}
minProcessingTime
=
Math
.
min
(
minProcessingTime
,
totalprocessTime
);
if
(
machineIds
.
containsKey
(
e
.
getEquipId
()))
{
if
(
machineIds
.
get
(
e
.
getEquipId
())<
totalprocessTime
)
...
...
@@ -307,6 +308,7 @@ public class RoutingDataService {
mos
.
add
(
mo
);
}
entry
.
setMinProcessingTime
(
minProcessingTime
);
entry
.
setMachineOptions
(
mos
);
}
}
...
...
src/main/java/com/aps/service/Algorithm/SimulatedAnnealing.java
View file @
41cab0d7
// Source code is decompiled from a .class file using FernFlower decompiler (from Intellij IDEA).
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.Chromosome
;
import
com.aps.entity.Algorithm.GAScheduleResult
;
import
com.aps.entity.Algorithm.IDAndChildID.GroupResult
;
import
com.aps.entity.basic.*
;
import
java.util.*
;
import
java.util.concurrent.*
;
import
com.aps.entity.basic.Entry
;
import
com.aps.entity.basic.Machine
;
import
com.aps.entity.basic.Material
;
import
com.aps.entity.basic.Order
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Random
;
import
java.util.Set
;
import
java.util.TreeMap
;
import
java.util.concurrent.ArrayBlockingQueue
;
import
java.util.concurrent.CopyOnWriteArrayList
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.ThreadPoolExecutor
;
import
java.util.concurrent.TimeUnit
;
import
java.util.stream.Collectors
;
/**
* 模拟退火算法
*/
public
class
SimulatedAnnealing
{
private
final
Random
rnd
=
new
Random
();
// ==================== 改进判断参数 ====================
private
static
final
double
SIGNIFICANT_IMPROVEMENT_THRESHOLD
=
0.0001
;
// 显著改进阈值:只有改进超过这个值才重置无改进计数
private
static
final
double
SIGNIFICANT_IMPROVEMENT_THRESHOLD
=
1.0
E
-
4
;
private
List
<
Entry
>
allOperations
;
private
List
<
Order
>
orders
;
private
TreeMap
<
String
,
Material
>
materials
;
private
List
<
GroupResult
>
_entryRel
;
private
FitnessCalculator
fitnessCalculator
;
private
Map
<
String
,
Entry
>
entrys
;
private
Map
<
Integer
,
Entry
>
entrybyids
;
private
List
<
Machine
>
cachedMachines
;
private
List
<
Order
>
cachedOrders
;
private
List
<
GroupResult
>
cachedEntryRel
;
private
TreeMap
<
String
,
Material
>
cachedMaterials
;
private
List
<
Entry
>
cachedAllOperations
;
private
final
ExecutorService
decodeExecutor
;
private
void
log
(
String
message
)
{
log
(
message
,
false
);
this
.
log
(
message
,
false
);
}
private
void
log
(
String
message
,
boolean
enableLogging
)
{
if
(
enableLogging
)
{
FileHelper
.
writeLogFile
(
message
);
}
}
private
List
<
Entry
>
allOperations
;
private
List
<
Order
>
orders
;
private
TreeMap
<
String
,
Material
>
materials
;
private
List
<
GroupResult
>
_entryRel
;
private
FitnessCalculator
fitnessCalculator
;
private
Map
<
String
,
Entry
>
entrys
;
private
Map
<
Integer
,
Entry
>
entrybyids
;
public
SimulatedAnnealing
(
List
<
Entry
>
allOperations
,
List
<
Order
>
orders
,
TreeMap
<
String
,
Material
>
materials
,
List
<
GroupResult
>
entryRel
,
FitnessCalculator
_fitnessCalculator
)
{
}
public
SimulatedAnnealing
(
List
<
Entry
>
allOperations
,
List
<
Order
>
orders
,
TreeMap
<
String
,
Material
>
materials
,
List
<
GroupResult
>
entryRel
,
FitnessCalculator
_fitnessCalculator
)
{
this
.
decodeExecutor
=
new
ThreadPoolExecutor
(
Runtime
.
getRuntime
().
availableProcessors
()
-
1
,
Runtime
.
getRuntime
().
availableProcessors
()
-
1
,
0L
,
TimeUnit
.
MILLISECONDS
,
new
ArrayBlockingQueue
(
200
),
new
ThreadPoolExecutor
.
CallerRunsPolicy
());
this
.
allOperations
=
allOperations
;
this
.
orders
=
orders
;
this
.
materials
=
materials
;
_entryRel
=
entryRel
;
Map
<
Integer
,
Object
>
mp
=
buildEntryKey
();
this
.
_entryRel
=
entryRel
;
Map
<
Integer
,
Object
>
mp
=
this
.
buildEntryKey
();
this
.
fitnessCalculator
=
_fitnessCalculator
;
entrys
=(
Map
<
String
,
Entry
>)
mp
.
get
(
1
);
entrybyids
=(
Map
<
Integer
,
Entry
>)
mp
.
get
(
2
);
}
private
final
ExecutorService
decodeExecutor
=
new
ThreadPoolExecutor
(
Runtime
.
getRuntime
().
availableProcessors
()
-
1
,
// 核心线程数=CPU-1,无切换开销
Runtime
.
getRuntime
().
availableProcessors
()
-
1
,
// 最大线程数=核心数
0L
,
TimeUnit
.
MILLISECONDS
,
new
ArrayBlockingQueue
<>(
200
),
// 有界队列,避免内存溢出
new
ThreadPoolExecutor
.
CallerRunsPolicy
()
// 任务满了主线程执行,不丢失任务
);
public
List
<
Chromosome
>
batchSearch
(
List
<
Chromosome
>
chromosomes
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
List
<
Chromosome
>
saHcOptimized
=
new
ArrayList
<>();
// CompletableFuture.allOf(chromosomes.stream()
// .map(chromosome -> CompletableFuture.runAsync(() -> {
// Chromosome optimized = searchWithHillClimbing(chromosome, decoder, param);
// saHcOptimized.add(optimized);
// }, decodeExecutor))
// .toArray(CompletableFuture[]::new))
// .join();
for
(
Chromosome
chromosome:
chromosomes
)
{
Chromosome
optimized
=
searchWithHillClimbing
(
chromosome
,
vns
,
decoder
,
machines
);
saHcOptimized
.
add
(
optimized
);
}
return
saHcOptimized
;
this
.
entrys
=
(
Map
)
mp
.
get
(
1
);
this
.
entrybyids
=
(
Map
)
mp
.
get
(
2
);
this
.
cachedAllOperations
=
ProductionDeepCopyUtil
.
deepCopyList
(
new
CopyOnWriteArrayList
(
allOperations
),
Entry
.
class
);
this
.
cachedOrders
=
ProductionDeepCopyUtil
.
deepCopyList
(
new
CopyOnWriteArrayList
(
orders
),
Order
.
class
);
this
.
cachedEntryRel
=
ProductionDeepCopyUtil
.
deepCopyList
(
new
CopyOnWriteArrayList
(
entryRel
),
GroupResult
.
class
);
this
.
cachedMaterials
=
ProductionDeepCopyUtil
.
deepCopyTreeMap
(
materials
,
String
.
class
,
Material
.
class
);
}
public
Chromosome
batchSearchGetMax
(
List
<
Chromosome
>
chromosomes
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
List
<
Chromosome
>
saHcOptimized
=
batchSearch
(
chromosomes
,
vns
,
decoder
,
machines
);
public
List
<
Chromosome
>
batchSearch
(
List
<
Chromosome
>
chromosomes
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
List
<
Chromosome
>
saHcOptimized
=
new
ArrayList
(
);
int
bestidx
=
Getbest
(
saHcOptimized
,
null
);
if
(
bestidx
>-
1
)
{
return
saHcOptimized
.
get
(
bestidx
);
for
(
Chromosome
chromosome
:
chromosomes
)
{
Chromosome
optimized
=
this
.
searchWithHillClimbing
(
chromosome
,
vns
,
decoder
,
machines
);
saHcOptimized
.
add
(
optimized
);
}
return
null
;
return
saHcOptimized
;
}
/**
* 模拟退火搜索,当温度降低到一定程度后切换到爬山法
* 流程:模拟退火全局探索(按概率接受劣解)→ 降温 → 温度低时爬山法局部求精 → 输出最优
*/
public
Chromosome
searchWithHillClimbing
(
Chromosome
chromosome
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
log
(
"模拟退火+爬山法 - 开始执行"
,
true
);
Chromosome
current
=
ProductionDeepCopyUtil
.
deepCopy
(
chromosome
,
Chromosome
.
class
);
Chromosome
best
=
ProductionDeepCopyUtil
.
deepCopy
(
chromosome
,
Chromosome
.
class
);
writeKpi
(
best
);
public
Chromosome
batchSearchGetMax
(
List
<
Chromosome
>
chromosomes
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
List
<
Chromosome
>
saHcOptimized
=
this
.
batchSearch
(
chromosomes
,
vns
,
decoder
,
machines
);
int
bestidx
=
this
.
Getbest
(
saHcOptimized
,
(
Chromosome
)
null
);
return
bestidx
>
-
1
?
(
Chromosome
)
saHcOptimized
.
get
(
bestidx
)
:
null
;
}
// 记录初始KPI用于计算改进率
double
[]
initialFitnessLevel
=
best
.
getFitnessLevel
().
clone
();
public
Chromosome
searchWithHillClimbing
(
Chromosome
chromosome
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
this
.
log
(
"模拟退火+爬山法 - 开始执行"
,
true
);
Chromosome
current
=
vns
.
copyChromosome
(
chromosome
);
this
.
decode
(
decoder
,
current
,
machines
);
Chromosome
best
=
this
.
lightCopy
(
current
);
this
.
writeKpi
(
best
);
double
[]
initialFitnessLevel
=
(
double
[])
best
.
getFitnessLevel
().
clone
();
double
initialFitness
=
best
.
getFitness
();
// log("模拟退火+爬山法 - 初始化解码完成");
// 初始化温度(优化:更快收敛)
double
temperature
=
100.0
;
double
coolingRate
=
0.90
;
// 优化:降温更快
double
temperatureThreshold
=
5.0
;
// 优化:温度阈值更高
int
maxIterations
=
100
;
// 优化:从300减少到100次
double
temperature
=
(
double
)
100.0
F
;
double
coolingRate
=
0.9
;
double
temperatureThreshold
=
(
double
)
5.0
F
;
int
maxIterations
=
100
;
int
noImproveCount
=
0
;
int
maxNoImprove
=
15
;
// 优化:从30减少到15次
// 新增:改进率监控参数
int
stagnantWindow
=
15
;
// 观察窗口大小
int
[]
recentImprovements
=
new
int
[
stagnantWindow
];
// 记录最近窗口内的改进情况
double
improvementRateThreshold
=
0.05
;
// 改进率阈值(5%)
log
(
String
.
format
(
"模拟退火+爬山法 - 参数配置:温度=%.1f, 降温率=%.2f, 阈值=%.1f, 最大迭代=%d, 最大无改进=%d"
,
temperature
,
coolingRate
,
temperatureThreshold
,
maxIterations
,
maxNoImprove
));
int
maxNoImprove
=
15
;
int
stagnantWindow
=
15
;
int
[]
recentImprovements
=
new
int
[
stagnantWindow
];
double
improvementRateThreshold
=
0.05
;
this
.
log
(
String
.
format
(
"模拟退火+爬山法 - 参数配置:温度=%.1f, 降温率=%.2f, 阈值=%.1f, 最大迭代=%d, 最大无改进=%d"
,
temperature
,
coolingRate
,
temperatureThreshold
,
maxIterations
,
maxNoImprove
));
int
acceptCount
=
0
;
int
improveCount
=
0
;
int
significantImproveCount
=
0
;
int
totalIterations
=
0
;
for
(
int
i
=
0
;
i
<
maxIterations
;
i
++
)
{
for
(
int
i
=
0
;
i
<
maxIterations
;
++
i
)
{
totalIterations
=
i
+
1
;
boolean
improved
=
false
;
decoder
.
DelOrder
(
current
);
// 1. 使用智能策略生成邻域解(找瓶颈工序/设备)
Chromosome
neighbor
=
vns
.
generateNeighbor
(
current
);
// 2. 解码
decode
(
decoder
,
neighbor
,
machines
);
// 3. 计算能量差
double
energyDifference
=
calculateEnergyDifference
(
neighbor
,
current
);
// 4. 按概率接受新解(模拟退火核心:有概率接受劣解)
this
.
decode
(
decoder
,
neighbor
,
machines
);
double
energyDifference
=
this
.
calculateEnergyDifference
(
neighbor
,
current
);
boolean
accepted
=
false
;
if
(
energyDifference
>
0
||
rnd
.
nextDouble
()
<
Math
.
exp
(
energyDifference
/
temperature
))
{
if
(
energyDifference
>
(
double
)
0.0
F
||
this
.
rnd
.
nextDouble
()
<
Math
.
exp
(
energyDifference
/
temperature
))
{
current
=
neighbor
;
accepted
=
true
;
acceptCount
++;
// 更新全局最优
if
(
isBetter
(
current
,
best
))
{
best
=
ProductionDeepCopyUtil
.
deepCopy
(
current
,
Chromosome
.
class
);
writeKpi
(
best
);
++
acceptCount
;
if
(
this
.
isBetter
(
neighbor
,
best
))
{
best
=
this
.
lightCopy
(
neighbor
);
this
.
writeKpi
(
best
);
improved
=
true
;
improveCount
++
;
boolean
isSignificant
=
isSignificantImprovement
(
current
,
best
);
++
improveCount
;
boolean
isSignificant
=
this
.
isSignificantImprovement
(
neighbor
,
best
);
if
(
isSignificant
)
{
noImproveCount
=
0
;
// 只有显著改进才重置无改进计数
significantImproveCount
++
;
logImprovementDetails
(
best
,
initialFitnessLevel
,
initialFitness
,
totalIterations
);
log
(
String
.
format
(
"模拟退火+爬山法 - 迭代%d:找到更优解(显著),fitness=%.4f"
,
totalIterations
,
best
.
getFitness
()),
true
);
noImproveCount
=
0
;
++
significantImproveCount
;
this
.
logImprovementDetails
(
best
,
initialFitnessLevel
,
initialFitness
,
totalIterations
);
this
.
log
(
String
.
format
(
"模拟退火+爬山法 - 迭代%d:找到更优解(显著),fitness=%.4f"
,
totalIterations
,
best
.
getFitness
()),
true
);
}
else
{
// 微小改进也接受,但不重置计数
log
(
String
.
format
(
"模拟退火+爬山法 - 迭代%d:找到更优解(微小),fitness=%.4f"
,
totalIterations
,
best
.
getFitness
()),
true
);
this
.
log
(
String
.
format
(
"模拟退火+爬山法 - 迭代%d:找到更优解(微小),fitness=%.4f"
,
totalIterations
,
best
.
getFitness
()),
true
);
}
}
}
if
(!
improved
)
{
noImproveCount
++
;
++
noImproveCount
;
}
// 记录本次改进情况
if
(
totalIterations
<=
stagnantWindow
)
{
recentImprovements
[
totalIterations
-
1
]
=
improved
?
1
:
0
;
}
// 5. 降温
temperature
*=
coolingRate
;
if
(
totalIterations
%
10
==
0
)
{
System
.
gc
();
}
// 每30次迭代输出一次状态
if
((
totalIterations
)
%
30
==
0
)
{
log
(
String
.
format
(
"模拟退火+爬山法 - 迭代%d/%d:温度=%.4f, 接受数=%d, 改进数=%d, 无改进连续=%d, 总改进率=%.2f%%"
,
totalIterations
,
maxIterations
,
temperature
,
acceptCount
,
improveCount
,
noImproveCount
,
totalIterations
>
0
?
(
double
)
improveCount
/
totalIterations
*
100
:
0
));
if
(
totalIterations
%
30
==
0
)
{
this
.
log
(
String
.
format
(
"模拟退火+爬山法 - 迭代%d/%d:温度=%.4f, 接受数=%d, 改进数=%d, 无改进连续=%d, 总改进率=%.2f%%"
,
totalIterations
,
maxIterations
,
temperature
,
acceptCount
,
improveCount
,
noImproveCount
,
totalIterations
>
0
?
(
double
)
improveCount
/
(
double
)
totalIterations
*
(
double
)
100.0
F
:
(
double
)
0.0
F
));
}
// 6. 提前停止条件
boolean
shouldStop
=
false
;
String
stopReason
=
""
;
if
(
temperature
<
temperatureThreshold
)
{
shouldStop
=
true
;
stopReason
=
"温度低于阈值"
;
...
...
@@ -201,194 +164,141 @@ public class SimulatedAnnealing {
shouldStop
=
true
;
stopReason
=
String
.
format
(
"连续无改进达到上限(%d次)"
,
maxNoImprove
);
}
else
if
(
totalIterations
>=
stagnantWindow
)
{
// 检查改进率是否过低
double
recentImproveRate
=
calculateRecentImprovementRate
(
recentImprovements
,
stagnantWindow
);
double
recentImproveRate
=
this
.
calculateRecentImprovementRate
(
recentImprovements
,
stagnantWindow
);
if
(
recentImproveRate
<
improvementRateThreshold
)
{
shouldStop
=
true
;
stopReason
=
String
.
format
(
"最近%d次迭代改进率过低(%.2f%%)"
,
stagnantWindow
,
recentImproveRate
*
100
);
stopReason
=
String
.
format
(
"最近%d次迭代改进率过低(%.2f%%)"
,
stagnantWindow
,
recentImproveRate
*
(
double
)
100.0
F
);
}
}
if
(
shouldStop
)
{
log
(
String
.
format
(
"模拟退火+爬山法 - 提前停止:%s,迭代%d次,最终温度=%.4f"
,
stopReason
,
totalIterations
,
temperature
));
logFinalSummary
(
best
,
initialFitnessLevel
,
initialFitness
,
improveCount
,
significantImproveCount
,
totalIterations
);
log
(
"模拟退火+爬山法 - 切换到爬山法求精"
);
HillClimbing
hillClimbing
=
new
HillClimbing
(
allOperations
,
orders
,
materials
,
_entryRel
,
fitnessCalculator
);
this
.
log
(
String
.
format
(
"模拟退火+爬山法 - 提前停止:%s,迭代%d次,最终温度=%.4f"
,
stopReason
,
totalIterations
,
temperature
));
this
.
logFinalSummary
(
best
,
initialFitnessLevel
,
initialFitness
,
improveCount
,
significantImproveCount
,
totalIterations
);
this
.
log
(
"模拟退火+爬山法 - 切换到爬山法求精"
);
HillClimbing
hillClimbing
=
new
HillClimbing
(
this
.
allOperations
,
this
.
orders
,
this
.
materials
,
this
.
_entryRel
,
this
.
fitnessCalculator
);
Chromosome
refined
=
hillClimbing
.
search
(
best
,
decoder
,
machines
);
log
(
"模拟退火+爬山法 - 爬山法求精完成"
);
this
.
log
(
"模拟退火+爬山法 - 爬山法求精完成"
);
return
refined
;
}
}
log
(
String
.
format
(
"模拟退火+爬山法 - 完成所有%d次迭代,最终fitness=%.4f"
,
maxIterations
,
best
.
getFitness
()),
true
);
logFinalSummary
(
best
,
initialFitnessLevel
,
initialFitness
,
improveCount
,
significantImproveCount
,
totalIterations
);
// 7. 输出全局最优排产
this
.
log
(
String
.
format
(
"模拟退火+爬山法 - 完成所有%d次迭代,最终fitness=%.4f"
,
maxIterations
,
best
.
getFitness
()),
true
);
this
.
logFinalSummary
(
best
,
initialFitnessLevel
,
initialFitness
,
improveCount
,
significantImproveCount
,
totalIterations
);
return
best
;
}
/**
* 模拟退火搜索
* 流程:模拟退火全局探索(按概率接受劣解)→ 降温 → 输出最优
*/
public
Chromosome
search
(
Chromosome
chromosome
,
TabuSearch
tabusearch
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
log
(
"模拟退火 - 开始执行"
,
true
);
public
Chromosome
search
(
Chromosome
chromosome
,
TabuSearch
tabusearch
,
VariableNeighborhoodSearch
vns
,
GeneticDecoder
decoder
,
List
<
Machine
>
machines
)
{
this
.
log
(
"模拟退火 - 开始执行"
,
true
);
Chromosome
current
=
vns
.
copyChromosome
(
chromosome
);
decode
(
decoder
,
current
,
machines
);
Chromosome
best
=
ProductionDeepCopyUtil
.
deepCopy
(
current
,
Chromosome
.
class
);
writeKpi
(
best
);
// 初始化解码
// 记录初始KPI用于计算改进率
double
[]
initialFitnessLevel
=
best
.
getFitnessLevel
().
clone
();
this
.
decode
(
decoder
,
current
,
machines
);
Chromosome
best
=
this
.
lightCopy
(
current
);
this
.
writeKpi
(
best
);
double
[]
initialFitnessLevel
=
(
double
[])
best
.
getFitnessLevel
().
clone
();
double
initialFitness
=
best
.
getFitness
();
// log("模拟退火+爬山法 - 初始化解码完成");
// 初始化温度(优化:更快收敛)
double
temperature
=
100.0
;
double
coolingRate
=
0.90
;
// 优化:降温更快
double
temperatureThreshold
=
5.0
;
// 优化:温度阈值更高
int
maxIterations
=
80
;
// 优化:从300减少到80次
double
temperature
=
(
double
)
100.0
F
;
double
coolingRate
=
0.9
;
double
temperatureThreshold
=
(
double
)
5.0
F
;
int
maxIterations
=
80
;
int
noImproveCount
=
0
;
int
maxNoImprove
=
10
;
// 优化:从20减少到10次
// 新增:改进率监控参数
int
stagnantWindow
=
10
;
// 观察窗口大小
int
[]
recentImprovements
=
new
int
[
stagnantWindow
];
// 记录最近窗口内的改进情况
double
improvementRateThreshold
=
0.001
;
// 改进率阈值
log
(
String
.
format
(
"模拟退火 - 参数配置:温度=%.1f, 降温率=%.2f, 阈值=%.1f, 最大迭代=%d, 最大无改进=%d"
,
temperature
,
coolingRate
,
temperatureThreshold
,
maxIterations
,
maxNoImprove
));
int
maxNoImprove
=
10
;
int
stagnantWindow
=
10
;
int
[]
recentImprovements
=
new
int
[
stagnantWindow
];
double
improvementRateThreshold
=
0.001
;
this
.
log
(
String
.
format
(
"模拟退火 - 参数配置:温度=%.1f, 降温率=%.2f, 阈值=%.1f, 最大迭代=%d, 最大无改进=%d"
,
temperature
,
coolingRate
,
temperatureThreshold
,
maxIterations
,
maxNoImprove
));
int
acceptCount
=
0
;
int
improveCount
=
0
;
int
significantImproveCount
=
0
;
int
totalIterations
=
0
;
for
(
int
i
=
0
;
i
<
maxIterations
;
i
++
)
{
for
(
int
i
=
0
;
i
<
maxIterations
;
++
i
)
{
totalIterations
=
i
+
1
;
boolean
improved
=
false
;
log
(
String
.
format
(
"模拟退火 - 迭代%d:"
,
totalIterations
));
this
.
log
(
String
.
format
(
"模拟退火 - 迭代%d:"
,
totalIterations
));
decoder
.
DelOrder
(
current
);
// 1. 使用智能策略生成邻域解(找瓶颈工序/设备)
Chromosome
neighbor
=
vns
.
generateNeighbor
(
current
);
// 2. 解码
decode
(
decoder
,
neighbor
,
machines
);
// 跳过禁忌解(除非是最优解)
if
(
tabusearch
.
isTabu
(
neighbor
.
getGeneStr
())
&&
!
isBetter
(
current
,
best
))
{
this
.
decode
(
decoder
,
neighbor
,
machines
);
if
(
tabusearch
.
isTabu
(
neighbor
.
getGeneStr
())
&&
!
this
.
isBetter
(
current
,
best
))
{
temperature
*=
coolingRate
;
noImproveCount
++;
// 记录本次无改进
++
noImproveCount
;
if
(
totalIterations
<=
stagnantWindow
)
{
recentImprovements
[
totalIterations
-
1
]
=
0
;
}
continue
;
}
// 3. 计算能量差
double
energyDifference
=
calculateEnergyDifference
(
neighbor
,
current
);
// 4. 按概率接受新解(模拟退火核心:有概率接受劣解)
boolean
accepted
=
false
;
if
(
energyDifference
>
0
||
rnd
.
nextDouble
()
<
Math
.
exp
(
energyDifference
/
temperature
))
{
current
=
neighbor
;
acceptCount
++;
if
(
isBetter
(
current
,
best
))
{
best
=
ProductionDeepCopyUtil
.
deepCopy
(
current
,
Chromosome
.
class
);
tabusearch
.
addToTabuList
(
best
.
getGeneStr
());
writeKpi
(
best
);
improved
=
true
;
improveCount
++;
boolean
isSignificant
=
isSignificantImprovement
(
current
,
best
);
if
(
isSignificant
)
{
noImproveCount
=
0
;
significantImproveCount
++;
logImprovementDetails
(
best
,
initialFitnessLevel
,
initialFitness
,
totalIterations
);
log
(
String
.
format
(
"模拟退火 - 迭代%d:找到更优解(显著),fitness=%.4f"
,
totalIterations
,
best
.
getFitness
()));
}
else
{
log
(
String
.
format
(
"模拟退火 - 迭代%d:找到更优解(微小),fitness=%.4f"
,
totalIterations
,
best
.
getFitness
()));
}
else
{
double
energyDifference
=
this
.
calculateEnergyDifference
(
neighbor
,
current
);
boolean
accepted
=
false
;
if
(
energyDifference
>
(
double
)
0.0
F
||
this
.
rnd
.
nextDouble
()
<
Math
.
exp
(
energyDifference
/
temperature
))
{
current
=
neighbor
;
++
acceptCount
;
if
(
this
.
isBetter
(
neighbor
,
best
))
{
best
=
this
.
lightCopy
(
neighbor
);
tabusearch
.
addToTabuList
(
best
.
getGeneStr
());
this
.
writeKpi
(
best
);
improved
=
true
;
++
improveCount
;
boolean
isSignificant
=
this
.
isSignificantImprovement
(
neighbor
,
best
);
if
(
isSignificant
)
{
noImproveCount
=
0
;
++
significantImproveCount
;
this
.
logImprovementDetails
(
best
,
initialFitnessLevel
,
initialFitness
,
totalIterations
);
this
.
log
(
String
.
format
(
"模拟退火 - 迭代%d:找到更优解(显著),fitness=%.4f"
,
totalIterations
,
best
.
getFitness
()));
}
else
{
this
.
log
(
String
.
format
(
"模拟退火 - 迭代%d:找到更优解(微小),fitness=%.4f"
,
totalIterations
,
best
.
getFitness
()));
}
}
}
// decoder.DelOrder(current);
}
if
(!
improved
)
{
noImproveCount
++;
}
// 记录本次改进情况
if
(
totalIterations
<=
stagnantWindow
)
{
recentImprovements
[
totalIterations
-
1
]
=
improved
?
1
:
0
;
}
// 5. 降温
temperature
*=
coolingRate
;
if
(!
improved
)
{
++
noImproveCount
;
}
// 每次迭代都输出状态
log
(
String
.
format
(
"模拟退火 - 迭代%d/%d:温度=%.4f, 接受数=%d, 改进数=%d, 无改进连续=%d, 总改进率=%.2f%%"
,
totalIterations
,
maxIterations
,
temperature
,
acceptCount
,
improveCount
,
noImproveCount
,
totalIterations
>
0
?
(
double
)
improveCount
/
totalIterations
*
100
:
0
));
if
(
totalIterations
<=
stagnantWindow
)
{
recentImprovements
[
totalIterations
-
1
]
=
improved
?
1
:
0
;
}
// 6. 检查提前停止条件
boolean
shouldStop
=
false
;
String
stopReason
=
""
;
temperature
*=
coolingRate
;
if
(
totalIterations
%
10
==
0
)
{
System
.
gc
();
}
if
(
temperature
<
temperatureThreshold
)
{
shouldStop
=
true
;
stopReason
=
"温度低于阈值"
;
}
else
if
(
noImproveCount
>=
maxNoImprove
)
{
shouldStop
=
true
;
stopReason
=
String
.
format
(
"连续无改进达到上限(%d次)"
,
maxNoImprove
);
}
else
if
(
totalIterations
>=
stagnantWindow
)
{
// 检查改进率是否过低
double
recentImproveRate
=
calculateRecentImprovementRate
(
recentImprovements
,
stagnantWindow
);
if
(
recentImproveRate
<
improvementRateThreshold
)
{
this
.
log
(
String
.
format
(
"模拟退火 - 迭代%d/%d:温度=%.4f, 接受数=%d, 改进数=%d, 无改进连续=%d, 总改进率=%.2f%%"
,
totalIterations
,
maxIterations
,
temperature
,
acceptCount
,
improveCount
,
noImproveCount
,
totalIterations
>
0
?
(
double
)
improveCount
/
(
double
)
totalIterations
*
(
double
)
100.0
F
:
(
double
)
0.0
F
));
boolean
shouldStop
=
false
;
String
stopReason
=
""
;
if
(
temperature
<
temperatureThreshold
)
{
shouldStop
=
true
;
stopReason
=
String
.
format
(
"最近%d次迭代改进率过低(%.2f%%)"
,
stagnantWindow
,
recentImproveRate
*
100
);
stopReason
=
"温度低于阈值"
;
}
else
if
(
noImproveCount
>=
maxNoImprove
)
{
shouldStop
=
true
;
stopReason
=
String
.
format
(
"连续无改进达到上限(%d次)"
,
maxNoImprove
);
}
else
if
(
totalIterations
>=
stagnantWindow
)
{
double
recentImproveRate
=
this
.
calculateRecentImprovementRate
(
recentImprovements
,
stagnantWindow
);
if
(
recentImproveRate
<
improvementRateThreshold
)
{
shouldStop
=
true
;
stopReason
=
String
.
format
(
"最近%d次迭代改进率过低(%.2f%%)"
,
stagnantWindow
,
recentImproveRate
*
(
double
)
100.0
F
);
}
}
}
if
(
shouldStop
)
{
log
(
String
.
format
(
"模拟退火 - 提前停止:%s,迭代%d次,最终温度=%.4f"
,
stopReason
,
totalIterations
,
temperature
)
);
logFinalSummary
(
best
,
initialFitnessLevel
,
initialFitness
,
improveCount
,
significantImproveCount
,
totalIterations
)
;
return
best
;
if
(
shouldStop
)
{
this
.
log
(
String
.
format
(
"模拟退火 - 提前停止:%s,迭代%d次,最终温度=%.4f"
,
stopReason
,
totalIterations
,
temperature
));
this
.
logFinalSummary
(
best
,
initialFitnessLevel
,
initialFitness
,
improveCount
,
significantImproveCount
,
totalIterations
);
return
best
;
}
}
}
log
(
String
.
format
(
"模拟退火 - 完成所有%d次迭代,最终fitness=%.4f"
,
maxIterations
,
best
.
getFitness
()),
true
);
logFinalSummary
(
best
,
initialFitnessLevel
,
initialFitness
,
improveCount
,
significantImproveCount
,
totalIterations
);
// 7. 输出全局最优排产
this
.
log
(
String
.
format
(
"模拟退火 - 完成所有%d次迭代,最终fitness=%.4f"
,
maxIterations
,
best
.
getFitness
()),
true
);
this
.
logFinalSummary
(
best
,
initialFitnessLevel
,
initialFitness
,
improveCount
,
significantImproveCount
,
totalIterations
);
return
best
;
}
/**
* 记录改进详情
*/
private
void
logImprovementDetails
(
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
)
{
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
++)
{
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
));
sb
.
append
(
String
.
format
(
"KPI%d: %.4f→%.4f(+%.4f) "
,
i
+
1
,
initialFitnessLevel
[
i
],
currentFitness
[
i
],
improvement
));
}
}
else
{
sb
.
append
(
"(KPI数据不可用) "
);
...
...
@@ -396,219 +306,191 @@ public class SimulatedAnnealing {
double
totalImprovement
=
best
.
getFitness
()
-
initialFitness
;
sb
.
append
(
String
.
format
(
"总Fitness: %.4f→%.4f(+%.4f)"
,
initialFitness
,
best
.
getFitness
(),
totalImprovement
));
log
(
sb
.
toString
());
this
.
log
(
sb
.
toString
());
}
/**
* 计算最近改进率
*/
private
double
calculateRecentImprovementRate
(
int
[]
recentImprovements
,
int
windowSize
)
{
int
improveCount
=
0
;
for
(
int
i
=
0
;
i
<
windowSize
;
i
++)
{
for
(
int
i
=
0
;
i
<
windowSize
;
++
i
)
{
improveCount
+=
recentImprovements
[
i
];
}
return
(
double
)
improveCount
/
windowSize
;
return
(
double
)
improveCount
/
(
double
)
windowSize
;
}
/**
* 记录最终总结
*/
private
void
logFinalSummary
(
Chromosome
best
,
double
[]
initialFitnessLevel
,
double
initialFitness
,
int
improveCount
,
int
significantImproveCount
,
int
totalIterations
)
{
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
)
{
sb
.
append
(
String
.
format
(
"总迭代%d次, 成功改进%d次(显著%d次), 改进率%.2f%%. "
,
totalIterations
,
improveCount
,
significantImproveCount
,
totalIterations
>
0
?
(
double
)
improveCount
/
(
double
)
totalIterations
*
(
double
)
100.0
F
:
(
double
)
0.0
F
));
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
++)
{
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
));
sb
.
append
(
String
.
format
(
"KPI%d: %.4f→%.4f(%.2f%%) "
,
i
+
1
,
initialFitnessLevel
[
i
],
currentFitness
[
i
],
initialFitnessLevel
[
i
]
>
(
double
)
0.0
F
?
improvement
/
initialFitnessLevel
[
i
]
*
(
double
)
100.0
F
:
(
double
)
0.0
F
));
}
}
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
());
sb
.
append
(
String
.
format
(
"总Fitness: %.4f→%.4f(%.2f%%)"
,
initialFitness
,
best
.
getFitness
(),
initialFitness
>
(
double
)
0.0
F
?
totalImprovement
/
initialFitness
*
(
double
)
100.0
F
:
(
double
)
0.0
F
));
this
.
log
(
sb
.
toString
());
}
private
void
writeKpi
(
Chromosome
chromosome
)
{
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
]
+
","
;
for
(
int
i
=
0
;
i
<
fitness1
.
length
;
++
i
)
{
fitness
=
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
()));
this
.
log
(
String
.
format
(
"模拟退火 - kpi:%s"
,
fitness
),
true
);
if
(
chromosome
.
getMakespan
()
!=
(
double
)
0.0
F
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"模拟退火 - kpi-Makespan: %f"
,
chromosome
.
getMakespan
()));
}
if
(
chromosome
.
getDelayTime
()!=
0
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"模拟退火 - kpi-DelayTime: %f"
,
chromosome
.
getDelayTime
()));
if
(
chromosome
.
getDelayTime
()
!=
(
double
)
0.0
F
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"模拟退火 - kpi-DelayTime: %f"
,
chromosome
.
getDelayTime
()));
}
if
(
chromosome
.
getTotalChangeoverTime
()!=
0
)
{
if
(
chromosome
.
getTotalChangeoverTime
()
!=
(
double
)
0.0
F
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"模拟退火 - kpi-ChangeoverTime: %f"
,
chromosome
.
getTotalChangeoverTime
()));
}
if
(
chromosome
.
getMachineLoadStd
()!=
0
)
{
if
(
chromosome
.
getMachineLoadStd
()
!=
(
double
)
0.0
F
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"模拟退火 - kpi-MachineLoad: %f"
,
chromosome
.
getMachineLoadStd
()));
}
if
(
chromosome
.
getTotalFlowTime
()!=
0
)
{
if
(
chromosome
.
getTotalFlowTime
()
!=
(
double
)
0.0
F
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"模拟退火 - kpi-FlowTime: %f"
,
chromosome
.
getTotalFlowTime
()));
}
}
/**
* 计算能量差(基于fitnessLevel数组的比较)
*/
private
double
calculateEnergyDifference
(
Chromosome
neighbor
,
Chromosome
current
)
{
double
[]
neighborFitness
=
neighbor
.
getFitnessLevel
();
double
[]
currentFitness
=
current
.
getFitnessLevel
();
double
diff
=
(
double
)
0.0
F
;
// 计算加权能量差
double
diff
=
0
;
for
(
int
i
=
0
;
i
<
neighborFitness
.
length
;
i
++)
{
diff
+=
(
neighborFitness
[
i
]
-
currentFitness
[
i
]);
for
(
int
i
=
0
;
i
<
neighborFitness
.
length
;
++
i
)
{
diff
+=
neighborFitness
[
i
]
-
currentFitness
[
i
];
}
return
diff
;
}
/**
* 按优先级分组工序
*/
private
Map
<
Double
,
List
<
Entry
>>
groupOperationsByPriority
()
{
Map
<
Double
,
List
<
Entry
>>
groups
=
new
HashMap
<>();
for
(
Entry
op
:
allOperations
)
{
Map
<
Double
,
List
<
Entry
>>
groups
=
new
HashMap
();
for
(
Entry
op
:
this
.
allOperations
)
{
double
priority
=
op
.
getPriority
();
groups
.
computeIfAbsent
(
priority
,
k
->
new
ArrayList
<>(
)).
add
(
op
);
((
List
)
groups
.
computeIfAbsent
(
priority
,
(
k
)
->
new
ArrayList
()
)).
add
(
op
);
}
// 过滤掉:设备只有一个且只有一个GroupId的优先级组
Map
<
Double
,
List
<
Entry
>>
filteredGroups
=
new
HashMap
<>();
for
(
Map
.
Entry
<
Double
,
List
<
Entry
>>
entry
:
groups
.
entrySet
())
{
List
<
Entry
>
ops
=
entry
.
getValue
();
// 检查是否所有工序都只有一个设备选项
boolean
allSingleMachine
=
ops
.
stream
()
.
allMatch
(
op
->
op
.
getMachineOptions
().
size
()
<=
1
);
Map
<
Double
,
List
<
Entry
>>
filteredGroups
=
new
HashMap
();
// 检查是否只有一个GroupId
Set
<
Integer
>
groupIds
=
ops
.
stream
()
.
map
(
Entry:
:
getGroupId
)
.
collect
(
Collectors
.
toSet
());
// 如果两个条件都满足,过滤掉这个优先级组
if
(!(
allSingleMachine
&&
groupIds
.
size
()
<=
1
))
{
filteredGroups
.
put
(
entry
.
getKey
(),
ops
);
for
(
Map
.
Entry
<
Double
,
List
<
Entry
>>
entry
:
groups
.
entrySet
())
{
List
<
Entry
>
ops
=
(
List
)
entry
.
getValue
();
boolean
allSingleMachine
=
ops
.
stream
().
allMatch
((
opx
)
->
opx
.
getMachineOptions
().
size
()
<=
1
);
Set
<
Integer
>
groupIds
=
(
Set
)
ops
.
stream
().
map
(
Entry:
:
getGroupId
).
collect
(
Collectors
.
toSet
());
if
(!
allSingleMachine
||
groupIds
.
size
()
>
1
)
{
filteredGroups
.
put
((
Double
)
entry
.
getKey
(),
ops
);
}
}
return
filteredGroups
;
}
/**
* 构建Entry索引:op.getGroupId() + "_" + op.getSequence() -> Entry
*/
private
Map
<
Integer
,
Object
>
buildEntryKey
()
{
Map
<
Integer
,
Object
>
index0
=
new
HashMap
<>();
Map
<
String
,
Entry
>
index
=
new
HashMap
<>();
Map
<
Integer
,
Entry
>
index2
=
new
HashMap
<>();
List
<
Entry
>
allOps
=
this
.
allOperations
;
Map
<
Integer
,
Object
>
index0
=
new
HashMap
();
Map
<
String
,
Entry
>
index
=
new
HashMap
();
Map
<
Integer
,
Entry
>
index2
=
new
HashMap
();
for
(
Entry
op
:
allOp
s
)
{
for
(
Entry
op
:
this
.
allOperation
s
)
{
String
key
=
op
.
getGroupId
()
+
"_"
+
op
.
getSequence
();
index
.
put
(
key
,
op
);
index2
.
put
(
op
.
getId
(),
op
);
}
index0
.
put
(
1
,
index
);
index0
.
put
(
2
,
index2
);
index0
.
put
(
1
,
index
);
index0
.
put
(
2
,
index2
);
return
index0
;
}
/**
* 解码染色体
*/
/**
* 解码染色体
*/
private
void
decode
(
GeneticDecoder
decoder
,
Chromosome
chromosome
,
List
<
Machine
>
machines
)
{
chromosome
.
setResult
(
new
CopyOnWriteArrayList
<>());
// 假设Machine类有拷贝方法,或使用MapStruct等工具进行映射
chromosome
.
setMachines
(
ProductionDeepCopyUtil
.
deepCopyList
(
machines
,
Machine
.
class
));
// 简单拷贝,实际可能需要深拷贝
chromosome
.
setOrders
(
ProductionDeepCopyUtil
.
deepCopyList
(
new
CopyOnWriteArrayList
<>(
orders
),
Order
.
class
)
);
// 简单拷贝,实际可能需要深拷贝
chromosome
.
setOperatRel
(
ProductionDeepCopyUtil
.
deepCopyList
(
new
CopyOnWriteArrayList
<>(
_entryRel
),
GroupResult
.
class
)
);
// 简单拷贝,实际可能需要深拷贝
chromosome
.
setMaterials
(
ProductionDeepCopyUtil
.
deepCopyTreeMap
(
materials
,
String
.
class
,
Material
.
class
));
// 简单拷贝,实际可能需要深拷贝
chromosome
.
setAllOperations
(
ProductionDeepCopyUtil
.
deepCopyList
(
new
CopyOnWriteArrayList
<>(
allOperations
),
Entry
.
class
)
);
// 简单拷贝,实际可能需要深拷贝
//chromosome.setObjectiveWeights(_objectiveWeights);
// chromosome.setBaseTime(param.getBaseTime());
// chromosome.setInitMachines(ProductionDeepCopyUtil.deepCopyList(machines,Machine.class)); // 简单拷贝,实际可能需要深拷贝
// _sceneService.saveChromosomeToFile(chromosome, "12345679");
// 加载锁定工单到ResultOld
List
<
GAScheduleResult
>
lockedOrders
=
GlobalCacheUtil
.
get
(
"locked_orders_"
+
chromosome
.
getScenarioID
());
private
void
decode
(
GeneticDecoder
decoder
,
Chromosome
chromosome
,
List
<
Machine
>
machines
)
{
chromosome
.
setResult
(
new
CopyOnWriteArrayList
());
if
(
this
.
cachedMachines
==
null
)
{
this
.
cachedMachines
=
ProductionDeepCopyUtil
.
deepCopyList
(
machines
,
Machine
.
class
);
}
chromosome
.
setMachines
(
ProductionDeepCopyUtil
.
deepCopyList
(
this
.
cachedMachines
,
Machine
.
class
));
chromosome
.
setOrders
(
ProductionDeepCopyUtil
.
deepCopyList
(
new
CopyOnWriteArrayList
(
this
.
cachedOrders
),
Order
.
class
));
chromosome
.
setOperatRel
(
ProductionDeepCopyUtil
.
deepCopyList
(
new
CopyOnWriteArrayList
(
this
.
cachedEntryRel
),
GroupResult
.
class
));
chromosome
.
setMaterials
(
ProductionDeepCopyUtil
.
deepCopyTreeMap
(
this
.
cachedMaterials
,
String
.
class
,
Material
.
class
));
chromosome
.
setAllOperations
(
ProductionDeepCopyUtil
.
deepCopyList
(
new
CopyOnWriteArrayList
(
this
.
cachedAllOperations
),
Entry
.
class
));
List
<
GAScheduleResult
>
lockedOrders
=
(
List
)
GlobalCacheUtil
.
get
(
"locked_orders_"
+
chromosome
.
getScenarioID
());
if
(
lockedOrders
!=
null
&&
!
lockedOrders
.
isEmpty
())
{
chromosome
.
setResultOld
(
ProductionDeepCopyUtil
.
deepCopyList
(
lockedOrders
,
GAScheduleResult
.
class
));
log
(
"将 "
+
lockedOrders
.
size
()
+
" 个锁定工单加载到初始种群中"
);
this
.
log
(
"将 "
+
lockedOrders
.
size
()
+
" 个锁定工单加载到初始种群中"
);
}
else
{
chromosome
.
setResultOld
(
new
CopyOnWriteArrayList
<>
());
chromosome
.
setResultOld
(
new
CopyOnWriteArrayList
());
}
decoder
.
decodeChromosomeWithCache
(
chromosome
,
false
);
decoder
.
decodeChromosomeWithCache
(
chromosome
,
false
);
}
private
Chromosome
lightCopy
(
Chromosome
source
)
{
Chromosome
copy
=
new
Chromosome
();
copy
.
setOperationSequencing
(
new
CopyOnWriteArrayList
(
source
.
getOperationSequencing
()));
copy
.
setMachineSelection
(
new
CopyOnWriteArrayList
(
source
.
getMachineSelection
()));
copy
.
setGlobalOpList
(
new
CopyOnWriteArrayList
(
source
.
getGlobalOpList
()));
copy
.
setOrders
(
new
CopyOnWriteArrayList
(
source
.
getOrders
()));
copy
.
setAllOperations
(
new
CopyOnWriteArrayList
(
source
.
getAllOperations
()));
copy
.
setResult
(
source
.
getResult
());
copy
.
setMachines
(
source
.
getMachines
());
copy
.
setOperatRel
(
new
CopyOnWriteArrayList
(
source
.
getOperatRel
()));
copy
.
setScenarioID
(
source
.
getScenarioID
());
copy
.
setBaseTime
(
source
.
getBaseTime
());
copy
.
setGenerateType
(
source
.
getGenerateType
());
copy
.
setFitnessLevel
(
source
.
getFitnessLevel
());
copy
.
setFitness
(
source
.
getFitness
());
return
copy
;
}
/**
* 比较两个染色体的优劣(基于fitnessLevel多层次比较)
*/
private
boolean
isBetter
(
Chromosome
c1
,
Chromosome
c2
)
{
return
fitnessCalculator
.
isBetter
(
c1
,
c2
);
return
this
.
fitnessCalculator
.
isBetter
(
c1
,
c2
);
}
/**
* 判断是否为显著改进(只有超过阈值的改进才重置无改进计数)
*/
private
boolean
isSignificantImprovement
(
Chromosome
newChromo
,
Chromosome
oldChromo
)
{
if
(!
isBetter
(
newChromo
,
oldChromo
))
{
if
(!
this
.
isBetter
(
newChromo
,
oldChromo
))
{
return
false
;
}
else
{
double
newFitness
=
newChromo
.
getFitness
();
double
oldFitness
=
oldChromo
.
getFitness
();
return
newFitness
-
oldFitness
>
1.0
E
-
4
;
}
double
newFitness
=
newChromo
.
getFitness
();
double
oldFitness
=
oldChromo
.
getFitness
();
return
(
newFitness
-
oldFitness
)
>
SIGNIFICANT_IMPROVEMENT_THRESHOLD
;
}
private
int
Getbest
(
List
<
Chromosome
>
candidates
,
Chromosome
best
)
{
// 找出最佳候选方案
private
int
Getbest
(
List
<
Chromosome
>
candidates
,
Chromosome
best
)
{
int
bestidx
=
-
1
;
if
(
best
==
null
)
{
best
=
candidates
.
get
(
0
);
bestidx
=
0
;
if
(
best
==
null
)
{
best
=
(
Chromosome
)
candidates
.
get
(
0
);
bestidx
=
0
;
}
for
(
int
i
=
0
;
i
<
candidates
.
size
();
i
++)
{
Chromosome
candidate
=
candidates
.
get
(
i
);
if
(
isBetter
(
candidate
,
best
))
{
for
(
int
i
=
0
;
i
<
candidates
.
size
();
++
i
)
{
Chromosome
candidate
=
(
Chromosome
)
candidates
.
get
(
i
);
if
(
this
.
isBetter
(
candidate
,
best
))
{
bestidx
=
i
;
}
}
return
bestidx
;
}
}
\ No newline at end of file
}
src/main/java/com/aps/service/Algorithm/VariableNeighborhoodSearch.java
View file @
41cab0d7
...
...
@@ -71,32 +71,8 @@ public class VariableNeighborhoodSearch {
private
static
final
double
DIVERSITY_WEIGHT
=
0.4
;
// 设备选择多样性权重(越高越倾向选择次数少的设备)
private
static
final
double
RANDOM_NOISE_FOR_MACHINE
=
0.1
;
// 机器选择的随机扰动因子
// 日志级别
private
static
final
int
LOG_LEVEL_DEBUG
=
0
;
private
static
final
int
LOG_LEVEL_INFO
=
1
;
private
static
final
int
LOG_LEVEL_WARN
=
2
;
private
int
currentLogLevel
=
LOG_LEVEL_INFO
;
// 局部搜索优化
private
static
final
int
MAX_LOCAL_SEARCH_NEIGHBORS
=
2
;
// 从5减少到2,大幅减少解码次数
private
void
log
(
String
message
)
{
log
(
message
,
LOG_LEVEL_INFO
,
false
);
}
private
void
log
(
String
message
,
boolean
enableLogging
)
{
log
(
message
,
LOG_LEVEL_INFO
,
enableLogging
);
}
private
void
log
(
String
message
,
int
level
)
{
log
(
message
,
level
,
false
);
}
private
void
log
(
String
message
,
int
level
,
boolean
enableLogging
)
{
if
(
enableLogging
&&
level
>=
currentLogLevel
)
{
FileHelper
.
writeLogFile
(
message
);
}
}
private
List
<
Entry
>
allOperations
;
...
...
@@ -140,6 +116,23 @@ public class VariableNeighborhoodSearch {
private
TreeMap
<
String
,
Material
>
cachedMaterials
;
private
List
<
Entry
>
cachedAllOperations
;
// CP-SAT 邻域需要的缓存
private
GeneticDecoder
cpSatDecoder
;
private
List
<
Machine
>
cpSatMachines
;
private
ObjectiveWeights
cpSatWeights
;
private
boolean
cpSatEnabled
=
false
;
/**
* 启用 CP-SAT 邻域
*/
public
void
enableCpSatNeighborhood
(
GeneticDecoder
decoder
,
List
<
Machine
>
machines
,
ObjectiveWeights
weights
)
{
this
.
cpSatDecoder
=
decoder
;
this
.
cpSatMachines
=
machines
;
this
.
cpSatWeights
=
weights
;
this
.
cpSatEnabled
=
true
;
FileHelper
.
writeLogFile
(
"[VNS-CpSat] CP-SAT 邻域已启用"
);
}
private
GeneticOperations
geneticOperations
;
// 邻域结构成功率统计(用于 search() 方法)
...
...
@@ -261,26 +254,41 @@ public class VariableNeighborhoodSearch {
// HybridShake - 混合抖动:综合调整
neighborhoods
.
add
(
new
NeighborhoodStructure
(
"HybridShake"
,
this
::
hybridShakeWrapper
));
// CpSatLocalOptimize - CP-SAT 局部重优化(仅当启用时)
neighborhoods
.
add
(
new
NeighborhoodStructure
(
"CpSatLocalOptimize"
,
this
::
cpSatLocalOptimizeWrapper
));
return
neighborhoods
;
}
/**
* 定义邻域结构(按成功率排序)
* 定义邻域结构(按成功率排序
,CP-SAT 始终保留一个名额
)
*/
private
List
<
NeighborhoodStructure
>
defineNeighborhoods
()
{
// 按成功率排序
List
<
NeighborhoodWithStats
>
sorted
=
new
ArrayList
<>(
neighborhoodsWithStats
);
sorted
.
sort
((
a
,
b
)
->
Double
.
compare
(
b
.
getSuccessRate
(),
a
.
getSuccessRate
()));
// 提取 NeighborhoodStructure
List
<
NeighborhoodStructure
>
result
=
new
ArrayList
<>();
// for (NeighborhoodWithStats ns : sorted) {
// result.add(ns.structure);
// }
// 始终保留 CP-SAT 邻域(避免因成功率低被挤出前 N 名后永远无法被选中)
NeighborhoodWithStats
cpSatNs
=
null
;
int
maxNeighborhoods
=
Math
.
min
(
MAX_NEIGHBORHOODS
,
sorted
.
size
());
for
(
int
i
=
0
;
i
<
maxNeighborhoods
;
i
++)
{
result
.
add
(
sorted
.
get
(
i
).
structure
);
NeighborhoodWithStats
ns
=
sorted
.
get
(
i
);
if
(
"CpSatLocalOptimize"
.
equals
(
ns
.
structure
.
name
))
{
cpSatNs
=
ns
;
}
result
.
add
(
ns
.
structure
);
}
// 如果 CP-SAT 未被前 N 名选中且已启用,额外追加
if
(
cpSatNs
==
null
&&
cpSatEnabled
)
{
for
(
NeighborhoodWithStats
ns
:
neighborhoodsWithStats
)
{
if
(
"CpSatLocalOptimize"
.
equals
(
ns
.
structure
.
name
))
{
result
.
add
(
ns
.
structure
);
break
;
}
}
}
return
result
;
...
...
@@ -304,7 +312,7 @@ public class VariableNeighborhoodSearch {
/**
* 对种群中的每个个体进行变邻域搜索
*/
public
List
<
Chromosome
>
search
(
List
<
Chromosome
>
population
,
TabuSearch
tabuSearch
,
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
)
{
...
...
@@ -339,8 +347,9 @@ public class VariableNeighborhoodSearch {
log
(
"变邻域搜索(共用禁忌表) - 开始执行"
,
true
);
// 深拷贝当前染色体
Chromosome
current
=
ProductionDeepCopyUtil
.
deepCopy
(
chromosome
,
Chromosome
.
class
);
Chromosome
best
=
ProductionDeepCopyUtil
.
deepCopy
(
chromosome
,
Chromosome
.
class
);
Chromosome
current
=
lightCopy
(
chromosome
);
Chromosome
best
=
copyChromosome
(
chromosome
);
writeKpi
(
best
);
// 记录初始KPI用于跟踪改进
...
...
@@ -421,9 +430,9 @@ public class VariableNeighborhoodSearch {
}
if
(
accept
)
{
current
=
ProductionDeepCopyUtil
.
deepCopy
(
localBest
,
Chromosome
.
class
);
current
=
lightCopy
(
localBest
);
if
(
betterThanBest
)
{
best
=
ProductionDeepCopyUtil
.
deepCopy
(
localBest
,
Chromosome
.
class
);
best
=
lightCopy
(
localBest
);
writeKpi
(
best
);
totalImprovements
++;
roundHadImprovement
=
true
;
...
...
@@ -535,6 +544,8 @@ public class VariableNeighborhoodSearch {
logVNSFinalSummary
(
best
,
initialFitnessLevel
,
initialFitness
,
totalRounds
,
totalImprovements
,
totalSignificantImprovements
);
log
(
String
.
format
(
"变邻域搜索(融合禁忌) - 结束, 总轮次=%d"
,
totalRounds
),
true
);
decode
(
decoder
,
best
,
machines
);
return
best
;
}
...
...
@@ -616,21 +627,25 @@ public class VariableNeighborhoodSearch {
log
(
String
.
format
(
"变邻域搜索 - kpi:%s"
,
fitness
),
true
);
if
(
chromosome
.
getMakespan
()!=
0
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"变邻域搜索 - kpi-Makespan: %f"
,
chromosome
.
getMakespan
()));
log
(
String
.
format
(
"变邻域搜索 - kpi-Makespan: %f"
,
chromosome
.
getMakespan
()));
}
if
(
chromosome
.
getDelayTime
()!=
0
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"变邻域搜索 - kpi-DelayTime: %f"
,
chromosome
.
getDelayTime
()));
log
(
String
.
format
(
"变邻域搜索 - kpi-DelayTime: %f"
,
chromosome
.
getDelayTime
()));
}
if
(
chromosome
.
getTotalChangeoverTime
()!=
0
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"变邻域搜索 - kpi-ChangeoverTime: %f"
,
chromosome
.
getTotalChangeoverTime
()));
log
(
String
.
format
(
"变邻域搜索 - kpi-ChangeoverTime: %f"
,
chromosome
.
getTotalChangeoverTime
()));
}
if
(
chromosome
.
getMachineLoadStd
()!=
0
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"变邻域搜索 - kpi-MachineLoad: %f"
,
chromosome
.
getMachineLoadStd
()));
log
(
String
.
format
(
"变邻域搜索 - kpi-MachineLoad: %f"
,
chromosome
.
getMachineLoadStd
()));
}
if
(
chromosome
.
getTotalFlowTime
()!=
0
)
{
FileHelper
.
writeLogFile
(
String
.
format
(
"变邻域搜索 - kpi-FlowTime: %f"
,
chromosome
.
getTotalFlowTime
()));
log
(
String
.
format
(
"变邻域搜索 - kpi-FlowTime: %f"
,
chromosome
.
getTotalFlowTime
()));
}
// ==================== 打印各 KPI 的 Gap ====================
log
(
KpiLowerBoundCalculator
.
generateGapReport
(
chromosome
));
}
/**
...
...
@@ -2125,12 +2140,23 @@ public class VariableNeighborhoodSearch {
Chromosome
neighbor
=
new
Chromosome
();
neighbor
.
setGenerateType
(
chromosome
.
getGenerateType
());
neighbor
.
setID
(
UUID
.
randomUUID
().
toString
());
neighbor
.
setOperationSequencing
(
chromosome
.
getOperationSequencing
());
neighbor
.
setMachineSelection
(
chromosome
.
getMachineSelection
());
neighbor
.
setOperationSequencing
(
new
CopyOnWriteArrayList
<>(
chromosome
.
getOperationSequencing
()));
neighbor
.
setMachineSelection
(
new
CopyOnWriteArrayList
<>(
chromosome
.
getMachineSelection
()));
neighbor
.
setGlobalOpList
(
new
CopyOnWriteArrayList
<>(
chromosome
.
getGlobalOpList
()));
neighbor
.
setScenarioID
(
chromosome
.
getScenarioID
());
neighbor
.
setBaseTime
(
chromosome
.
getBaseTime
());
neighbor
.
setFitnessLevel
(
chromosome
.
getFitnessLevel
());
neighbor
.
setGlobalOpList
(
chromosome
.
getGlobalOpList
());
// 拷贝 orders/allOperations/operatRel,确保 DelOrder 能正常清理 SF 工序
if
(
chromosome
.
getOrders
()
!=
null
)
{
neighbor
.
setOrders
(
new
CopyOnWriteArrayList
<>(
chromosome
.
getOrders
()));
}
if
(
chromosome
.
getAllOperations
()
!=
null
)
{
neighbor
.
setAllOperations
(
new
CopyOnWriteArrayList
<>(
chromosome
.
getAllOperations
()));
}
if
(
chromosome
.
getOperatRel
()
!=
null
)
{
neighbor
.
setOperatRel
(
new
CopyOnWriteArrayList
<>(
chromosome
.
getOperatRel
()));
}
return
neighbor
;
}
...
...
@@ -2380,25 +2406,36 @@ public class VariableNeighborhoodSearch {
CopyOnWriteArrayList
<
Integer
>
os
=
neighbor
.
getOperationSequencing
();
CopyOnWriteArrayList
<
Integer
>
ms
=
neighbor
.
getMachineSelection
();
log
(
String
.
format
(
"generateSameMachineSwapNeighbor: os.size=%d, ms.size=%d, idx1=%d, globalOpList.size=%d"
,
os
.
size
(),
ms
.
size
(),
idx1
,
neighbor
.
getGlobalOpList
()
!=
null
?
neighbor
.
getGlobalOpList
().
size
()
:
0
));
if
(
os
.
size
()
<
2
||
ms
.
size
()
<
2
)
{
log
(
"generateSameMachineSwapNeighbor: os或ms太小,返回"
);
return
neighbor
;
}
// ========== 修复1: op1 必须非 null ==========
Entry
op1
=
positionIndex
.
get
(
idx1
);
if
(
op1
==
null
)
{
log
(
String
.
format
(
"generateSameMachineSwapNeighbor: positionIndex中找不到idx1=%d"
,
idx1
));
return
neighbor
;
}
log
(
String
.
format
(
"generateSameMachineSwapNeighbor: op1=订单%d工序%d, machineOptions.size=%d"
,
op1
.
getGroupId
(),
op1
.
getSequence
(),
op1
.
getMachineOptions
().
size
()));
// ========== 修复2: machinePositionIndex 查找必须非 null 且有效 ==========
String
op1Key
=
op1
.
getGroupId
()
+
"_"
+
op1
.
getSequence
();
Integer
maPos1
=
machinePositionIndex
.
get
(
op1Key
);
if
(
maPos1
==
null
||
maPos1
<
0
||
maPos1
>=
ms
.
size
())
{
log
(
String
.
format
(
"generateSameMachineSwapNeighbor: maPos1=%s无效 (ms.size=%d)"
,
maPos1
,
ms
.
size
()));
return
neighbor
;
}
int
machineSeq1
=
ms
.
get
(
maPos1
);
if
(
machineSeq1
<
1
||
machineSeq1
>
op1
.
getMachineOptions
().
size
())
{
log
(
String
.
format
(
"generateSameMachineSwapNeighbor: machineSeq1=%d超出范围[1-%d]"
,
machineSeq1
,
op1
.
getMachineOptions
().
size
()));
return
neighbor
;
}
...
...
@@ -2444,6 +2481,11 @@ public class VariableNeighborhoodSearch {
Collections
.
swap
(
os
,
idx1
,
idx2
);
neighbor
.
setOperationSequencing
(
os
);
log
(
String
.
format
(
"generateSameMachineSwapNeighbor: swap完成 idx1=%d(订单%d工序%d) <-> idx2=%d(订单%d工序%d), 同机器候选数=%d"
,
idx1
,
op1
.
getGroupId
(),
op1
.
getSequence
(),
idx2
,
positionIndex
.
get
(
idx2
).
getGroupId
(),
positionIndex
.
get
(
idx2
).
getSequence
(),
sameMachineOsPositions
.
size
()));
return
neighbor
;
}
...
...
@@ -2512,8 +2554,8 @@ public class VariableNeighborhoodSearch {
Chromosome
best
=
copyChromosome
(
chromosome
);
decode
(
decoder
,
best
,
machines
);
Chromosome
current
=
ProductionDeepCopyUtil
.
deepCopy
(
best
,
Chromosome
.
class
);
geneticOperations
.
DelOrder
(
current
);
Chromosome
current
=
lightCopy
(
best
);
writeKpi
(
best
);
// 预定义邻域结构,避免每次循环重复创建
List
<
NeighborhoodStructure
>
neighborhoods
=
defineNeighborhoods
();
...
...
@@ -2558,6 +2600,29 @@ public class VariableNeighborhoodSearch {
* 解码染色体
*/
private
void
decode
(
GeneticDecoder
decoder
,
Chromosome
chromosome
,
List
<
Machine
>
machines
)
{
// MS 校验:解码前检查 machineSelection 与 machineOptions 是否匹配
List
<
GlobalOperationInfo
>
gops
=
chromosome
.
getGlobalOpList
();
List
<
Integer
>
msCheck
=
chromosome
.
getMachineSelection
();
if
(
gops
!=
null
&&
msCheck
!=
null
)
{
int
msErrors
=
0
;
StringBuilder
sb
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
Math
.
min
(
gops
.
size
(),
msCheck
.
size
());
i
++)
{
Entry
op
=
gops
.
get
(
i
).
getOp
();
int
msVal
=
msCheck
.
get
(
i
);
if
(
op
!=
null
&&
op
.
getMachineOptions
()
!=
null
&&
(
msVal
<
1
||
msVal
>
op
.
getMachineOptions
().
size
()))
{
msErrors
++;
if
(
msErrors
<=
3
)
{
sb
.
append
(
String
.
format
(
" [idx=%d 订单%d工序%d ms=%d range=1-%d]"
,
i
,
op
.
getGroupId
(),
op
.
getSequence
(),
msVal
,
op
.
getMachineOptions
().
size
()));
}
}
}
if
(
msErrors
>
0
)
{
log
(
String
.
format
(
"decode-MS校验失败: 共%d处越界 %s"
,
msErrors
,
sb
.
toString
()));
}
}
chromosome
.
setResult
(
new
CopyOnWriteArrayList
<>());
// 缓存 Machine 列表(第一次调用时缓存)
...
...
@@ -2724,7 +2789,28 @@ public class VariableNeighborhoodSearch {
}
return
index
;
}
/**
* 轻量拷贝:只复制 generateNeighbor/DelOrder 需要的字段,避免全量 JSON 深拷贝导致 OOM。
* result/machines/operatRel 等重型数据共享引用(generateNeighbor 只读,不修改)。
*/
private
Chromosome
lightCopy
(
Chromosome
source
)
{
Chromosome
copy
=
new
Chromosome
();
copy
.
setOperationSequencing
(
new
CopyOnWriteArrayList
<>(
source
.
getOperationSequencing
()));
copy
.
setMachineSelection
(
new
CopyOnWriteArrayList
<>(
source
.
getMachineSelection
()));
copy
.
setGlobalOpList
(
new
CopyOnWriteArrayList
<>(
source
.
getGlobalOpList
()));
copy
.
setOrders
(
new
CopyOnWriteArrayList
<>(
source
.
getOrders
()));
copy
.
setAllOperations
(
new
CopyOnWriteArrayList
<>(
source
.
getAllOperations
()));
copy
.
setResult
(
source
.
getResult
());
copy
.
setMachines
(
source
.
getMachines
());
copy
.
setOperatRel
(
new
CopyOnWriteArrayList
<>(
source
.
getOperatRel
()));
copy
.
setScenarioID
(
source
.
getScenarioID
());
copy
.
setBaseTime
(
source
.
getBaseTime
());
copy
.
setGenerateType
(
source
.
getGenerateType
());
copy
.
setFitnessLevel
(
source
.
getFitnessLevel
());
copy
.
setFitness
(
source
.
getFitness
());
geneticOperations
.
DelOrder
(
copy
);
return
copy
;
}
/**
* 构建位置->Entry索引
*/
...
...
@@ -3675,10 +3761,10 @@ public class VariableNeighborhoodSearch {
// 随机选择一个不同于当前的机器
int
newSelection
;
if
(
options
.
size
()
==
2
)
{
newSelection
=
(
currentSelection
==
1
)
?
0
:
1
;
newSelection
=
(
currentSelection
==
1
)
?
2
:
1
;
}
else
{
do
{
newSelection
=
rnd
.
nextInt
(
options
.
size
());
newSelection
=
rnd
.
nextInt
(
options
.
size
())
+
1
;
}
while
(
newSelection
==
currentSelection
);
}
...
...
@@ -3736,4 +3822,43 @@ public class VariableNeighborhoodSearch {
log
(
"HybridShake: 混合抖动完成"
);
return
neighbor
;
}
/**
* CP-SAT 局部重优化邻域
* 在当前调度的基础上,释放一部分工序,用 CP-SAT 重优化机器选择
*/
private
Chromosome
cpSatLocalOptimizeWrapper
(
Chromosome
chromosome
)
{
if
(!
cpSatEnabled
||
cpSatDecoder
==
null
||
cpSatMachines
==
null
)
{
return
null
;
}
try
{
// CP-SAT 需要解码后的时间信息,先做一次解码
cpSatDecoder
.
serialDecode
(
chromosome
);
// 只做一次快速重优化(释放约 10% 的工序)
CpSatLnsNeighborhood
lns
=
new
CpSatLnsNeighborhood
(
cachedAllOperations
,
cpSatMachines
,
orders
,
materials
,
_entryRel
,
fitnessCalculator
);
int
releaseCount
=
Math
.
max
(
30
,
(
int
)(
cachedAllOperations
.
size
()
*
0.10
));
int
timeLimitSec
=
Math
.
min
(
8
,
12000
/
Math
.
max
(
100
,
cachedAllOperations
.
size
()));
Chromosome
neighbor
=
lns
.
optimizeNeighborhood
(
chromosome
,
releaseCount
,
timeLimitSec
);
if
(
neighbor
!=
null
)
{
log
(
"CpSatLocalOptimize: 生成邻居成功"
);
}
return
neighbor
;
}
catch
(
Exception
e
)
{
log
(
"CpSatLocalOptimize: 异常 - "
+
e
.
getMessage
());
return
null
;
}
}
public
static
void
log
(
String
message
)
{
log
(
message
,
true
);
}
public
static
void
log
(
String
message
,
boolean
enableLogging
)
{
FileHelper
.
log
(
message
,
enableLogging
);
}
}
\ No newline at end of file
src/test/java/com/aps/demo/PlanResultServiceTest.java
View file @
41cab0d7
...
...
@@ -43,8 +43,8 @@ public class PlanResultServiceTest {
// planResultService.execute2("64E64F6B68094AF38CEDC418630C3CC2");//2000
// planResultService.execute2("E1448B3C9C8743DEAB39708F2CFE348A");//倒排bomces
// planResultService.execute2("197083D0D26A449EB179AC103C753FD3
");
planResultService
.
execute2
(
"F8F147BD627C47B1A190399DD7A697F6"
);
planResultService
.
execute2
(
"85DA28EC5F4643449E65A51253D5F127
"
);
//
planResultService.execute2("F8F147BD627C47B1A190399DD7A697F6");
// planResultService.execute2("9FEDFD92BB6A4675BF9B1CC64505D1AB");
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment