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 result; private String filteringProcess; public FirstBallPredictionResult(List 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 redBalls, List blueBalls) { // 参数验证 validateInputParams(level, redBalls, blueBalls); // 根据不同级别处理 Map> ballWithCoefficientMap = new HashMap<>(); List allCandidateBalls = new ArrayList<>(); // Step 1: 根据5个前区号码获取候选球 for (Integer redBall : redBalls) { List 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 top3HistoryTop = getTop3FromHistoryTop(); allCandidateBalls.addAll(top3HistoryTop); // Step 3: 从百期排行表获取前3个 List top3HistoryTop100 = getTop3FromHistoryTop100(); allCandidateBalls.addAll(top3HistoryTop100); // Step 4: 从后区号码获取候选球 for (Integer blueBall : blueBalls) { List 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 predictFirstBall(String level, List redBalls, List blueBalls) { // 参数验证 validateInputParams(level, redBalls, blueBalls); // 根据不同级别处理 List result; Map> ballWithCoefficientMap = new HashMap<>(); List allCandidateBalls = new ArrayList<>(); // Step 1: 根据5个前区号码获取候选球 for (Integer redBall : redBalls) { List 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 top3HistoryTop = getTop3FromHistoryTop(); allCandidateBalls.addAll(top3HistoryTop); // Step 3: 从百期排行表获取前3个 List top3HistoryTop100 = getTop3FromHistoryTop100(); allCandidateBalls.addAll(top3HistoryTop100); // Step 4: 从后区号码获取候选球 for (Integer blueBall : blueBalls) { List 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 getHighLevelBallsWithCoefficients(Integer masterBallNumber) { List d9List = d9Mapper.selectList( new LambdaQueryWrapper() .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 result = new ArrayList<>(); int index = 0; int addedCount = 0; while (addedCount < 17 && index < d9List.size()) { double currentCoefficient = d9List.get(index).getCoefficient(); List 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 selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needCount); for (Integer selectedBall : selectedBalls) { result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber)); addedCount++; } } } return result; } /** * 中位策略:获取D9表中平均值附近的17个球号 */ private List getMiddleLevelBallsWithCoefficients(Integer masterBallNumber) { List d9List = d9Mapper.selectList( new LambdaQueryWrapper() .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 result = new ArrayList<>(); // 先加入平均值位置的球号 result.add(new BallWithCoefficient(d9List.get(avgPosition).getSlaveBallNumber(), d9List.get(avgPosition).getCoefficient(), masterBallNumber)); // 向上取8个球号(使用高位策略的毛边处理逻辑) List upperBalls = getUpperBallsFromAverage(d9List, avgPosition, 8, masterBallNumber); result.addAll(upperBalls); // 向下取8个球号(使用低位策略的毛边处理逻辑) List lowerBalls = getLowerBallsFromAverage(d9List, avgPosition, 8, masterBallNumber); result.addAll(lowerBalls); return result; } /** * 从平均值位置向上取指定数量的球号 */ private List getUpperBallsFromAverage(List d9List, int avgPosition, int needCount, Integer masterBallNumber) { List result = new ArrayList<>(); int index = avgPosition - 1; // 从平均值位置的上一个开始 int addedCount = 0; // 从平均值向上遍历(使用高位策略的逻辑) while (addedCount < needCount && index >= 0) { double currentCoefficient = d9List.get(index).getCoefficient(); List 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 selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needFromGroup); for (Integer selectedBall : selectedBalls) { result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber)); addedCount++; } } } return result; } /** * 从平均值位置向下取指定数量的球号 */ private List getLowerBallsFromAverage(List d9List, int avgPosition, int needCount, Integer masterBallNumber) { List result = new ArrayList<>(); int index = avgPosition + 1; // 从平均值位置的下一个开始 int addedCount = 0; // 从平均值向下遍历(使用低位策略的逻辑) while (addedCount < needCount && index < d9List.size()) { double currentCoefficient = d9List.get(index).getCoefficient(); List 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 selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needFromGroup); for (Integer selectedBall : selectedBalls) { result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber)); addedCount++; } } } return result; } /** * 低位策略:获取D9表中最小值向上17个球号 */ private List getLowLevelBallsWithCoefficients(Integer masterBallNumber) { List d9List = d9Mapper.selectList( new LambdaQueryWrapper() .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 result = new ArrayList<>(); int index = minPosition; int addedCount = 0; // 从最小值开始向上遍历 while (addedCount < 17 && index >= 0) { double currentCoefficient = d9List.get(index).getCoefficient(); List 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 selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needCount); for (Integer selectedBall : selectedBalls) { result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber)); addedCount++; } } } return result; } /** * 从D12表获取前17位球号 */ private List getTop17FromD12WithCoefficients(Integer masterBallNumber, String level) { List d12List = d12Mapper.selectList( new LambdaQueryWrapper() .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 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 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 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 upperBalls = getUpperBallsFromAverageD12(d12List, avgPosition, 8, masterBallNumber); result.addAll(upperBalls); // 向下取8个球号(使用低位策略的毛边处理逻辑) List 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 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 selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needCount); for (Integer selectedBall : selectedBalls) { result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber)); addedCount++; } } } } return result; } /** * 从D12表平均值位置向上取指定数量的球号 */ private List getUpperBallsFromAverageD12(List d12List, int avgPosition, int needCount, Integer masterBallNumber) { List result = new ArrayList<>(); int index = avgPosition - 1; // 从平均值位置的上一个开始 int addedCount = 0; // 从平均值向上遍历(使用高位策略的逻辑) while (addedCount < needCount && index >= 0) { double currentCoefficient = d12List.get(index).getCoefficient(); List 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 selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needFromGroup); for (Integer selectedBall : selectedBalls) { result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber)); addedCount++; } } } return result; } /** * 从D12表平均值位置向下取指定数量的球号 */ private List getLowerBallsFromAverageD12(List d12List, int avgPosition, int needCount, Integer masterBallNumber) { List result = new ArrayList<>(); int index = avgPosition + 1; // 从平均值位置的下一个开始 int addedCount = 0; // 从平均值向下遍历(使用低位策略的逻辑) while (addedCount < needCount && index < d12List.size()) { double currentCoefficient = d12List.get(index).getCoefficient(); List 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 selectedBalls = handleMultipleBoundaryConflicts(sameCoefficientBalls, needFromGroup); for (Integer selectedBall : selectedBalls) { result.add(new BallWithCoefficient(selectedBall, currentCoefficient, masterBallNumber)); addedCount++; } } } return result; } /** * 获取历史排行表中系数最大的前3位 */ private List getTop3FromHistoryTop() { List historyTopList = dltFrontendHistoryTopMapper.selectList( new LambdaQueryWrapper() .orderByDesc(DltFrontendHistoryTop::getActiveCoefficient)); if (CollectionUtils.isEmpty(historyTopList)) { return new ArrayList<>(); } List 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 getTop3FromHistoryTop100() { List historyTop100List = dltFrontendHistoryTop100Mapper.selectList( new LambdaQueryWrapper() .orderByDesc(DltFrontendHistoryTop100::getActiveCoefficient)); if (CollectionUtils.isEmpty(historyTop100List)) { return new ArrayList<>(); } // 取前3位 List top3Balls = new ArrayList<>(); int count = 0; int index = 0; while (count < 3 && index < historyTop100List.size()) { double currentCoefficient = historyTop100List.get(index).getActiveCoefficient(); List 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 selectedBalls = selectBallsFromHistoryAll(sameCoefficientBalls, needCount); top3Balls.addAll(selectedBalls); count += selectedBalls.size(); } } return top3Balls; } /** * 从dlt_frontend_history_all表中根据活跃系数选择指定数量的球号 * @param candidateBalls 候选球号列表 * @param selectCount 需要选择的数量 * @return 选中的球号列表 */ private List selectBallsFromHistoryAll(List candidateBalls, int selectCount) { if (CollectionUtils.isEmpty(candidateBalls) || selectCount <= 0) { return new ArrayList<>(); } // 获取所有候选球号在dlt_frontend_history_all表中的活跃系数 Map ballCoefficientMap = new HashMap<>(); for (Integer ballNumber : candidateBalls) { DltFrontendHistoryAll record = dltFrontendHistoryAllMapper.selectOne( new LambdaQueryWrapper() .eq(DltFrontendHistoryAll::getBallNumber, ballNumber)); double coefficient = (record != null) ? record.getActiveCoefficient() : 0.0; ballCoefficientMap.put(ballNumber, coefficient); } // 按活跃系数降序排序 List> sortedEntries = ballCoefficientMap.entrySet().stream() .sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue())) .collect(Collectors.toList()); // 选择前selectCount个球号 List 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 handleMultipleBoundaryConflicts(List 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 top100Coefficients = new HashMap<>(); for (Integer ball : candidateBalls) { DltFrontendHistoryTop100 record = dltFrontendHistoryTop100Mapper.selectOne( new LambdaQueryWrapper() .eq(DltFrontendHistoryTop100::getBallNumber, ball)); if (record != null) { top100Coefficients.put(ball, record.getActiveCoefficient()); } else { top100Coefficients.put(ball, 0.0); } } // 按系数降序排序 List> sortedByTop100 = top100Coefficients.entrySet().stream() .sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue())) .collect(Collectors.toList()); List result = new ArrayList<>(); // 按系数分组处理 int currentIndex = 0; while (result.size() < selectCount && currentIndex < sortedByTop100.size()) { Double currentCoefficient = sortedByTop100.get(currentIndex).getValue(); List 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 selectedFromTop = selectFromHistoryTop(sameTop100CoefficientBalls, needFromThisGroup); result.addAll(selectedFromTop); } } return result; } /** * 从dlt_frontend_history_top表中选择指定数量的球号 */ private List selectFromHistoryTop(List 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 topCoefficients = new HashMap<>(); for (Integer ball : candidateBalls) { DltFrontendHistoryTop record = dltFrontendHistoryTopMapper.selectOne( new LambdaQueryWrapper() .eq(DltFrontendHistoryTop::getBallNumber, ball)); if (record != null) { topCoefficients.put(ball, record.getActiveCoefficient()); } else { topCoefficients.put(ball, 0.0); } } // 按系数降序排序,选择前selectCount个 List> sortedByTop = topCoefficients.entrySet().stream() .sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue())) .collect(Collectors.toList()); List 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 candidateBalls) { if (CollectionUtils.isEmpty(candidateBalls)) { return null; } if (candidateBalls.size() == 1) { return candidateBalls.get(0); } // 1. 先从dlt_frontend_history_top_100比较 Map top100Coefficients = new HashMap<>(); for (Integer ball : candidateBalls) { DltFrontendHistoryTop100 record = dltFrontendHistoryTop100Mapper.selectOne( new LambdaQueryWrapper() .eq(DltFrontendHistoryTop100::getBallNumber, ball)); if (record != null) { top100Coefficients.put(ball, record.getActiveCoefficient()); } else { top100Coefficients.put(ball, 0.0); } } // 找出系数最大的球 Optional> maxTop100Entry = top100Coefficients.entrySet().stream() .max(Map.Entry.comparingByValue()); if (maxTop100Entry.isPresent()) { Double maxCoefficient = maxTop100Entry.get().getValue(); List 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 topCoefficients = new HashMap<>(); for (Integer ball : maxCoefficientBalls) { DltFrontendHistoryTop record = dltFrontendHistoryTopMapper.selectOne( new LambdaQueryWrapper() .eq(DltFrontendHistoryTop::getBallNumber, ball)); if (record != null) { topCoefficients.put(ball, record.getActiveCoefficient()); } else { topCoefficients.put(ball, 0.0); } } Optional> maxTopEntry = topCoefficients.entrySet().stream() .max(Map.Entry.comparingByValue()); if (maxTopEntry.isPresent()) { Double maxTopCoefficient = maxTopEntry.get().getValue(); List 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 allCandidateBalls, Map> ballWithCoefficientMap) { StringBuilder processBuilder = new StringBuilder(); // 统计球号出现次数 Map ballFrequencyMap = new HashMap<>(); for (Integer ball : allCandidateBalls) { ballFrequencyMap.put(ball, ballFrequencyMap.getOrDefault(ball, 0) + 1); } // 按频率分组 Map> frequencyGroups = new TreeMap<>((a, b) -> b.compareTo(a)); // 按频率降序 for (Map.Entry 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> entry : frequencyGroups.entrySet()) { int frequency = entry.getKey(); List 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 frequencyDescriptions = new ArrayList<>(); // 只显示频率大于等于最低筛选频率的球号 for (Map.Entry> entry : frequencyGroups.entrySet()) { int frequency = entry.getKey(); if (frequency >= minSelectedFrequency) { List 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 resultBalls = new ArrayList<>(); List directSelected = new ArrayList<>(); List needFurtherSelection = new ArrayList<>(); boolean hasSecondarySelection = false; String selectionSteps = ""; String detailedCoefficientInfo = ""; // 逐个处理每个频率组 for (Map.Entry> frequencyGroup : frequencyGroups.entrySet()) { List 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 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 selectedBalls; private String stepDescription; private String detailedInfo; public D9SelectionResult(List selectedBalls, String stepDescription, String detailedInfo) { this.selectedBalls = selectedBalls; this.stepDescription = stepDescription; this.detailedInfo = detailedInfo; } } /** * 根据D9系数筛选球号(带过程记录) */ private D9SelectionResult selectBallsByD9CoefficientWithProcess(List candidateBalls, Map> ballWithCoefficientMap, int needCount) { // 计算每个球的D9系数和 Map ballCoefficientSum = new HashMap<>(); for (Integer ball : candidateBalls) { List coefficients = ballWithCoefficientMap.getOrDefault(ball, new ArrayList<>()); double sum = coefficients.stream() .mapToDouble(BallWithCoefficient::getCoefficient) .sum(); ballCoefficientSum.put(ball, sum); } // 生成系数详情信息 List coefficientDetails = new ArrayList<>(); for (Map.Entry entry : ballCoefficientSum.entrySet()) { coefficientDetails.add(entry.getKey() + "(系数和" + String.format("%.2f", entry.getValue()) + ")"); } Collections.sort(coefficientDetails); String detailedInfo = "T3系数和详情:" + String.join(",", coefficientDetails); // 按D9系数分组 Map> coefficientGroups = new TreeMap<>((a, b) -> b.compareTo(a)); // 降序 for (Map.Entry entry : ballCoefficientSum.entrySet()) { double coefficient = entry.getValue(); int ball = entry.getKey(); coefficientGroups.computeIfAbsent(coefficient, k -> new ArrayList<>()).add(ball); } List result = new ArrayList<>(); String stepDescription = ""; boolean usedHistoryRanking = false; // 逐个处理每个系数组 for (Map.Entry> coefficientGroup : coefficientGroups.entrySet()) { List ballsInGroup = coefficientGroup.getValue(); int remainingNeeded = needCount - result.size(); if (remainingNeeded <= 0) { break; } if (ballsInGroup.size() <= remainingNeeded) { // 组内球数小于等于剩余需要数,全部加入 result.addAll(ballsInGroup); } else { // 组内球数大于剩余需要数,按百期排行筛选 List 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 selectBallsByD9Coefficient(List candidateBalls, Map> ballWithCoefficientMap, int needCount) { // 计算每个球的D9系数和 Map ballCoefficientSum = new HashMap<>(); for (Integer ball : candidateBalls) { List coefficients = ballWithCoefficientMap.getOrDefault(ball, new ArrayList<>()); double sum = coefficients.stream() .mapToDouble(BallWithCoefficient::getCoefficient) .sum(); ballCoefficientSum.put(ball, sum); } // 按D9系数分组 Map> coefficientGroups = new TreeMap<>((a, b) -> b.compareTo(a)); // 降序 for (Map.Entry entry : ballCoefficientSum.entrySet()) { double coefficient = entry.getValue(); int ball = entry.getKey(); coefficientGroups.computeIfAbsent(coefficient, k -> new ArrayList<>()).add(ball); } List result = new ArrayList<>(); // 逐个处理每个系数组 for (Map.Entry> coefficientGroup : coefficientGroups.entrySet()) { List ballsInGroup = coefficientGroup.getValue(); int remainingNeeded = needCount - result.size(); if (remainingNeeded <= 0) { break; } if (ballsInGroup.size() <= remainingNeeded) { // 组内球数小于等于剩余需要数,全部加入 result.addAll(ballsInGroup); } else { // 组内球数大于剩余需要数,按百期排行筛选 List selectedFromGroup = selectBallsByHistoryRanking(ballsInGroup, remainingNeeded); result.addAll(selectedFromGroup); } } return result; } /** * 根据历史排行筛选球号 */ private List selectBallsByHistoryRanking(List candidateBalls, int needCount) { // 先按百期排行筛选 Map top100Rankings = getHistoryTop100Rankings(candidateBalls); // 按百期排行分组 Map> top100Groups = new TreeMap<>((a, b) -> b.compareTo(a)); // 降序 for (Map.Entry entry : top100Rankings.entrySet()) { double ranking = entry.getValue(); int ball = entry.getKey(); top100Groups.computeIfAbsent(ranking, k -> new ArrayList<>()).add(ball); } List result = new ArrayList<>(); // 逐个处理每个百期排行组 for (Map.Entry> rankingGroup : top100Groups.entrySet()) { List ballsInGroup = rankingGroup.getValue(); int remainingNeeded = needCount - result.size(); if (remainingNeeded <= 0) { break; } if (ballsInGroup.size() <= remainingNeeded) { // 组内球数小于等于剩余需要数,全部加入 result.addAll(ballsInGroup); } else { // 组内球数大于剩余需要数,按历史排行筛选 List selectedFromGroup = selectTopBallsByRanking(ballsInGroup, remainingNeeded); result.addAll(selectedFromGroup); } } return result; } /** * 根据历史排行筛选最优球号 */ private List selectTopBallsByRanking(List candidateBalls, int needCount) { Map 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 getHistoryTop100Rankings(List balls) { Map result = new HashMap<>(); for (Integer ball : balls) { DltFrontendHistoryTop100 record = dltFrontendHistoryTop100Mapper.selectOne( new LambdaQueryWrapper() .eq(DltFrontendHistoryTop100::getBallNumber, ball)); result.put(ball, record != null ? record.getActiveCoefficient() : 0.0); } return result; } /** * 获取球号在历史排行表中的系数 */ private Map getHistoryTopRankings(List balls) { Map result = new HashMap<>(); for (Integer ball : balls) { DltFrontendHistoryTop record = dltFrontendHistoryTopMapper.selectOne( new LambdaQueryWrapper() .eq(DltFrontendHistoryTop::getBallNumber, ball)); result.put(ball, record != null ? record.getActiveCoefficient() : 0.0); } return result; } /** * 验证输入参数 */ private void validateInputParams(String level, List redBalls, List 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"); } } } }