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