mirror of
https://codeup.aliyun.com/64f7d6b8ce01efaafef1e678/coal/coal.git
synced 2026-07-25 23:27:07 +08:00
feat(RawCoalBlendCalculator): enhance optimal solution finding and hinting mechanism
This commit is contained in:
@@ -64,6 +64,38 @@ public class RawCoalBlendCalculator { /**
|
||||
// 添加参数约束
|
||||
addParameterConstraints(model, coalPercents, coals, constrains, paramFlags);
|
||||
|
||||
// 第一步:求出最优解(coalPercents之和的最大值)
|
||||
CoalBlendResultInfoEntity optimalResult = findOptimalSolution(model, coalPercents, coals, paramFlags, maxTime);
|
||||
if (optimalResult != null) {
|
||||
results.add(optimalResult);
|
||||
log.info("找到最优解的总比例: {}", optimalResult.getPercentSum());
|
||||
}
|
||||
|
||||
// 如果已经达到要求的数量,直接返回
|
||||
if (results.size() >= count) {
|
||||
results.sort((r1, r2) -> Double.compare(r2.getPercentSum(), r1.getPercentSum()));
|
||||
return results;
|
||||
}
|
||||
|
||||
// 重新创建模型来求解所有解
|
||||
CpModel newModel = new CpModel();
|
||||
List<IntVar> newCoalPercents = new ArrayList<>();
|
||||
for (int i = 0; i < coals.size(); i++) {
|
||||
CoalBlendCoalInfoEntity coal = coals.get(i);
|
||||
int maxPercent = (int) (coal.getYmPercent() * 100);
|
||||
newCoalPercents.add(newModel.newIntVar(0, maxPercent, "coal_" + i));
|
||||
}
|
||||
|
||||
// 重新添加约束
|
||||
newModel.addGreaterThan(LinearExpr.sum(newCoalPercents.toArray(new IntVar[0])), 0);
|
||||
addSequentialConstraints(newModel, newCoalPercents, coals);
|
||||
addParameterConstraints(newModel, newCoalPercents, coals, constrains, paramFlags);
|
||||
|
||||
// 将最优解作为hint来指导求解
|
||||
if (optimalResult != null) {
|
||||
addOptimalSolutionAsHint(newModel, newCoalPercents, optimalResult, coals);
|
||||
}
|
||||
|
||||
// 创建求解器
|
||||
CpSolver solver = new CpSolver();
|
||||
SatParameters.Builder parameters = solver.getParameters();
|
||||
@@ -72,17 +104,21 @@ public class RawCoalBlendCalculator { /**
|
||||
parameters.setLogSearchProgress(false);
|
||||
|
||||
HashSet<String> seen = new HashSet<>();
|
||||
// 将最优解的key加入seen集合,避免重复
|
||||
if (optimalResult != null) {
|
||||
seen.add(generateSolutionKey(optimalResult));
|
||||
}
|
||||
|
||||
// 求解
|
||||
solver.solve(model, new CpSolverSolutionCallback() {
|
||||
solver.solve(newModel, new CpSolverSolutionCallback() {
|
||||
@Override
|
||||
public void onSolutionCallback() {
|
||||
// 检查解的有效性
|
||||
if (!isValidSolution(this, coalPercents)) {
|
||||
if (!isValidSolution(this, newCoalPercents)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CoalBlendResultInfoEntity result = createResult(this, coalPercents, coals, paramFlags);
|
||||
CoalBlendResultInfoEntity result = createResult(this, newCoalPercents, coals, paramFlags);
|
||||
|
||||
// 去重
|
||||
String key = generateSolutionKey(result);
|
||||
@@ -103,7 +139,123 @@ public class RawCoalBlendCalculator { /**
|
||||
log.error("原煤配煤计算失败", e);
|
||||
}
|
||||
|
||||
// 按照coalPercents之和从大到小排序,最优解在最前面
|
||||
results.sort((r1, r2) -> Double.compare(r2.getPercentSum(), r1.getPercentSum()));
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 求出最优解(coalPercents之和最大的完整解)
|
||||
*/
|
||||
private static CoalBlendResultInfoEntity findOptimalSolution(CpModel model, List<IntVar> coalPercents,
|
||||
List<CoalBlendCoalInfoEntity> coals, AtomicBoolean[] paramFlags, Integer maxTime) {
|
||||
try {
|
||||
// 创建总和变量
|
||||
IntVar totalPercent = model.newIntVar(0, Integer.MAX_VALUE, "total_percent");
|
||||
model.addEquality(totalPercent, LinearExpr.sum(coalPercents.toArray(new IntVar[0])));
|
||||
|
||||
// 设置目标:最大化总和
|
||||
model.maximize(totalPercent);
|
||||
|
||||
// 创建求解器
|
||||
CpSolver solver = new CpSolver();
|
||||
SatParameters.Builder parameters = solver.getParameters();
|
||||
parameters.setMaxTimeInSeconds(maxTime);
|
||||
parameters.setLogSearchProgress(false);
|
||||
|
||||
CpSolverStatus status = solver.solve(model);
|
||||
|
||||
if (status == CpSolverStatus.OPTIMAL || status == CpSolverStatus.FEASIBLE) {
|
||||
// 创建一个匿名的回调实现来获取解
|
||||
return new CoalBlendResultInfoEntity() {{
|
||||
// 设置比例信息
|
||||
List<CoalPercentVo> percentInfos = new ArrayList<>();
|
||||
double totalPercentValue = 0;
|
||||
|
||||
for (int i = 0; i < coalPercents.size(); i++) {
|
||||
long percentValue = solver.value(coalPercents.get(i));
|
||||
if (percentValue > 0) {
|
||||
double percent = percentValue / 100.0; // 转换回百分比
|
||||
percentInfos.add(new CoalPercentVo(
|
||||
coals.get(i).getName(),
|
||||
percent,
|
||||
0.0,
|
||||
percent));
|
||||
totalPercentValue += percent;
|
||||
}
|
||||
}
|
||||
|
||||
setPercentInfos(percentInfos);
|
||||
setPercentSum(totalPercentValue);
|
||||
|
||||
// 计算各参数值
|
||||
if (totalPercentValue > 0) {
|
||||
for (int paramIndex = 1; paramIndex <= 20; paramIndex++) {
|
||||
if (!paramFlags[paramIndex - 1].get()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String paramName = "param" + paramIndex;
|
||||
double weightedSum = 0;
|
||||
|
||||
for (int i = 0; i < coals.size(); i++) {
|
||||
long percentValue = solver.value(coalPercents.get(i));
|
||||
if (percentValue > 0) {
|
||||
Double paramValue = coals.get(i).getParam(paramName);
|
||||
if (paramValue != null) {
|
||||
weightedSum += (percentValue / 100.0) * paramValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double averageValue = weightedSum / totalPercentValue;
|
||||
|
||||
try {
|
||||
String setterName = "setParam" + paramIndex;
|
||||
getClass().getMethod(setterName, Double.class).invoke(this, averageValue);
|
||||
} catch (Exception e) {
|
||||
log.warn("设置参数{}值失败", paramName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("求解最优解失败", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将最优解作为hint添加到新模型中
|
||||
*/
|
||||
private static void addOptimalSolutionAsHint(CpModel model, List<IntVar> coalPercents,
|
||||
CoalBlendResultInfoEntity optimalResult, List<CoalBlendCoalInfoEntity> coals) {
|
||||
try {
|
||||
// 为每个变量设置hint值
|
||||
for (int i = 0; i < coalPercents.size(); i++) {
|
||||
String coalName = coals.get(i).getName();
|
||||
long hintValue = 0;
|
||||
|
||||
// 从最优解中找到对应煤种的比例
|
||||
for (CoalPercentVo percentVo : optimalResult.getPercentInfos()) {
|
||||
if (coalName.equals(percentVo.getName())) {
|
||||
hintValue = (long) (percentVo.getPercent() * 100); // 转换为0.01%单位
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加hint
|
||||
model.addHint(coalPercents.get(i), hintValue);
|
||||
}
|
||||
|
||||
log.info("已将最优解作为hint添加到模型中,总比例: {}", optimalResult.getPercentSum());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("添加最优解hint失败", e);
|
||||
}
|
||||
} /**
|
||||
* 添加顺序约束:只有前一种煤完全使用后才能使用下一种煤
|
||||
*/
|
||||
@@ -165,14 +317,16 @@ public class RawCoalBlendCalculator { /**
|
||||
|
||||
try {
|
||||
// 计算加权参数值 = Σ(煤i的比例 * 煤i的参数值) / 总比例
|
||||
// 使用1000倍精度来减少精度损失
|
||||
final int PRECISION_MULTIPLIER = 1000;
|
||||
long[] coefficients = new long[coalPercents.size()];
|
||||
boolean hasValidCoal = false;
|
||||
|
||||
for (int i = 0; i < coals.size(); i++) {
|
||||
Double paramValue = coals.get(i).getParam(paramName);
|
||||
if (paramValue != null) {
|
||||
// 系数需要放大100倍来处理小数(比例是百分比*100)
|
||||
coefficients[i] = (long) (paramValue * 100);
|
||||
// 系数需要放大1000倍来处理小数(比例是百分比*100)
|
||||
coefficients[i] = (long) (paramValue * PRECISION_MULTIPLIER);
|
||||
hasValidCoal = true;
|
||||
} else {
|
||||
coefficients[i] = 0;
|
||||
@@ -208,7 +362,7 @@ public class RawCoalBlendCalculator { /**
|
||||
}
|
||||
|
||||
// 添加最小值约束:weightedSum >= minValue * totalPercent
|
||||
long minBound = (long) (minValue * 100);
|
||||
long minBound = (long) (minValue * PRECISION_MULTIPLIER);
|
||||
LinearExpr minConstraint = LinearExpr.newBuilder()
|
||||
.addTerm(totalPercent, minBound)
|
||||
.build();
|
||||
@@ -235,8 +389,16 @@ public class RawCoalBlendCalculator { /**
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加最大值约束:weightedSum <= maxValue * totalPercent
|
||||
long maxBound = (long) (maxValue * 100);
|
||||
// 为等式约束(min == max)添加小的容差
|
||||
long maxBound;
|
||||
if (minValue != null && Math.abs(maxValue - minValue) < 0.001) {
|
||||
// 等式约束,添加小的容差(0.1%)
|
||||
maxBound = (long) (maxValue * PRECISION_MULTIPLIER + PRECISION_MULTIPLIER * 0.001);
|
||||
log.debug("参数{}等式约束,添加容差: 原值={}, 调整后上界={}", paramName, maxValue, maxBound / (double) PRECISION_MULTIPLIER);
|
||||
} else {
|
||||
maxBound = (long) (maxValue * PRECISION_MULTIPLIER);
|
||||
}
|
||||
|
||||
LinearExpr maxConstraint = LinearExpr.newBuilder()
|
||||
.addTerm(totalPercent, maxBound)
|
||||
.build();
|
||||
|
||||
Reference in New Issue
Block a user