1355 lines
60 KiB
Java
1355 lines
60 KiB
Java
|
|
package com.xy.xyaicpzs.dlt;
|
|||
|
|
|
|||
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||
|
|
import com.xy.xyaicpzs.domain.entity.*;
|
|||
|
|
import com.xy.xyaicpzs.mapper.*;
|
|||
|
|
import lombok.Data;
|
|||
|
|
import lombok.extern.slf4j.Slf4j;
|
|||
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|||
|
|
import org.springframework.stereotype.Component;
|
|||
|
|
import org.springframework.util.CollectionUtils;
|
|||
|
|
|
|||
|
|
import java.util.*;
|
|||
|
|
import java.util.stream.Collectors;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 前区首球预测器
|
|||
|
|
*/
|
|||
|
|
@Slf4j
|
|||
|
|
@Component
|
|||
|
|
public class FirstBallPredictor {
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private D9Mapper d9Mapper;
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private D12Mapper d12Mapper;
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private DltFrontendHistoryTop100Mapper dltFrontendHistoryTop100Mapper;
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private DltFrontendHistoryTopMapper dltFrontendHistoryTopMapper;
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private DltFrontendHistoryAllMapper dltFrontendHistoryAllMapper;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 球号和系数的关联类
|
|||
|
|
*/
|
|||
|
|
@Data
|
|||
|
|
public static class BallWithCoefficient {
|
|||
|
|
private Integer ballNumber;
|
|||
|
|
private Double coefficient;
|
|||
|
|
private Integer masterBallNumber; // 主球号,用于标识来源
|
|||
|
|
|
|||
|
|
public BallWithCoefficient(Integer ballNumber, Double coefficient, Integer masterBallNumber) {
|
|||
|
|
this.ballNumber = ballNumber;
|
|||
|
|
this.coefficient = coefficient;
|
|||
|
|
this.masterBallNumber = masterBallNumber;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@Override
|
|||
|
|
public String toString() {
|
|||
|
|
return "Ball{" +
|
|||
|
|
"ballNumber=" + ballNumber +
|
|||
|
|
", coefficient=" + coefficient +
|
|||
|
|
", masterBallNumber=" + masterBallNumber +
|
|||
|
|
'}';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 首球预测结果类
|
|||
|
|
*/
|
|||
|
|
@Data
|
|||
|
|
public static class FirstBallPredictionResult {
|
|||
|
|
private List<Integer> result;
|
|||
|
|
private String filteringProcess;
|
|||
|
|
|
|||
|
|
public FirstBallPredictionResult(List<Integer> result, String filteringProcess) {
|
|||
|
|
this.result = result;
|
|||
|
|
this.filteringProcess = filteringProcess;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 首球预测主方法(带筛选过程)
|
|||
|
|
* @param level 位置级别(high/middle/low)
|
|||
|
|
* @param redBalls 前区号码
|
|||
|
|
* @param blueBalls 后区号码
|
|||
|
|
* @return 预测结果和筛选过程
|
|||
|
|
*/
|
|||
|
|
public FirstBallPredictionResult predictFirstBallWithProcess(String level, List<Integer> redBalls, List<Integer> blueBalls) {
|
|||
|
|
// 参数验证
|
|||
|
|
validateInputParams(level, redBalls, blueBalls);
|
|||
|
|
|
|||
|
|
// 根据不同级别处理
|
|||
|
|
Map<Integer, List<BallWithCoefficient>> ballWithCoefficientMap = new HashMap<>();
|
|||
|
|
|
|||
|
|
List<Integer> allCandidateBalls = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// Step 1: 根据5个前区号码获取候选球
|
|||
|
|
for (Integer redBall : redBalls) {
|
|||
|
|
List<BallWithCoefficient> ballsWithCoefficients;
|
|||
|
|
if ("high".equalsIgnoreCase(level)) {
|
|||
|
|
ballsWithCoefficients = getHighLevelBallsWithCoefficients(redBall);
|
|||
|
|
} else if ("middle".equalsIgnoreCase(level)) {
|
|||
|
|
ballsWithCoefficients = getMiddleLevelBallsWithCoefficients(redBall);
|
|||
|
|
} else { // low
|
|||
|
|
ballsWithCoefficients = getLowLevelBallsWithCoefficients(redBall);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
|||
|
|
ballWithCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
|||
|
|
allCandidateBalls.add(ball.getBallNumber());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Step 2: 从历史排行表获取前3个
|
|||
|
|
List<Integer> top3HistoryTop = getTop3FromHistoryTop();
|
|||
|
|
allCandidateBalls.addAll(top3HistoryTop);
|
|||
|
|
|
|||
|
|
// Step 3: 从百期排行表获取前3个
|
|||
|
|
List<Integer> top3HistoryTop100 = getTop3FromHistoryTop100();
|
|||
|
|
allCandidateBalls.addAll(top3HistoryTop100);
|
|||
|
|
|
|||
|
|
// Step 4: 从后区号码获取候选球
|
|||
|
|
for (Integer blueBall : blueBalls) {
|
|||
|
|
List<BallWithCoefficient> blueballsWithCoefficients = getTop17FromD12WithCoefficients(blueBall, level);
|
|||
|
|
for (BallWithCoefficient ball : blueballsWithCoefficients) {
|
|||
|
|
allCandidateBalls.add(ball.getBallNumber());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 最终筛选12个球
|
|||
|
|
return selectFinal12Balls(allCandidateBalls, ballWithCoefficientMap);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 首球预测主方法(原版本,保持兼容性)
|
|||
|
|
* @param level 位置级别(high/middle/low)
|
|||
|
|
* @param redBalls 前区号码
|
|||
|
|
* @param blueBalls 后区号码
|
|||
|
|
* @return 预测的12个首球号码
|
|||
|
|
*/
|
|||
|
|
public List<Integer> predictFirstBall(String level, List<Integer> redBalls, List<Integer> blueBalls) {
|
|||
|
|
// 参数验证
|
|||
|
|
validateInputParams(level, redBalls, blueBalls);
|
|||
|
|
|
|||
|
|
// 根据不同级别处理
|
|||
|
|
List<Integer> result;
|
|||
|
|
Map<Integer, List<BallWithCoefficient>> ballWithCoefficientMap = new HashMap<>();
|
|||
|
|
|
|||
|
|
List<Integer> allCandidateBalls = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// Step 1: 根据5个前区号码获取候选球
|
|||
|
|
for (Integer redBall : redBalls) {
|
|||
|
|
List<BallWithCoefficient> ballsWithCoefficients;
|
|||
|
|
if ("high".equalsIgnoreCase(level)) {
|
|||
|
|
ballsWithCoefficients = getHighLevelBallsWithCoefficients(redBall);
|
|||
|
|
} else if ("middle".equalsIgnoreCase(level)) {
|
|||
|
|
ballsWithCoefficients = getMiddleLevelBallsWithCoefficients(redBall);
|
|||
|
|
} else { // low
|
|||
|
|
ballsWithCoefficients = getLowLevelBallsWithCoefficients(redBall);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (BallWithCoefficient ball : ballsWithCoefficients) {
|
|||
|
|
ballWithCoefficientMap.computeIfAbsent(ball.getBallNumber(), k -> new ArrayList<>()).add(ball);
|
|||
|
|
allCandidateBalls.add(ball.getBallNumber());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Step 2: 从历史排行表获取前3个
|
|||
|
|
List<Integer> top3HistoryTop = getTop3FromHistoryTop();
|
|||
|
|
allCandidateBalls.addAll(top3HistoryTop);
|
|||
|
|
|
|||
|
|
// Step 3: 从百期排行表获取前3个
|
|||
|
|
List<Integer> top3HistoryTop100 = getTop3FromHistoryTop100();
|
|||
|
|
allCandidateBalls.addAll(top3HistoryTop100);
|
|||
|
|
|
|||
|
|
// Step 4: 从后区号码获取候选球
|
|||
|
|
for (Integer blueBall : blueBalls) {
|
|||
|
|
List<BallWithCoefficient> blueballsWithCoefficients = getTop17FromD12WithCoefficients(blueBall, level);
|
|||
|
|
for (BallWithCoefficient ball : blueballsWithCoefficients) {
|
|||
|
|
allCandidateBalls.add(ball.getBallNumber());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 最终筛选12个球
|
|||
|
|
FirstBallPredictionResult predictionResult = selectFinal12Balls(allCandidateBalls, ballWithCoefficientMap);
|
|||
|
|
result = predictionResult.getResult();
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 高位策略:获取D9表中系数最大的前17位球号
|
|||
|
|
*/
|
|||
|
|
private List<BallWithCoefficient> getHighLevelBallsWithCoefficients(Integer masterBallNumber) {
|
|||
|
|
List<D9> d9List = d9Mapper.selectList(
|
|||
|
|
new LambdaQueryWrapper<D9>()
|
|||
|
|
.eq(D9::getMasterBallNumber, masterBallNumber));
|
|||
|
|
if (CollectionUtils.isEmpty(d9List)) {
|
|||
|
|
log.warn("No D9 records found for master ball: {}", masterBallNumber);
|
|||
|
|
return new ArrayList<>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按系数从大到小排序
|
|||
|
|
d9List.sort((d1, d2) -> d2.getCoefficient().compareTo(d1.getCoefficient()));
|
|||
|
|
|
|||
|
|
List<BallWithCoefficient> result = new ArrayList<>();
|
|||
|
|
int index = 0;
|
|||
|
|
int addedCount = 0;
|
|||
|
|
|
|||
|
|
while (addedCount < 17 && index < d9List.size()) {
|
|||
|
|
double currentCoefficient = d9List.get(index).getCoefficient();
|
|||
|
|
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 收集所有相同系数的球号
|
|||
|
|
while (index < d9List.size() &&
|
|||
|
|
d9List.get(index).getCoefficient().equals(currentCoefficient)) {
|
|||
|
|
sameCoefficientBalls.add(d9List.get(index).getSlaveBallNumber());
|
|||
|
|
index++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算还需要多少个球
|
|||
|
|
int needCount = Math.min(17 - addedCount, sameCoefficientBalls.size());
|
|||
|
|
|
|||
|
|
if (sameCoefficientBalls.size() == 1) {
|
|||
|
|
// 只有一个球号,直接加入
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(0), currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
} else if (needCount == sameCoefficientBalls.size()) {
|
|||
|
|
// 需要选择的数量等于可用数量,全部加入
|
|||
|
|
for (int i = 0; i < sameCoefficientBalls.size(); i++) {
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(i), currentCoefficient, masterBallNumber));
|
|||
|
|
}
|
|||
|
|
addedCount += sameCoefficientBalls.size();
|
|||
|
|
} else {
|
|||
|
|
// 需要从多个相同系数的球号中选择部分,处理毛边
|
|||
|
|
List<Integer> selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needCount);
|
|||
|
|
for (Integer selectedBall : selectedBalls) {
|
|||
|
|
result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 中位策略:获取D9表中平均值附近的17个球号
|
|||
|
|
*/
|
|||
|
|
private List<BallWithCoefficient> getMiddleLevelBallsWithCoefficients(Integer masterBallNumber) {
|
|||
|
|
List<D9> d9List = d9Mapper.selectList(
|
|||
|
|
new LambdaQueryWrapper<D9>()
|
|||
|
|
.eq(D9::getMasterBallNumber, masterBallNumber));
|
|||
|
|
if (CollectionUtils.isEmpty(d9List)) {
|
|||
|
|
log.warn("No D9 records found for master ball: {}", masterBallNumber);
|
|||
|
|
return new ArrayList<>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按系数从大到小排序
|
|||
|
|
d9List.sort((d1, d2) -> d2.getCoefficient().compareTo(d1.getCoefficient()));
|
|||
|
|
|
|||
|
|
// 计算平均系数
|
|||
|
|
double avgCoefficient = d9List.stream()
|
|||
|
|
.mapToDouble(D9::getCoefficient)
|
|||
|
|
.average()
|
|||
|
|
.orElse(0.0);
|
|||
|
|
|
|||
|
|
// 找到比平均值大且最接近平均值的位置(从下到上遍历)
|
|||
|
|
int avgPosition = -1;
|
|||
|
|
for (int i = d9List.size() - 1; i >= 0; i--) {
|
|||
|
|
double diff = d9List.get(i).getCoefficient() - avgCoefficient;
|
|||
|
|
if (diff >= 0) {
|
|||
|
|
avgPosition = i;
|
|||
|
|
break; // 找到第一个比平均值大的位置就停止
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果没找到大于平均值的系数,取第一个
|
|||
|
|
if (avgPosition == -1) {
|
|||
|
|
avgPosition = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<BallWithCoefficient> result = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 先加入平均值位置的球号
|
|||
|
|
result.add(new BallWithCoefficient(d9List.get(avgPosition).getSlaveBallNumber(),
|
|||
|
|
d9List.get(avgPosition).getCoefficient(), masterBallNumber));
|
|||
|
|
|
|||
|
|
// 向上取8个球号(使用高位策略的毛边处理逻辑)
|
|||
|
|
List<BallWithCoefficient> upperBalls = getUpperBallsFromAverage(d9List, avgPosition, 8, masterBallNumber);
|
|||
|
|
result.addAll(upperBalls);
|
|||
|
|
|
|||
|
|
// 向下取8个球号(使用低位策略的毛边处理逻辑)
|
|||
|
|
List<BallWithCoefficient> lowerBalls = getLowerBallsFromAverage(d9List, avgPosition, 8, masterBallNumber);
|
|||
|
|
result.addAll(lowerBalls);
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 从平均值位置向上取指定数量的球号
|
|||
|
|
*/
|
|||
|
|
private List<BallWithCoefficient> getUpperBallsFromAverage(List<D9> d9List, int avgPosition, int needCount, Integer masterBallNumber) {
|
|||
|
|
List<BallWithCoefficient> result = new ArrayList<>();
|
|||
|
|
int index = avgPosition - 1; // 从平均值位置的上一个开始
|
|||
|
|
int addedCount = 0;
|
|||
|
|
|
|||
|
|
// 从平均值向上遍历(使用高位策略的逻辑)
|
|||
|
|
while (addedCount < needCount && index >= 0) {
|
|||
|
|
double currentCoefficient = d9List.get(index).getCoefficient();
|
|||
|
|
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 收集所有相同系数的球号(向前收集)
|
|||
|
|
int tempIndex = index;
|
|||
|
|
while (tempIndex >= 0 &&
|
|||
|
|
d9List.get(tempIndex).getCoefficient().equals(currentCoefficient)) {
|
|||
|
|
sameCoefficientBalls.add(d9List.get(tempIndex).getSlaveBallNumber());
|
|||
|
|
tempIndex--;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新index为下一个不同系数的位置
|
|||
|
|
index = tempIndex;
|
|||
|
|
|
|||
|
|
// 计算还需要多少个球
|
|||
|
|
int needFromGroup = Math.min(needCount - addedCount, sameCoefficientBalls.size());
|
|||
|
|
|
|||
|
|
if (sameCoefficientBalls.size() == 1) {
|
|||
|
|
// 只有一个球号,直接加入
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(0), currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
} else if (needFromGroup == sameCoefficientBalls.size()) {
|
|||
|
|
// 需要选择的数量等于可用数量,全部加入
|
|||
|
|
for (int i = 0; i < sameCoefficientBalls.size(); i++) {
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(i), currentCoefficient, masterBallNumber));
|
|||
|
|
}
|
|||
|
|
addedCount += sameCoefficientBalls.size();
|
|||
|
|
} else {
|
|||
|
|
// 需要从多个相同系数的球号中选择部分,处理毛边(向上使用高位策略)
|
|||
|
|
List<Integer> selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needFromGroup);
|
|||
|
|
for (Integer selectedBall : selectedBalls) {
|
|||
|
|
result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 从平均值位置向下取指定数量的球号
|
|||
|
|
*/
|
|||
|
|
private List<BallWithCoefficient> getLowerBallsFromAverage(List<D9> d9List, int avgPosition, int needCount, Integer masterBallNumber) {
|
|||
|
|
List<BallWithCoefficient> result = new ArrayList<>();
|
|||
|
|
int index = avgPosition + 1; // 从平均值位置的下一个开始
|
|||
|
|
int addedCount = 0;
|
|||
|
|
|
|||
|
|
// 从平均值向下遍历(使用低位策略的逻辑)
|
|||
|
|
while (addedCount < needCount && index < d9List.size()) {
|
|||
|
|
double currentCoefficient = d9List.get(index).getCoefficient();
|
|||
|
|
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 收集所有相同系数的球号(向后收集)
|
|||
|
|
int tempIndex = index;
|
|||
|
|
while (tempIndex < d9List.size() &&
|
|||
|
|
d9List.get(tempIndex).getCoefficient().equals(currentCoefficient)) {
|
|||
|
|
sameCoefficientBalls.add(d9List.get(tempIndex).getSlaveBallNumber());
|
|||
|
|
tempIndex++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新index为下一个不同系数的位置
|
|||
|
|
index = tempIndex;
|
|||
|
|
|
|||
|
|
// 计算还需要多少个球
|
|||
|
|
int needFromGroup = Math.min(needCount - addedCount, sameCoefficientBalls.size());
|
|||
|
|
|
|||
|
|
if (sameCoefficientBalls.size() == 1) {
|
|||
|
|
// 只有一个球号,直接加入
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(0), currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
} else if (needFromGroup == sameCoefficientBalls.size()) {
|
|||
|
|
// 需要选择的数量等于可用数量,全部加入
|
|||
|
|
for (int i = 0; i < sameCoefficientBalls.size(); i++) {
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(i), currentCoefficient, masterBallNumber));
|
|||
|
|
}
|
|||
|
|
addedCount += sameCoefficientBalls.size();
|
|||
|
|
} else {
|
|||
|
|
// 需要从多个相同系数的球号中选择部分,处理毛边(向下使用高位策略,因为是前区)
|
|||
|
|
List<Integer> selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needFromGroup);
|
|||
|
|
for (Integer selectedBall : selectedBalls) {
|
|||
|
|
result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 低位策略:获取D9表中最小值向上17个球号
|
|||
|
|
*/
|
|||
|
|
private List<BallWithCoefficient> getLowLevelBallsWithCoefficients(Integer masterBallNumber) {
|
|||
|
|
List<D9> d9List = d9Mapper.selectList(
|
|||
|
|
new LambdaQueryWrapper<D9>()
|
|||
|
|
.eq(D9::getMasterBallNumber, masterBallNumber));
|
|||
|
|
if (CollectionUtils.isEmpty(d9List)) {
|
|||
|
|
log.warn("No D9 records found for master ball: {}", masterBallNumber);
|
|||
|
|
return new ArrayList<>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按系数从大到小排序
|
|||
|
|
d9List.sort((d1, d2) -> d2.getCoefficient().compareTo(d1.getCoefficient()));
|
|||
|
|
|
|||
|
|
// 找到最小值的位置
|
|||
|
|
int minPosition = d9List.size() - 1;
|
|||
|
|
|
|||
|
|
List<BallWithCoefficient> result = new ArrayList<>();
|
|||
|
|
int index = minPosition;
|
|||
|
|
int addedCount = 0;
|
|||
|
|
|
|||
|
|
// 从最小值开始向上遍历
|
|||
|
|
while (addedCount < 17 && index >= 0) {
|
|||
|
|
double currentCoefficient = d9List.get(index).getCoefficient();
|
|||
|
|
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 收集所有相同系数的球号(向前收集)
|
|||
|
|
int tempIndex = index;
|
|||
|
|
while (tempIndex >= 0 &&
|
|||
|
|
d9List.get(tempIndex).getCoefficient().equals(currentCoefficient)) {
|
|||
|
|
sameCoefficientBalls.add(d9List.get(tempIndex).getSlaveBallNumber());
|
|||
|
|
tempIndex--;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新index为下一个不同系数的位置
|
|||
|
|
index = tempIndex;
|
|||
|
|
|
|||
|
|
// 计算还需要多少个球
|
|||
|
|
int needCount = Math.min(17 - addedCount, sameCoefficientBalls.size());
|
|||
|
|
|
|||
|
|
if (sameCoefficientBalls.size() == 1) {
|
|||
|
|
// 只有一个球号,直接加入
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(0), currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
} else if (needCount == sameCoefficientBalls.size()) {
|
|||
|
|
// 需要选择的数量等于可用数量,全部加入
|
|||
|
|
for (int i = 0; i < sameCoefficientBalls.size(); i++) {
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(i), currentCoefficient, masterBallNumber));
|
|||
|
|
}
|
|||
|
|
addedCount += sameCoefficientBalls.size();
|
|||
|
|
} else {
|
|||
|
|
// 需要从多个相同系数的球号中选择部分,处理毛边
|
|||
|
|
List<Integer> selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needCount);
|
|||
|
|
for (Integer selectedBall : selectedBalls) {
|
|||
|
|
result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 从D12表获取前17位球号
|
|||
|
|
*/
|
|||
|
|
private List<BallWithCoefficient> getTop17FromD12WithCoefficients(Integer masterBallNumber, String level) {
|
|||
|
|
List<D12> d12List = d12Mapper.selectList(
|
|||
|
|
new LambdaQueryWrapper<D12>()
|
|||
|
|
.eq(D12::getMasterBallNumber, masterBallNumber));
|
|||
|
|
if (CollectionUtils.isEmpty(d12List)) {
|
|||
|
|
log.warn("No D12 records found for master ball: {}", masterBallNumber);
|
|||
|
|
return new ArrayList<>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按系数从大到小排序
|
|||
|
|
d12List.sort((d1, d2) -> d2.getCoefficient().compareTo(d1.getCoefficient()));
|
|||
|
|
|
|||
|
|
List<BallWithCoefficient> result = new ArrayList<>();
|
|||
|
|
|
|||
|
|
if ("high".equalsIgnoreCase(level)) {
|
|||
|
|
// 高位:取系数最大的前17位
|
|||
|
|
int index = 0;
|
|||
|
|
int addedCount = 0;
|
|||
|
|
|
|||
|
|
while (addedCount < 17 && index < d12List.size()) {
|
|||
|
|
double currentCoefficient = d12List.get(index).getCoefficient();
|
|||
|
|
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 收集所有相同系数的球号
|
|||
|
|
while (index < d12List.size() &&
|
|||
|
|
d12List.get(index).getCoefficient().equals(currentCoefficient)) {
|
|||
|
|
sameCoefficientBalls.add(d12List.get(index).getSlaveBallNumber());
|
|||
|
|
index++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算还需要多少个球
|
|||
|
|
int needCount = Math.min(17 - addedCount, sameCoefficientBalls.size());
|
|||
|
|
|
|||
|
|
if (sameCoefficientBalls.size() == 1) {
|
|||
|
|
// 只有一个球号,直接加入
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(0), currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
} else if (needCount == sameCoefficientBalls.size()) {
|
|||
|
|
// 需要选择的数量等于可用数量,全部加入
|
|||
|
|
for (int i = 0; i < sameCoefficientBalls.size(); i++) {
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(i), currentCoefficient, masterBallNumber));
|
|||
|
|
}
|
|||
|
|
addedCount += sameCoefficientBalls.size();
|
|||
|
|
} else {
|
|||
|
|
// 需要从多个相同系数的球号中选择部分,处理毛边
|
|||
|
|
List<Integer> selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needCount);
|
|||
|
|
for (Integer selectedBall : selectedBalls) {
|
|||
|
|
result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else if ("middle".equalsIgnoreCase(level)) {
|
|||
|
|
// 中位:计算平均值,取其附近的17个球
|
|||
|
|
double avgCoefficient = d12List.stream()
|
|||
|
|
.mapToDouble(D12::getCoefficient)
|
|||
|
|
.average()
|
|||
|
|
.orElse(0.0);
|
|||
|
|
|
|||
|
|
// 找到比平均值大且最接近平均值的位置(从下到上遍历)
|
|||
|
|
int avgPosition = -1;
|
|||
|
|
for (int i = d12List.size() - 1; i >= 0; i--) {
|
|||
|
|
double diff = d12List.get(i).getCoefficient() - avgCoefficient;
|
|||
|
|
if (diff >= 0) {
|
|||
|
|
avgPosition = i;
|
|||
|
|
break; // 找到第一个比平均值大的位置就停止
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果没找到大于平均值的系数,取第一个
|
|||
|
|
if (avgPosition == -1) {
|
|||
|
|
avgPosition = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 先加入平均值位置的球号
|
|||
|
|
result.add(new BallWithCoefficient(d12List.get(avgPosition).getSlaveBallNumber(),
|
|||
|
|
d12List.get(avgPosition).getCoefficient(), masterBallNumber));
|
|||
|
|
|
|||
|
|
// 向上取8个球号(使用高位策略的毛边处理逻辑)
|
|||
|
|
List<BallWithCoefficient> upperBalls = getUpperBallsFromAverageD12(d12List, avgPosition, 8, masterBallNumber);
|
|||
|
|
result.addAll(upperBalls);
|
|||
|
|
|
|||
|
|
// 向下取8个球号(使用低位策略的毛边处理逻辑)
|
|||
|
|
List<BallWithCoefficient> lowerBalls = getLowerBallsFromAverageD12(d12List, avgPosition, 8, masterBallNumber);
|
|||
|
|
result.addAll(lowerBalls);
|
|||
|
|
} else { // low
|
|||
|
|
// 低位:取最小值向上的17个球
|
|||
|
|
int minPosition = d12List.size() - 1;
|
|||
|
|
|
|||
|
|
int index = minPosition;
|
|||
|
|
int addedCount = 0;
|
|||
|
|
|
|||
|
|
// 从最小值开始向上遍历
|
|||
|
|
while (addedCount < 17 && index >= 0) {
|
|||
|
|
double currentCoefficient = d12List.get(index).getCoefficient();
|
|||
|
|
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 收集所有相同系数的球号(向前收集)
|
|||
|
|
int tempIndex = index;
|
|||
|
|
while (tempIndex >= 0 &&
|
|||
|
|
d12List.get(tempIndex).getCoefficient().equals(currentCoefficient)) {
|
|||
|
|
sameCoefficientBalls.add(d12List.get(tempIndex).getSlaveBallNumber());
|
|||
|
|
tempIndex--;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新index为下一个不同系数的位置
|
|||
|
|
index = tempIndex;
|
|||
|
|
|
|||
|
|
// 计算还需要多少个球
|
|||
|
|
int needCount = Math.min(17 - addedCount, sameCoefficientBalls.size());
|
|||
|
|
|
|||
|
|
if (sameCoefficientBalls.size() == 1) {
|
|||
|
|
// 只有一个球号,直接加入
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(0), currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
} else if (needCount == sameCoefficientBalls.size()) {
|
|||
|
|
// 需要选择的数量等于可用数量,全部加入
|
|||
|
|
for (int i = 0; i < sameCoefficientBalls.size(); i++) {
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(i), currentCoefficient, masterBallNumber));
|
|||
|
|
}
|
|||
|
|
addedCount += sameCoefficientBalls.size();
|
|||
|
|
} else {
|
|||
|
|
// 需要从多个相同系数的球号中选择部分,处理毛边
|
|||
|
|
List<Integer> selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needCount);
|
|||
|
|
for (Integer selectedBall : selectedBalls) {
|
|||
|
|
result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 从D12表平均值位置向上取指定数量的球号
|
|||
|
|
*/
|
|||
|
|
private List<BallWithCoefficient> getUpperBallsFromAverageD12(List<D12> d12List, int avgPosition, int needCount, Integer masterBallNumber) {
|
|||
|
|
List<BallWithCoefficient> result = new ArrayList<>();
|
|||
|
|
int index = avgPosition - 1; // 从平均值位置的上一个开始
|
|||
|
|
int addedCount = 0;
|
|||
|
|
|
|||
|
|
// 从平均值向上遍历(使用高位策略的逻辑)
|
|||
|
|
while (addedCount < needCount && index >= 0) {
|
|||
|
|
double currentCoefficient = d12List.get(index).getCoefficient();
|
|||
|
|
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 收集所有相同系数的球号(向前收集)
|
|||
|
|
int tempIndex = index;
|
|||
|
|
while (tempIndex >= 0 &&
|
|||
|
|
d12List.get(tempIndex).getCoefficient().equals(currentCoefficient)) {
|
|||
|
|
sameCoefficientBalls.add(d12List.get(tempIndex).getSlaveBallNumber());
|
|||
|
|
tempIndex--;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新index为下一个不同系数的位置
|
|||
|
|
index = tempIndex;
|
|||
|
|
|
|||
|
|
// 计算还需要多少个球
|
|||
|
|
int needFromGroup = Math.min(needCount - addedCount, sameCoefficientBalls.size());
|
|||
|
|
|
|||
|
|
if (sameCoefficientBalls.size() == 1) {
|
|||
|
|
// 只有一个球号,直接加入
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(0), currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
} else if (needFromGroup == sameCoefficientBalls.size()) {
|
|||
|
|
// 需要选择的数量等于可用数量,全部加入
|
|||
|
|
for (int i = 0; i < sameCoefficientBalls.size(); i++) {
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(i), currentCoefficient, masterBallNumber));
|
|||
|
|
}
|
|||
|
|
addedCount += sameCoefficientBalls.size();
|
|||
|
|
} else {
|
|||
|
|
// 需要从多个相同系数的球号中选择部分,处理毛边(向上使用高位策略)
|
|||
|
|
List<Integer> selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needFromGroup);
|
|||
|
|
for (Integer selectedBall : selectedBalls) {
|
|||
|
|
result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 从D12表平均值位置向下取指定数量的球号
|
|||
|
|
*/
|
|||
|
|
private List<BallWithCoefficient> getLowerBallsFromAverageD12(List<D12> d12List, int avgPosition, int needCount, Integer masterBallNumber) {
|
|||
|
|
List<BallWithCoefficient> result = new ArrayList<>();
|
|||
|
|
int index = avgPosition + 1; // 从平均值位置的下一个开始
|
|||
|
|
int addedCount = 0;
|
|||
|
|
|
|||
|
|
// 从平均值向下遍历(使用低位策略的逻辑)
|
|||
|
|
while (addedCount < needCount && index < d12List.size()) {
|
|||
|
|
double currentCoefficient = d12List.get(index).getCoefficient();
|
|||
|
|
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 收集所有相同系数的球号(向后收集)
|
|||
|
|
int tempIndex = index;
|
|||
|
|
while (tempIndex < d12List.size() &&
|
|||
|
|
d12List.get(tempIndex).getCoefficient().equals(currentCoefficient)) {
|
|||
|
|
sameCoefficientBalls.add(d12List.get(tempIndex).getSlaveBallNumber());
|
|||
|
|
tempIndex++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新index为下一个不同系数的位置
|
|||
|
|
index = tempIndex;
|
|||
|
|
|
|||
|
|
// 计算还需要多少个球
|
|||
|
|
int needFromGroup = Math.min(needCount - addedCount, sameCoefficientBalls.size());
|
|||
|
|
|
|||
|
|
if (sameCoefficientBalls.size() == 1) {
|
|||
|
|
// 只有一个球号,直接加入
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(0), currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
} else if (needFromGroup == sameCoefficientBalls.size()) {
|
|||
|
|
// 需要选择的数量等于可用数量,全部加入
|
|||
|
|
for (int i = 0; i < sameCoefficientBalls.size(); i++) {
|
|||
|
|
result.add(new BallWithCoefficient(sameCoefficientBalls.get(i), currentCoefficient, masterBallNumber));
|
|||
|
|
}
|
|||
|
|
addedCount += sameCoefficientBalls.size();
|
|||
|
|
} else {
|
|||
|
|
// 需要从多个相同系数的球号中选择部分,处理毛边(向下使用前区策略)
|
|||
|
|
List<Integer> selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needFromGroup);
|
|||
|
|
for (Integer selectedBall : selectedBalls) {
|
|||
|
|
result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber));
|
|||
|
|
addedCount++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取历史排行表中系数最大的前3位
|
|||
|
|
*/
|
|||
|
|
private List<Integer> getTop3FromHistoryTop() {
|
|||
|
|
List<DltFrontendHistoryTop> historyTopList = dltFrontendHistoryTopMapper.selectList(
|
|||
|
|
new LambdaQueryWrapper<DltFrontendHistoryTop>()
|
|||
|
|
.orderByDesc(DltFrontendHistoryTop::getActiveCoefficient));
|
|||
|
|
if (CollectionUtils.isEmpty(historyTopList)) {
|
|||
|
|
return new ArrayList<>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<Integer> result = new ArrayList<>();
|
|||
|
|
int size = Math.min(historyTopList.size(), 3);
|
|||
|
|
|
|||
|
|
for (int i = 0; i < size; i++) {
|
|||
|
|
result.add(historyTopList.get(i).getBallNumber());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取百期排行表中系数最大的前3位
|
|||
|
|
*/
|
|||
|
|
private List<Integer> getTop3FromHistoryTop100() {
|
|||
|
|
List<DltFrontendHistoryTop100> historyTop100List = dltFrontendHistoryTop100Mapper.selectList(
|
|||
|
|
new LambdaQueryWrapper<DltFrontendHistoryTop100>()
|
|||
|
|
.orderByDesc(DltFrontendHistoryTop100::getActiveCoefficient));
|
|||
|
|
if (CollectionUtils.isEmpty(historyTop100List)) {
|
|||
|
|
return new ArrayList<>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 取前3位
|
|||
|
|
List<Integer> top3Balls = new ArrayList<>();
|
|||
|
|
int count = 0;
|
|||
|
|
int index = 0;
|
|||
|
|
|
|||
|
|
while (count < 3 && index < historyTop100List.size()) {
|
|||
|
|
double currentCoefficient = historyTop100List.get(index).getActiveCoefficient();
|
|||
|
|
List<Integer> sameCoefficientBalls = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 收集所有相同系数的球号
|
|||
|
|
while (index < historyTop100List.size() &&
|
|||
|
|
historyTop100List.get(index).getActiveCoefficient().equals(currentCoefficient)) {
|
|||
|
|
sameCoefficientBalls.add(historyTop100List.get(index).getBallNumber());
|
|||
|
|
index++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 根据需要选择的数量和当前可用数量处理
|
|||
|
|
int needCount = Math.min(3 - count, sameCoefficientBalls.size());
|
|||
|
|
|
|||
|
|
if (sameCoefficientBalls.size() == 1) {
|
|||
|
|
// 只有一个球号,直接加入
|
|||
|
|
top3Balls.add(sameCoefficientBalls.get(0));
|
|||
|
|
count++;
|
|||
|
|
} else if (needCount == sameCoefficientBalls.size()) {
|
|||
|
|
// 需要选择的数量等于可用数量,全部加入
|
|||
|
|
top3Balls.addAll(sameCoefficientBalls);
|
|||
|
|
count += needCount;
|
|||
|
|
} else {
|
|||
|
|
// 需要从多个相同系数的球号中选择部分,使用dlt_frontend_history_all表比较
|
|||
|
|
List<Integer> selectedBalls = selectBallsFromHistoryAll(sameCoefficientBalls, needCount);
|
|||
|
|
top3Balls.addAll(selectedBalls);
|
|||
|
|
count += selectedBalls.size();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return top3Balls;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 从dlt_frontend_history_all表中根据活跃系数选择指定数量的球号
|
|||
|
|
* @param candidateBalls 候选球号列表
|
|||
|
|
* @param selectCount 需要选择的数量
|
|||
|
|
* @return 选中的球号列表
|
|||
|
|
*/
|
|||
|
|
private List<Integer> selectBallsFromHistoryAll(List<Integer> candidateBalls, int selectCount) {
|
|||
|
|
if (CollectionUtils.isEmpty(candidateBalls) || selectCount <= 0) {
|
|||
|
|
return new ArrayList<>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取所有候选球号在dlt_frontend_history_all表中的活跃系数
|
|||
|
|
Map<Integer, Double> ballCoefficientMap = new HashMap<>();
|
|||
|
|
for (Integer ballNumber : candidateBalls) {
|
|||
|
|
DltFrontendHistoryAll record = dltFrontendHistoryAllMapper.selectOne(
|
|||
|
|
new LambdaQueryWrapper<DltFrontendHistoryAll>()
|
|||
|
|
.eq(DltFrontendHistoryAll::getBallNumber, ballNumber));
|
|||
|
|
double coefficient = (record != null) ? record.getActiveCoefficient() : 0.0;
|
|||
|
|
ballCoefficientMap.put(ballNumber, coefficient);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按活跃系数降序排序
|
|||
|
|
List<Map.Entry<Integer, Double>> sortedEntries = ballCoefficientMap.entrySet().stream()
|
|||
|
|
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
|
|||
|
|
.collect(Collectors.toList());
|
|||
|
|
|
|||
|
|
// 选择前selectCount个球号
|
|||
|
|
List<Integer> result = new ArrayList<>();
|
|||
|
|
for (int i = 0; i < Math.min(selectCount, sortedEntries.size()); i++) {
|
|||
|
|
result.add(sortedEntries.get(i).getKey());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 处理多个边界冲突,选择指定数量的球号
|
|||
|
|
* @param candidateBalls 候选球号列表
|
|||
|
|
* @param selectCount 需要选择的数量
|
|||
|
|
* @return 选中的球号列表
|
|||
|
|
*/
|
|||
|
|
private List<Integer> handleMultipleBoundaryConflicts(List<Integer> candidateBalls, int selectCount) {
|
|||
|
|
if (CollectionUtils.isEmpty(candidateBalls) || selectCount <= 0) {
|
|||
|
|
return new ArrayList<>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (selectCount >= candidateBalls.size()) {
|
|||
|
|
return new ArrayList<>(candidateBalls);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 1. 先从dlt_frontend_history_top_100比较
|
|||
|
|
Map<Integer, Double> top100Coefficients = new HashMap<>();
|
|||
|
|
for (Integer ball : candidateBalls) {
|
|||
|
|
DltFrontendHistoryTop100 record = dltFrontendHistoryTop100Mapper.selectOne(
|
|||
|
|
new LambdaQueryWrapper<DltFrontendHistoryTop100>()
|
|||
|
|
.eq(DltFrontendHistoryTop100::getBallNumber, ball));
|
|||
|
|
if (record != null) {
|
|||
|
|
top100Coefficients.put(ball, record.getActiveCoefficient());
|
|||
|
|
} else {
|
|||
|
|
top100Coefficients.put(ball, 0.0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按系数降序排序
|
|||
|
|
List<Map.Entry<Integer, Double>> sortedByTop100 = top100Coefficients.entrySet().stream()
|
|||
|
|
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
|
|||
|
|
.collect(Collectors.toList());
|
|||
|
|
|
|||
|
|
List<Integer> result = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 按系数分组处理
|
|||
|
|
int currentIndex = 0;
|
|||
|
|
while (result.size() < selectCount && currentIndex < sortedByTop100.size()) {
|
|||
|
|
Double currentCoefficient = sortedByTop100.get(currentIndex).getValue();
|
|||
|
|
List<Integer> sameTop100CoefficientBalls = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 收集相同系数的球号
|
|||
|
|
while (currentIndex < sortedByTop100.size() &&
|
|||
|
|
sortedByTop100.get(currentIndex).getValue().equals(currentCoefficient)) {
|
|||
|
|
sameTop100CoefficientBalls.add(sortedByTop100.get(currentIndex).getKey());
|
|||
|
|
currentIndex++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int needFromThisGroup = Math.min(selectCount - result.size(), sameTop100CoefficientBalls.size());
|
|||
|
|
|
|||
|
|
if (sameTop100CoefficientBalls.size() == 1 || needFromThisGroup == sameTop100CoefficientBalls.size()) {
|
|||
|
|
// 只有一个球或者需要全部选择
|
|||
|
|
for (int i = 0; i < needFromThisGroup; i++) {
|
|||
|
|
result.add(sameTop100CoefficientBalls.get(i));
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 需要进一步筛选,使用dlt_frontend_history_top表
|
|||
|
|
List<Integer> selectedFromTop = selectFromHistoryTop(sameTop100CoefficientBalls, needFromThisGroup);
|
|||
|
|
result.addAll(selectedFromTop);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 从dlt_frontend_history_top表中选择指定数量的球号
|
|||
|
|
*/
|
|||
|
|
private List<Integer> selectFromHistoryTop(List<Integer> candidateBalls, int selectCount) {
|
|||
|
|
if (CollectionUtils.isEmpty(candidateBalls) || selectCount <= 0) {
|
|||
|
|
return new ArrayList<>();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (selectCount >= candidateBalls.size()) {
|
|||
|
|
return new ArrayList<>(candidateBalls);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取在dlt_frontend_history_top表中的系数
|
|||
|
|
Map<Integer, Double> topCoefficients = new HashMap<>();
|
|||
|
|
for (Integer ball : candidateBalls) {
|
|||
|
|
DltFrontendHistoryTop record = dltFrontendHistoryTopMapper.selectOne(
|
|||
|
|
new LambdaQueryWrapper<DltFrontendHistoryTop>()
|
|||
|
|
.eq(DltFrontendHistoryTop::getBallNumber, ball));
|
|||
|
|
if (record != null) {
|
|||
|
|
topCoefficients.put(ball, record.getActiveCoefficient());
|
|||
|
|
} else {
|
|||
|
|
topCoefficients.put(ball, 0.0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按系数降序排序,选择前selectCount个
|
|||
|
|
List<Map.Entry<Integer, Double>> sortedByTop = topCoefficients.entrySet().stream()
|
|||
|
|
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
|
|||
|
|
.collect(Collectors.toList());
|
|||
|
|
|
|||
|
|
List<Integer> result = new ArrayList<>();
|
|||
|
|
for (int i = 0; i < Math.min(selectCount, sortedByTop.size()); i++) {
|
|||
|
|
result.add(sortedByTop.get(i).getKey());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果仍然不够,按原始顺序补充
|
|||
|
|
while (result.size() < selectCount && result.size() < candidateBalls.size()) {
|
|||
|
|
for (Integer ball : candidateBalls) {
|
|||
|
|
if (!result.contains(ball) && result.size() < selectCount) {
|
|||
|
|
result.add(ball);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 处理边界冲突(毛边)
|
|||
|
|
*/
|
|||
|
|
private Integer handleBoundaryConflicts(List<Integer> candidateBalls) {
|
|||
|
|
if (CollectionUtils.isEmpty(candidateBalls)) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (candidateBalls.size() == 1) {
|
|||
|
|
return candidateBalls.get(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 1. 先从dlt_frontend_history_top_100比较
|
|||
|
|
Map<Integer, Double> top100Coefficients = new HashMap<>();
|
|||
|
|
for (Integer ball : candidateBalls) {
|
|||
|
|
DltFrontendHistoryTop100 record = dltFrontendHistoryTop100Mapper.selectOne(
|
|||
|
|
new LambdaQueryWrapper<DltFrontendHistoryTop100>()
|
|||
|
|
.eq(DltFrontendHistoryTop100::getBallNumber, ball));
|
|||
|
|
if (record != null) {
|
|||
|
|
top100Coefficients.put(ball, record.getActiveCoefficient());
|
|||
|
|
} else {
|
|||
|
|
top100Coefficients.put(ball, 0.0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 找出系数最大的球
|
|||
|
|
Optional<Map.Entry<Integer, Double>> maxTop100Entry = top100Coefficients.entrySet().stream()
|
|||
|
|
.max(Map.Entry.comparingByValue());
|
|||
|
|
|
|||
|
|
if (maxTop100Entry.isPresent()) {
|
|||
|
|
Double maxCoefficient = maxTop100Entry.get().getValue();
|
|||
|
|
List<Integer> maxCoefficientBalls = top100Coefficients.entrySet().stream()
|
|||
|
|
.filter(e -> e.getValue().equals(maxCoefficient))
|
|||
|
|
.map(Map.Entry::getKey)
|
|||
|
|
.collect(Collectors.toList());
|
|||
|
|
|
|||
|
|
if (maxCoefficientBalls.size() == 1) {
|
|||
|
|
return maxCoefficientBalls.get(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 如果仍有多个球系数相同,从dlt_frontend_history_top比较
|
|||
|
|
Map<Integer, Double> topCoefficients = new HashMap<>();
|
|||
|
|
for (Integer ball : maxCoefficientBalls) {
|
|||
|
|
DltFrontendHistoryTop record = dltFrontendHistoryTopMapper.selectOne(
|
|||
|
|
new LambdaQueryWrapper<DltFrontendHistoryTop>()
|
|||
|
|
.eq(DltFrontendHistoryTop::getBallNumber, ball));
|
|||
|
|
if (record != null) {
|
|||
|
|
topCoefficients.put(ball, record.getActiveCoefficient());
|
|||
|
|
} else {
|
|||
|
|
topCoefficients.put(ball, 0.0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Optional<Map.Entry<Integer, Double>> maxTopEntry = topCoefficients.entrySet().stream()
|
|||
|
|
.max(Map.Entry.comparingByValue());
|
|||
|
|
|
|||
|
|
if (maxTopEntry.isPresent()) {
|
|||
|
|
Double maxTopCoefficient = maxTopEntry.get().getValue();
|
|||
|
|
List<Integer> maxTopCoefficientBalls = topCoefficients.entrySet().stream()
|
|||
|
|
.filter(e -> e.getValue().equals(maxTopCoefficient))
|
|||
|
|
.map(Map.Entry::getKey)
|
|||
|
|
.collect(Collectors.toList());
|
|||
|
|
|
|||
|
|
if (maxTopCoefficientBalls.size() == 1) {
|
|||
|
|
return maxTopCoefficientBalls.get(0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 如果仍无法区分,默认选择第一位
|
|||
|
|
return candidateBalls.get(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 从所有候选球中筛选最终的12个球
|
|||
|
|
* @param allCandidateBalls 所有候选球
|
|||
|
|
* @param ballWithCoefficientMap 球号对应的系数信息
|
|||
|
|
* @return 最终选出的12个球和筛选过程
|
|||
|
|
*/
|
|||
|
|
private FirstBallPredictionResult selectFinal12Balls(List<Integer> allCandidateBalls, Map<Integer, List<BallWithCoefficient>> ballWithCoefficientMap) {
|
|||
|
|
StringBuilder processBuilder = new StringBuilder();
|
|||
|
|
|
|||
|
|
// 统计球号出现次数
|
|||
|
|
Map<Integer, Integer> ballFrequencyMap = new HashMap<>();
|
|||
|
|
for (Integer ball : allCandidateBalls) {
|
|||
|
|
ballFrequencyMap.put(ball, ballFrequencyMap.getOrDefault(ball, 0) + 1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按频率分组
|
|||
|
|
Map<Integer, List<Integer>> frequencyGroups = new TreeMap<>((a, b) -> b.compareTo(a)); // 按频率降序
|
|||
|
|
for (Map.Entry<Integer, Integer> entry : ballFrequencyMap.entrySet()) {
|
|||
|
|
int ball = entry.getKey();
|
|||
|
|
int frequency = entry.getValue();
|
|||
|
|
frequencyGroups.computeIfAbsent(frequency, k -> new ArrayList<>()).add(ball);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 预先确定最低筛选频率(即实际参与筛选的最低频率)
|
|||
|
|
int minSelectedFrequency = Integer.MAX_VALUE;
|
|||
|
|
int tempSelected = 0;
|
|||
|
|
for (Map.Entry<Integer, List<Integer>> entry : frequencyGroups.entrySet()) {
|
|||
|
|
int frequency = entry.getKey();
|
|||
|
|
List<Integer> balls = entry.getValue();
|
|||
|
|
|
|||
|
|
if (tempSelected + balls.size() <= 12) {
|
|||
|
|
// 这个频率组会全部被选中
|
|||
|
|
minSelectedFrequency = Math.min(minSelectedFrequency, frequency);
|
|||
|
|
tempSelected += balls.size();
|
|||
|
|
} else {
|
|||
|
|
// 这个频率组需要部分选择,说明这是最后一个参与筛选的频率组
|
|||
|
|
minSelectedFrequency = Math.min(minSelectedFrequency, frequency);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成频率分布说明(只显示实际参与筛选的球号)
|
|||
|
|
processBuilder.append("参与筛选的候选球号按频率分布为");
|
|||
|
|
List<String> frequencyDescriptions = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 只显示频率大于等于最低筛选频率的球号
|
|||
|
|
for (Map.Entry<Integer, List<Integer>> entry : frequencyGroups.entrySet()) {
|
|||
|
|
int frequency = entry.getKey();
|
|||
|
|
if (frequency >= minSelectedFrequency) {
|
|||
|
|
List<Integer> balls = entry.getValue();
|
|||
|
|
Collections.sort(balls); // 排序显示
|
|||
|
|
|
|||
|
|
if (balls.size() == 1) {
|
|||
|
|
frequencyDescriptions.add(balls.get(0) + "(出现" + frequency + "次)");
|
|||
|
|
} else {
|
|||
|
|
String ballsStr = balls.stream().map(String::valueOf).collect(Collectors.joining(", "));
|
|||
|
|
frequencyDescriptions.add(ballsStr + "(出现" + frequency + "次)");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
processBuilder.append(String.join(",", frequencyDescriptions));
|
|||
|
|
processBuilder.append(";");
|
|||
|
|
|
|||
|
|
List<Integer> resultBalls = new ArrayList<>();
|
|||
|
|
List<Integer> directSelected = new ArrayList<>();
|
|||
|
|
List<Integer> needFurtherSelection = new ArrayList<>();
|
|||
|
|
boolean hasSecondarySelection = false;
|
|||
|
|
String selectionSteps = "";
|
|||
|
|
String detailedCoefficientInfo = "";
|
|||
|
|
|
|||
|
|
// 逐个处理每个频率组
|
|||
|
|
for (Map.Entry<Integer, List<Integer>> frequencyGroup : frequencyGroups.entrySet()) {
|
|||
|
|
List<Integer> ballsInGroup = frequencyGroup.getValue();
|
|||
|
|
int remainingNeeded = 12 - resultBalls.size();
|
|||
|
|
|
|||
|
|
if (remainingNeeded <= 0) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (ballsInGroup.size() <= remainingNeeded) {
|
|||
|
|
// 组内球数小于等于剩余需要数,全部加入
|
|||
|
|
resultBalls.addAll(ballsInGroup);
|
|||
|
|
directSelected.addAll(ballsInGroup);
|
|||
|
|
} else {
|
|||
|
|
// 组内球数大于剩余需要数,需要筛选
|
|||
|
|
hasSecondarySelection = true;
|
|||
|
|
needFurtherSelection.addAll(ballsInGroup);
|
|||
|
|
|
|||
|
|
// 进行D9系数筛选
|
|||
|
|
D9SelectionResult d9Result = selectBallsByD9CoefficientWithProcess(ballsInGroup, ballWithCoefficientMap, remainingNeeded);
|
|||
|
|
resultBalls.addAll(d9Result.selectedBalls);
|
|||
|
|
selectionSteps = d9Result.stepDescription;
|
|||
|
|
detailedCoefficientInfo = d9Result.detailedInfo;
|
|||
|
|
break; // 只处理需要筛选的第一组
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成筛选过程说明
|
|||
|
|
if (hasSecondarySelection) {
|
|||
|
|
processBuilder.append("无法直接筛选出前12个,其中");
|
|||
|
|
Collections.sort(directSelected);
|
|||
|
|
String directSelectedStr = directSelected.stream().map(String::valueOf).collect(Collectors.joining(", "));
|
|||
|
|
processBuilder.append(directSelectedStr).append("直接入选,");
|
|||
|
|
|
|||
|
|
Collections.sort(needFurtherSelection);
|
|||
|
|
String needFurtherStr = needFurtherSelection.stream().map(String::valueOf).collect(Collectors.joining(", "));
|
|||
|
|
processBuilder.append(needFurtherStr).append("需要进行二次筛选,");
|
|||
|
|
|
|||
|
|
List<Integer> finalSelected = new ArrayList<>(resultBalls);
|
|||
|
|
finalSelected.removeAll(directSelected);
|
|||
|
|
Collections.sort(finalSelected);
|
|||
|
|
String finalSelectedStr = finalSelected.stream().map(String::valueOf).collect(Collectors.joining(", "));
|
|||
|
|
processBuilder.append("最终筛选出").append(finalSelectedStr).append(",");
|
|||
|
|
|
|||
|
|
String allResultStr = resultBalls.stream().map(String::valueOf).collect(Collectors.joining(", "));
|
|||
|
|
processBuilder.append("组成前12个球号:").append(allResultStr).append("。");
|
|||
|
|
|
|||
|
|
processBuilder.append("筛选步骤:").append(selectionSteps).append("。");
|
|||
|
|
if (!detailedCoefficientInfo.isEmpty()) {
|
|||
|
|
processBuilder.append(" ").append(detailedCoefficientInfo).append("。");
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
String allResultStr = resultBalls.stream().map(String::valueOf).collect(Collectors.joining(", "));
|
|||
|
|
processBuilder.append("直接筛选出前12个球号:").append(allResultStr).append("。");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 无论是否有二次筛选,都要显示筛选步骤
|
|||
|
|
if (!hasSecondarySelection || selectionSteps.isEmpty()) {
|
|||
|
|
processBuilder.append("筛选步骤:通过频率筛选确定所有球号,无需进行D9系数筛选、百期排位、历史排位。");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new FirstBallPredictionResult(resultBalls, processBuilder.toString());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* D9系数筛选结果类
|
|||
|
|
*/
|
|||
|
|
@Data
|
|||
|
|
private static class D9SelectionResult {
|
|||
|
|
private List<Integer> selectedBalls;
|
|||
|
|
private String stepDescription;
|
|||
|
|
private String detailedInfo;
|
|||
|
|
|
|||
|
|
public D9SelectionResult(List<Integer> selectedBalls, String stepDescription, String detailedInfo) {
|
|||
|
|
this.selectedBalls = selectedBalls;
|
|||
|
|
this.stepDescription = stepDescription;
|
|||
|
|
this.detailedInfo = detailedInfo;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 根据D9系数筛选球号(带过程记录)
|
|||
|
|
*/
|
|||
|
|
private D9SelectionResult selectBallsByD9CoefficientWithProcess(List<Integer> candidateBalls, Map<Integer, List<BallWithCoefficient>> ballWithCoefficientMap, int needCount) {
|
|||
|
|
// 计算每个球的D9系数和
|
|||
|
|
Map<Integer, Double> ballCoefficientSum = new HashMap<>();
|
|||
|
|
for (Integer ball : candidateBalls) {
|
|||
|
|
List<BallWithCoefficient> coefficients = ballWithCoefficientMap.getOrDefault(ball, new ArrayList<>());
|
|||
|
|
double sum = coefficients.stream()
|
|||
|
|
.mapToDouble(BallWithCoefficient::getCoefficient)
|
|||
|
|
.sum();
|
|||
|
|
ballCoefficientSum.put(ball, sum);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成系数详情信息
|
|||
|
|
List<String> coefficientDetails = new ArrayList<>();
|
|||
|
|
for (Map.Entry<Integer, Double> entry : ballCoefficientSum.entrySet()) {
|
|||
|
|
coefficientDetails.add(entry.getKey() + "(系数和" + String.format("%.2f", entry.getValue()) + ")");
|
|||
|
|
}
|
|||
|
|
Collections.sort(coefficientDetails);
|
|||
|
|
String detailedInfo = "T3系数和详情:" + String.join(",", coefficientDetails);
|
|||
|
|
|
|||
|
|
// 按D9系数分组
|
|||
|
|
Map<Double, List<Integer>> coefficientGroups = new TreeMap<>((a, b) -> b.compareTo(a)); // 降序
|
|||
|
|
for (Map.Entry<Integer, Double> entry : ballCoefficientSum.entrySet()) {
|
|||
|
|
double coefficient = entry.getValue();
|
|||
|
|
int ball = entry.getKey();
|
|||
|
|
coefficientGroups.computeIfAbsent(coefficient, k -> new ArrayList<>()).add(ball);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<Integer> result = new ArrayList<>();
|
|||
|
|
String stepDescription = "";
|
|||
|
|
boolean usedHistoryRanking = false;
|
|||
|
|
|
|||
|
|
// 逐个处理每个系数组
|
|||
|
|
for (Map.Entry<Double, List<Integer>> coefficientGroup : coefficientGroups.entrySet()) {
|
|||
|
|
List<Integer> ballsInGroup = coefficientGroup.getValue();
|
|||
|
|
int remainingNeeded = needCount - result.size();
|
|||
|
|
|
|||
|
|
if (remainingNeeded <= 0) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (ballsInGroup.size() <= remainingNeeded) {
|
|||
|
|
// 组内球数小于等于剩余需要数,全部加入
|
|||
|
|
result.addAll(ballsInGroup);
|
|||
|
|
} else {
|
|||
|
|
// 组内球数大于剩余需要数,按百期排行筛选
|
|||
|
|
List<Integer> selectedFromGroup = selectBallsByHistoryRanking(ballsInGroup, remainingNeeded);
|
|||
|
|
result.addAll(selectedFromGroup);
|
|||
|
|
usedHistoryRanking = true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (usedHistoryRanking) {
|
|||
|
|
stepDescription = "通过频率筛选确定部分球号,通过D9系数和筛选确定剩余球号,需要进行百期排位、历史排位";
|
|||
|
|
} else {
|
|||
|
|
stepDescription = "通过频率筛选确定部分球号,通过D9系数和筛选确定剩余球号,无需进行百期排位、历史排位";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new D9SelectionResult(result, stepDescription, detailedInfo);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 根据D9系数筛选球号(原版本保持兼容)
|
|||
|
|
*/
|
|||
|
|
private List<Integer> selectBallsByD9Coefficient(List<Integer> candidateBalls, Map<Integer, List<BallWithCoefficient>> ballWithCoefficientMap, int needCount) {
|
|||
|
|
// 计算每个球的D9系数和
|
|||
|
|
Map<Integer, Double> ballCoefficientSum = new HashMap<>();
|
|||
|
|
for (Integer ball : candidateBalls) {
|
|||
|
|
List<BallWithCoefficient> coefficients = ballWithCoefficientMap.getOrDefault(ball, new ArrayList<>());
|
|||
|
|
double sum = coefficients.stream()
|
|||
|
|
.mapToDouble(BallWithCoefficient::getCoefficient)
|
|||
|
|
.sum();
|
|||
|
|
ballCoefficientSum.put(ball, sum);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按D9系数分组
|
|||
|
|
Map<Double, List<Integer>> coefficientGroups = new TreeMap<>((a, b) -> b.compareTo(a)); // 降序
|
|||
|
|
for (Map.Entry<Integer, Double> entry : ballCoefficientSum.entrySet()) {
|
|||
|
|
double coefficient = entry.getValue();
|
|||
|
|
int ball = entry.getKey();
|
|||
|
|
coefficientGroups.computeIfAbsent(coefficient, k -> new ArrayList<>()).add(ball);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<Integer> result = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 逐个处理每个系数组
|
|||
|
|
for (Map.Entry<Double, List<Integer>> coefficientGroup : coefficientGroups.entrySet()) {
|
|||
|
|
List<Integer> ballsInGroup = coefficientGroup.getValue();
|
|||
|
|
int remainingNeeded = needCount - result.size();
|
|||
|
|
|
|||
|
|
if (remainingNeeded <= 0) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (ballsInGroup.size() <= remainingNeeded) {
|
|||
|
|
// 组内球数小于等于剩余需要数,全部加入
|
|||
|
|
result.addAll(ballsInGroup);
|
|||
|
|
} else {
|
|||
|
|
// 组内球数大于剩余需要数,按百期排行筛选
|
|||
|
|
List<Integer> selectedFromGroup = selectBallsByHistoryRanking(ballsInGroup, remainingNeeded);
|
|||
|
|
result.addAll(selectedFromGroup);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 根据历史排行筛选球号
|
|||
|
|
*/
|
|||
|
|
private List<Integer> selectBallsByHistoryRanking(List<Integer> candidateBalls, int needCount) {
|
|||
|
|
// 先按百期排行筛选
|
|||
|
|
Map<Integer, Double> top100Rankings = getHistoryTop100Rankings(candidateBalls);
|
|||
|
|
|
|||
|
|
// 按百期排行分组
|
|||
|
|
Map<Double, List<Integer>> top100Groups = new TreeMap<>((a, b) -> b.compareTo(a)); // 降序
|
|||
|
|
for (Map.Entry<Integer, Double> entry : top100Rankings.entrySet()) {
|
|||
|
|
double ranking = entry.getValue();
|
|||
|
|
int ball = entry.getKey();
|
|||
|
|
top100Groups.computeIfAbsent(ranking, k -> new ArrayList<>()).add(ball);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<Integer> result = new ArrayList<>();
|
|||
|
|
|
|||
|
|
// 逐个处理每个百期排行组
|
|||
|
|
for (Map.Entry<Double, List<Integer>> rankingGroup : top100Groups.entrySet()) {
|
|||
|
|
List<Integer> ballsInGroup = rankingGroup.getValue();
|
|||
|
|
int remainingNeeded = needCount - result.size();
|
|||
|
|
|
|||
|
|
if (remainingNeeded <= 0) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (ballsInGroup.size() <= remainingNeeded) {
|
|||
|
|
// 组内球数小于等于剩余需要数,全部加入
|
|||
|
|
result.addAll(ballsInGroup);
|
|||
|
|
} else {
|
|||
|
|
// 组内球数大于剩余需要数,按历史排行筛选
|
|||
|
|
List<Integer> selectedFromGroup = selectTopBallsByRanking(ballsInGroup, remainingNeeded);
|
|||
|
|
result.addAll(selectedFromGroup);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 根据历史排行筛选最优球号
|
|||
|
|
*/
|
|||
|
|
private List<Integer> selectTopBallsByRanking(List<Integer> candidateBalls, int needCount) {
|
|||
|
|
Map<Integer, Double> topRankings = getHistoryTopRankings(candidateBalls);
|
|||
|
|
|
|||
|
|
return topRankings.entrySet().stream()
|
|||
|
|
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
|
|||
|
|
.limit(needCount)
|
|||
|
|
.map(Map.Entry::getKey)
|
|||
|
|
.collect(Collectors.toList());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取球号在百期排行表中的系数
|
|||
|
|
*/
|
|||
|
|
private Map<Integer, Double> getHistoryTop100Rankings(List<Integer> balls) {
|
|||
|
|
Map<Integer, Double> result = new HashMap<>();
|
|||
|
|
for (Integer ball : balls) {
|
|||
|
|
DltFrontendHistoryTop100 record = dltFrontendHistoryTop100Mapper.selectOne(
|
|||
|
|
new LambdaQueryWrapper<DltFrontendHistoryTop100>()
|
|||
|
|
.eq(DltFrontendHistoryTop100::getBallNumber, ball));
|
|||
|
|
result.put(ball, record != null ? record.getActiveCoefficient() : 0.0);
|
|||
|
|
}
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取球号在历史排行表中的系数
|
|||
|
|
*/
|
|||
|
|
private Map<Integer, Double> getHistoryTopRankings(List<Integer> balls) {
|
|||
|
|
Map<Integer, Double> result = new HashMap<>();
|
|||
|
|
for (Integer ball : balls) {
|
|||
|
|
DltFrontendHistoryTop record = dltFrontendHistoryTopMapper.selectOne(
|
|||
|
|
new LambdaQueryWrapper<DltFrontendHistoryTop>()
|
|||
|
|
.eq(DltFrontendHistoryTop::getBallNumber, ball));
|
|||
|
|
result.put(ball, record != null ? record.getActiveCoefficient() : 0.0);
|
|||
|
|
}
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 验证输入参数
|
|||
|
|
*/
|
|||
|
|
private void validateInputParams(String level, List<Integer> redBalls, List<Integer> blueBalls) {
|
|||
|
|
if (!"high".equalsIgnoreCase(level) && !"middle".equalsIgnoreCase(level) && !"low".equalsIgnoreCase(level)) {
|
|||
|
|
throw new IllegalArgumentException("位置级别必须是high/middle/low之一");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (CollectionUtils.isEmpty(redBalls) || redBalls.size() != 5) {
|
|||
|
|
throw new IllegalArgumentException("前区号码必须为5个");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (CollectionUtils.isEmpty(blueBalls) || blueBalls.size() != 2) {
|
|||
|
|
throw new IllegalArgumentException("后区号码必须为2个");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (Integer redBall : redBalls) {
|
|||
|
|
if (redBall < 1 || redBall > 35) {
|
|||
|
|
throw new IllegalArgumentException("前区号码范围应为1-35");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (Integer blueBall : blueBalls) {
|
|||
|
|
if (blueBall < 1 || blueBall > 12) {
|
|||
|
|
throw new IllegalArgumentException("后区号码范围应为1-12");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|