智能选号功能修正,样式调整

This commit is contained in:
Mars 2025-06-19 14:00:14 +08:00
parent 77d81ffa4b
commit a093b50d5a
9 changed files with 1985 additions and 24 deletions

View File

@ -219,4 +219,62 @@ MIT License
- 系统会自动检查本地数据库中最新的开奖日期
- 只获取并更新比本地数据更新的开奖记录
- 更新过程会记录日志到 `lottery_update.log` 文件
- 支持双色球和大乐透两种彩票的数据更新
- 支持双色球和大乐透两种彩票的数据更新
## 分析功能说明
### 基础分析策略
#### 1. 冷热号码分析
- 热号统计近50期出现频率最高的号码
- 冷号:统计超过平均遗漏期数的号码
- API: GET `/api/analysis/hot-cold/{lottery_type}?periods=50`
#### 2. 号码分布分析
- 分区统计:将号码划分为多个区间,分析各区出号比例
- 奇偶比:分析奇偶数的分布规律
- 大小比:统计大数和小数的分布规律
- API: GET `/api/analysis/distribution/{lottery_type}?periods=100`
#### 3. 连号与重复号分析
- 连号追踪:统计连号出现的频率和位置
- 重复号观察:分析相邻期号码重复情况
- API: GET `/api/analysis/consecutive/{lottery_type}?periods=100`
### 数学理论应用
#### 1. 数学统计特征
- 计算红球总和的历史平均值和标准差
- 分析号码组合的数学特征
- API: GET `/api/analysis/math-stats/{lottery_type}?periods=100`
#### 2. 遗漏值分析
- 计算每个号码的当前遗漏值
- 分析历史最大遗漏
- API: GET `/api/analysis/missing/{lottery_type}`
### 智能选号策略
系统提供多种智能选号策略:
- 均衡策略:综合考虑号码分布特征
- 热号策略:偏好选择近期高频号码
- 冷号策略:偏好选择遗漏值较大的号码
- 遗漏值策略:基于遗漏值分析选号
- API: GET `/api/analysis/smart-numbers/{lottery_type}?strategy=balanced`
### 使用示例
1. 获取双色球热号冷号分析:
```bash
curl http://localhost:8000/api/analysis/hot-cold/ssq?periods=50
```
2. 获取大乐透号码分布分析:
```bash
curl http://localhost:8000/api/analysis/distribution/dlt?periods=100
```
3. 使用均衡策略生成双色球号码:
```bash
curl http://localhost:8000/api/analysis/smart-numbers/ssq?strategy=balanced
```

View File

@ -0,0 +1,108 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import Optional
from ...core.database import get_db
from ...services.analysis_service import LotteryAnalysisService
router = APIRouter()
@router.get("/hot-cold/{lottery_type}")
def get_hot_cold_numbers(
lottery_type: str,
periods: Optional[int] = 50,
db: Session = Depends(get_db)
):
"""获取热号和冷号分析"""
if lottery_type not in ['ssq', 'dlt']:
raise HTTPException(status_code=400, detail="Invalid lottery type")
analysis_service = LotteryAnalysisService(db)
return analysis_service.get_hot_cold_numbers(lottery_type, periods)
@router.get("/distribution/{lottery_type}")
def get_number_distribution(
lottery_type: str,
periods: Optional[int] = 100,
db: Session = Depends(get_db)
):
"""获取号码分布分析"""
if lottery_type not in ['ssq', 'dlt']:
raise HTTPException(status_code=400, detail="Invalid lottery type")
analysis_service = LotteryAnalysisService(db)
return analysis_service.analyze_number_distribution(lottery_type, periods)
@router.get("/consecutive/{lottery_type}")
def get_consecutive_analysis(
lottery_type: str,
periods: Optional[int] = 100,
db: Session = Depends(get_db)
):
"""获取连号和重复号分析"""
if lottery_type not in ['ssq', 'dlt']:
raise HTTPException(status_code=400, detail="Invalid lottery type")
analysis_service = LotteryAnalysisService(db)
return analysis_service.analyze_consecutive_numbers(lottery_type, periods)
@router.get("/math-stats/{lottery_type}")
def get_mathematical_stats(
lottery_type: str,
periods: Optional[int] = 100,
db: Session = Depends(get_db)
):
"""获取数学统计特征"""
if lottery_type not in ['ssq', 'dlt']:
raise HTTPException(status_code=400, detail="Invalid lottery type")
analysis_service = LotteryAnalysisService(db)
return analysis_service.analyze_mathematical_stats(lottery_type, periods)
@router.get("/missing/{lottery_type}")
def get_missing_periods(
lottery_type: str,
db: Session = Depends(get_db)
):
"""获取号码遗漏值分析"""
if lottery_type not in ['ssq', 'dlt']:
raise HTTPException(status_code=400, detail="Invalid lottery type")
analysis_service = LotteryAnalysisService(db)
return analysis_service.get_missing_periods(lottery_type)
@router.get("/smart-numbers/{lottery_type}")
def generate_smart_numbers(
lottery_type: str,
strategy: Optional[str] = 'balanced',
count: Optional[int] = 1,
periods: Optional[int] = 100,
db: Session = Depends(get_db)
):
"""智能选号
Args:
lottery_type: 彩票类型 ('ssq' 'dlt')
strategy: 选号策略 ('balanced', 'hot', 'cold', 'missing')
count: 生成注数默认1注
periods: 分析期数默认100期
"""
if lottery_type not in ['ssq', 'dlt']:
raise HTTPException(status_code=400, detail="Invalid lottery type")
if strategy not in ['balanced', 'hot', 'cold', 'missing']:
raise HTTPException(status_code=400, detail="Invalid strategy")
if count < 1 or count > 10:
raise HTTPException(
status_code=400, detail="Count must be between 1 and 10")
if periods < 50 or periods > 200:
raise HTTPException(
status_code=400, detail="Periods must be between 50 and 200")
analysis_service = LotteryAnalysisService(db)
return analysis_service.generate_smart_numbers(lottery_type, strategy, count, periods)

View File

@ -2,6 +2,7 @@ from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.core.config import settings
from app.api.v1.lottery import router as lottery_router
from app.api.endpoints.analysis import router as analysis_router
from app.core.database import Base, engine
# 创建数据库表
@ -15,7 +16,7 @@ app = FastAPI(
# 配置CORS
app.add_middleware(
CORSMiddleware,
allow_origins=settings.BACKEND_CORS_ORIGINS,
allow_origins=["*"], # 允许所有来源
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
@ -24,6 +25,8 @@ app.add_middleware(
# 注册路由
app.include_router(
lottery_router, prefix=f"{settings.API_V1_STR}/lottery", tags=["lottery"])
app.include_router(
analysis_router, prefix=f"{settings.API_V1_STR}/analysis", tags=["analysis"])
if __name__ == "__main__":
import uvicorn

View File

@ -0,0 +1,349 @@
from typing import List, Dict, Tuple, Optional
import numpy as np
from collections import defaultdict
from datetime import datetime, timedelta
from sqlalchemy.orm import Session
from ..models.lottery import SSQLottery, DLTLottery
class LotteryAnalysisService:
def __init__(self, db: Session):
self.db = db
def get_hot_cold_numbers(self, lottery_type: str, periods: int = 50) -> Dict:
"""分析热号和冷号
Args:
lottery_type: 彩票类型 ('ssq' 'dlt')
periods: 分析期数默认50期
Returns:
Dict: 包含热号和冷号的字典
"""
model = SSQLottery if lottery_type == 'ssq' else DLTLottery
recent_draws = self.db.query(model).order_by(
model.open_time.desc()).limit(periods).all()
# 初始化号码频率统计
red_freq = defaultdict(int)
blue_freq = defaultdict(int)
# 统计号码出现频率
for draw in recent_draws:
if lottery_type == 'ssq':
red_numbers = [draw.red_ball_1, draw.red_ball_2, draw.red_ball_3,
draw.red_ball_4, draw.red_ball_5, draw.red_ball_6]
for num in red_numbers:
red_freq[num] += 1
blue_freq[draw.blue_ball] += 1
else:
red_numbers = [draw.front_ball_1, draw.front_ball_2, draw.front_ball_3,
draw.front_ball_4, draw.front_ball_5]
for num in red_numbers:
red_freq[num] += 1
blue_freq[draw.back_ball_1] += 1
blue_freq[draw.back_ball_2] += 1
# 计算平均出现次数
red_avg = sum(red_freq.values()) / len(red_freq)
blue_avg = sum(blue_freq.values()) / len(blue_freq)
# 定义热号和冷号
hot_reds = [num for num, freq in red_freq.items() if freq > red_avg]
cold_reds = [num for num, freq in red_freq.items() if freq < red_avg]
hot_blues = [num for num, freq in blue_freq.items() if freq > blue_avg]
cold_blues = [num for num, freq in blue_freq.items()
if freq < blue_avg]
return {
'hot_reds': sorted(hot_reds),
'cold_reds': sorted(cold_reds),
'hot_blues': sorted(hot_blues),
'cold_blues': sorted(cold_blues),
'red_frequencies': dict(red_freq),
'blue_frequencies': dict(blue_freq)
}
def analyze_number_distribution(self, lottery_type: str, periods: int = 100) -> Dict:
"""分析号码分布(分区统计、奇偶比)
Args:
lottery_type: 彩票类型 ('ssq' 'dlt')
periods: 分析期数默认100期
Returns:
Dict: 包含各种分布统计的字典
"""
model = SSQLottery if lottery_type == 'ssq' else DLTLottery
recent_draws = self.db.query(model).order_by(
model.open_time.desc()).limit(periods).all()
# 初始化统计数据
zone_stats = defaultdict(int)
odd_count = 0
even_count = 0
total_numbers = 0
for draw in recent_draws:
if lottery_type == 'ssq':
red_numbers = [draw.red_ball_1, draw.red_ball_2, draw.red_ball_3,
draw.red_ball_4, draw.red_ball_5, draw.red_ball_6]
else:
red_numbers = [draw.front_ball_1, draw.front_ball_2, draw.front_ball_3,
draw.front_ball_4, draw.front_ball_5]
# 分区统计
for num in red_numbers:
# 对于双色球每5个数字一个区最后一个区是31-33
# 对于大乐透每5个数字一个区最后一个区是31-35
zone = (num - 1) // 5 + 1
zone_stats[zone] += 1
# 奇偶统计
if num % 2 == 0:
even_count += 1
else:
odd_count += 1
total_numbers += 1
# 计算比例
zone_distribution = {str(zone): count for zone,
count in zone_stats.items()}
odd_even_distribution = {
'odd': odd_count,
'even': even_count
}
return {
'zone_distribution': zone_distribution,
'odd_even_distribution': odd_even_distribution
}
def analyze_consecutive_numbers(self, lottery_type: str, periods: int = 100) -> Dict:
"""分析连号和重复号
Args:
lottery_type: 彩票类型 ('ssq' 'dlt')
periods: 分析期数默认100期
Returns:
Dict: 包含连号和重复号分析的字典
"""
model = SSQLottery if lottery_type == 'ssq' else DLTLottery
recent_draws = self.db.query(model).order_by(
model.open_time.desc()).limit(periods).all()
consecutive_stats = defaultdict(int)
repeat_stats = defaultdict(int)
prev_numbers = None
for draw in recent_draws:
if lottery_type == 'ssq':
current_numbers = sorted([
draw.red_ball_1, draw.red_ball_2, draw.red_ball_3,
draw.red_ball_4, draw.red_ball_5, draw.red_ball_6
])
else:
current_numbers = sorted([
draw.front_ball_1, draw.front_ball_2, draw.front_ball_3,
draw.front_ball_4, draw.front_ball_5
])
# 分析连号
consecutive_count = 0
for i in range(len(current_numbers)-1):
if current_numbers[i+1] - current_numbers[i] == 1:
consecutive_count += 1
consecutive_stats[consecutive_count] += 1
# 分析重复号
if prev_numbers:
repeat_count = len(set(current_numbers) & set(prev_numbers))
repeat_stats[repeat_count] += 1
prev_numbers = current_numbers
total_draws = len(recent_draws)
return {
'consecutive_distribution': {k: v/total_draws for k, v in consecutive_stats.items()},
'repeat_distribution': {k: v/(total_draws-1) for k, v in repeat_stats.items()}
}
def analyze_mathematical_stats(self, lottery_type: str, periods: int = 100) -> Dict:
"""分析数学统计特征
Args:
lottery_type: 彩票类型 ('ssq' 'dlt')
periods: 分析期数默认100期
Returns:
Dict: 包含数学统计特征的字典
"""
model = SSQLottery if lottery_type == 'ssq' else DLTLottery
recent_draws = self.db.query(model).order_by(
model.open_time.desc()).limit(periods).all()
sums = []
for draw in recent_draws:
if lottery_type == 'ssq':
red_numbers = [draw.red_ball_1, draw.red_ball_2, draw.red_ball_3,
draw.red_ball_4, draw.red_ball_5, draw.red_ball_6]
else:
red_numbers = [draw.front_ball_1, draw.front_ball_2, draw.front_ball_3,
draw.front_ball_4, draw.front_ball_5]
sums.append(sum(red_numbers))
mean = np.mean(sums)
std = np.std(sums)
return {
'sum_mean': float(mean),
'sum_std': float(std),
'sum_range': {
'min': float(mean - 2*std),
'max': float(mean + 2*std)
}
}
def get_missing_periods(self, lottery_type: str, periods: int = 200) -> Dict:
"""分析号码遗漏值仅分析最近periods期避免全表遍历
Args:
lottery_type: 彩票类型 ('ssq' 'dlt')
periods: 分析期数默认200期
Returns:
Dict: 包含各号码当前遗漏值的字典
"""
model = SSQLottery if lottery_type == 'ssq' else DLTLottery
max_red = 33 if lottery_type == 'ssq' else 35
max_blue = 16 if lottery_type == 'ssq' else 12
# 一次性查出最近periods期数据
draws = self.db.query(model).order_by(
model.open_time.desc()).limit(periods).all()
# 初始化遗漏值字典
red_missing = {i: 0 for i in range(1, max_red + 1)}
blue_missing = {i: 0 for i in range(1, max_blue + 1)}
# 标记号码是否已出现
red_found = {i: False for i in range(1, max_red + 1)}
blue_found = {i: False for i in range(1, max_blue + 1)}
for idx, draw in enumerate(draws):
if lottery_type == 'ssq':
red_numbers = [draw.red_ball_1, draw.red_ball_2, draw.red_ball_3,
draw.red_ball_4, draw.red_ball_5, draw.red_ball_6]
blue_numbers = [draw.blue_ball]
else:
red_numbers = [draw.front_ball_1, draw.front_ball_2, draw.front_ball_3,
draw.front_ball_4, draw.front_ball_5]
blue_numbers = [draw.back_ball_1, draw.back_ball_2]
# 红球遗漏统计
for i in range(1, max_red + 1):
if not red_found[i]:
if i in red_numbers:
red_found[i] = True
red_missing[i] = idx
# 蓝球遗漏统计
for i in range(1, max_blue + 1):
if not blue_found[i]:
if i in blue_numbers:
blue_found[i] = True
blue_missing[i] = idx
# 未出现过的号码遗漏值为periods
for i in range(1, max_red + 1):
if not red_found[i]:
red_missing[i] = periods
for i in range(1, max_blue + 1):
if not blue_found[i]:
blue_missing[i] = periods
return {
'red_missing': red_missing,
'blue_missing': blue_missing
}
def generate_smart_numbers(self, lottery_type: str, strategy: str = 'balanced', count: int = 1, periods: int = 100) -> List[Dict]:
"""智能选号
Args:
lottery_type: 彩票类型 ('ssq' 'dlt')
strategy: 选号策略 ('balanced', 'hot', 'cold', 'missing')
count: 生成注数
periods: 分析期数默认100期
Returns:
List[Dict]: 生成的号码列表
"""
import random
# 获取分析数据
hot_cold = self.get_hot_cold_numbers(lottery_type, periods)
distribution = self.analyze_number_distribution(lottery_type, periods)
missing = self.get_missing_periods(lottery_type, periods)
max_red = 33 if lottery_type == 'ssq' else 35
max_blue = 16 if lottery_type == 'ssq' else 12
red_count = 6 if lottery_type == 'ssq' else 5
blue_count = 1 if lottery_type == 'ssq' else 2
results = []
for _ in range(count):
# 根据策略选择红球
if strategy == 'hot':
red_pool = hot_cold['hot_reds']
elif strategy == 'cold':
red_pool = hot_cold['cold_reds']
elif strategy == 'missing':
red_pool = sorted(missing['red_missing'].items(),
key=lambda x: x[1], reverse=True)
red_pool = [num for num, _ in red_pool[:max_red//2]]
else: # balanced
red_pool = list(range(1, max_red + 1))
# 确保红球池不为空且数量足够
if not red_pool or len(red_pool) < red_count:
red_pool = list(range(1, max_red + 1))
# 选择红球
try:
red_numbers = sorted(random.sample(red_pool, red_count))
except ValueError:
# 如果采样失败,使用全范围随机
red_numbers = sorted(random.sample(
range(1, max_red + 1), red_count))
# 选择蓝球
if strategy == 'hot':
blue_pool = hot_cold['hot_blues']
elif strategy == 'cold':
blue_pool = hot_cold['cold_blues']
elif strategy == 'missing':
blue_pool = sorted(missing['blue_missing'].items(),
key=lambda x: x[1], reverse=True)
blue_pool = [num for num, _ in blue_pool[:max_blue//2]]
else: # balanced
blue_pool = list(range(1, max_blue + 1))
# 确保蓝球池不为空且数量足够
if not blue_pool or len(blue_pool) < blue_count:
blue_pool = list(range(1, max_blue + 1))
# 选择蓝球
try:
blue_numbers = sorted(random.sample(blue_pool, blue_count))
except ValueError:
# 如果采样失败,使用全范围随机
blue_numbers = sorted(random.sample(
range(1, max_blue + 1), blue_count))
results.append({
'red_numbers': red_numbers,
'blue_numbers': blue_numbers
})
return results

26
backend/main.py Normal file
View File

@ -0,0 +1,26 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api.endpoints import analysis
app = FastAPI(
title="彩票数据分析系统",
description="支持双色球和大乐透的数据管理、统计分析和智能选号功能",
version="1.0.0"
)
# 配置CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
app.include_router(analysis.router, prefix="/api/analysis", tags=["analysis"])
@app.get("/")
def read_root():
return {"message": "欢迎使用彩票数据分析系统"}

View File

@ -88,7 +88,7 @@ onUnmounted(() => {
font-size: 16px;
font-weight: 500;
}
@media screen and (max-width: 768px) {
@media screen and (max-width: 1024px) {
.el-header {
flex-direction: row;
height: 50px;

View File

@ -1,4 +1,5 @@
import axios from 'axios'
import request from '../utils/request'
const api = axios.create({
baseURL: '/api/v1/lottery'
@ -89,5 +90,67 @@ export const lotteryApi = {
generateDLTNumbers(params) {
return api.get('/dlt/generate', { params })
},
// 获取热号冷号分析
getHotColdNumbers(lotteryType, params) {
return request({
url: `/api/v1/analysis/hot-cold/${lotteryType}`,
method: 'get',
params
})
},
// 获取号码分布分析
getNumberDistribution(lotteryType, params) {
return request({
url: `/api/v1/analysis/distribution/${lotteryType}`,
method: 'get',
params
})
},
// 获取连号分析
getConsecutiveAnalysis(lotteryType, params) {
return request({
url: `/api/v1/analysis/consecutive/${lotteryType}`,
method: 'get',
params
})
},
// 获取数学统计特征
getMathematicalStats(lotteryType, params) {
return request({
url: `/api/v1/analysis/math-stats/${lotteryType}`,
method: 'get',
params
})
},
// 获取遗漏值分析
getMissingPeriods(lotteryType) {
return request({
url: `/api/v1/analysis/missing/${lotteryType}`,
method: 'get'
})
},
// 智能选号
generateSmartNumbers(lotteryType, params) {
return request({
url: `/api/v1/analysis/smart-numbers/${lotteryType}`,
method: 'get',
params: {
strategy: params.strategy || 'balanced',
count: params.count || 1,
periods: params.periods || 100
}
}).then(response => {
if (response && response.data) {
return response
}
throw new Error('返回数据格式错误')
})
}
}

View File

@ -0,0 +1,40 @@
import axios from 'axios'
import { ElMessage } from 'element-plus'
// 创建axios实例
const service = axios.create({
baseURL: 'http://localhost:8000', // API的基础URL
timeout: 15000 // 请求超时时间
})
// 请求拦截器
service.interceptors.request.use(
config => {
// 在发送请求之前做些什么
return config
},
error => {
// 对请求错误做些什么
console.error('Request error:', error)
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
// FastAPI 返回的数据在 response.data 中
return response
},
error => {
console.error('Response error:', error)
ElMessage({
message: error.message || 'Request failed',
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service

File diff suppressed because it is too large Load Diff