diff --git a/README.md b/README.md index a30647a..4d6092c 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,11 @@ - Python 3.8+ - FastAPI - SQLAlchemy -- PostgreSQL +- MySQL/PostgreSQL - Pydantic +- Scikit-learn (机器学习) +- NumPy (数值计算) +- Pandas (数据处理) ### 前端 - Vue 3 @@ -20,23 +23,42 @@ - Vue Router ## 系统功能 -1. 数据管理 - - 双色球和大乐透历史数据导入 - - 数据查询和筛选 - - 数据导出 - - 手动数据录入 -2. 统计分析 - - 号码出现频率统计 - - 热门号码分析 - - 冷门号码分析 - - 数据可视化展示 +### 1. 数据管理 +- 双色球和大乐透历史数据导入 +- 数据查询和筛选 +- 数据导出 +- 手动数据录入 +- 自动数据更新 -3. 智能选号 - - 随机选号 - - 频率选号 - - 热门号码选号 - - 冷门号码选号 +### 2. 基础统计分析 +- 号码出现频率统计 +- 热门号码分析 +- 冷门号码分析 +- 数据可视化展示 + +### 3. 高级数据分析 ⭐ 新功能 +- **遗漏值分析**: 分析各号码的遗漏期数 +- **和值分析**: 统计红球和值的分布规律 +- **AC值分析**: 邻号差值分析 +- **质合比分析**: 质数与合数的比例分析 +- **012路分析**: 除3余数分析 +- **跨度分析**: 最大最小号码差值分析 +- **综合分析**: 多维度数据整合分析 + +### 4. 智能预测系统 ⭐ 新功能 +- **机器学习预测**: 基于历史数据的AI预测 +- **模式预测**: 基于统计模式的预测 +- **集成预测**: 多方法综合预测 +- **预测模型训练**: 可自定义训练参数 +- **预测结果评估**: 预测准确率统计 + +### 5. 智能选号 +- 随机选号 +- 频率选号 +- 热门号码选号 +- 冷门号码选号 +- 自定义选号策略 ## 项目结构 ``` @@ -44,10 +66,15 @@ lottery/ ├── backend/ # 后端代码 │ ├── app/ │ │ ├── api/ # API 路由 +│ │ │ ├── endpoints/ # 基础API端点 +│ │ │ └── v1/ # API版本1 │ │ ├── core/ # 核心配置 │ │ ├── models/ # 数据模型 │ │ ├── schemas/ # 数据验证 │ │ └── services/ # 业务逻辑 +│ │ ├── analysis_service.py # 基础分析服务 +│ │ ├── advanced_analysis.py # 高级分析服务 ⭐ +│ │ └── prediction_service.py # 预测服务 ⭐ │ ├── requirements.txt # 依赖包 │ └── main.py # 入口文件 ├── frontend/ # 前端代码 @@ -57,6 +84,8 @@ lottery/ │ │ ├── components/ # 组件 │ │ ├── router/ # 路由配置 │ │ ├── views/ # 页面 +│ │ │ ├── AdvancedAnalysis.vue # 高级分析页面 ⭐ +│ │ │ └── Prediction.vue # 预测页面 ⭐ │ │ ├── App.vue # 根组件 │ │ └── main.js # 入口文件 │ ├── package.json # 依赖配置 @@ -67,7 +96,7 @@ lottery/ ## 开发环境要求 - Python 3.8+ - Node.js 16+ -- PostgreSQL 12+ +- MySQL 8.0+ 或 PostgreSQL 12+ - npm 或 yarn ## 安装和运行 @@ -87,7 +116,7 @@ pip install -r requirements.txt ``` 3. 配置数据库 -- 创建 PostgreSQL 数据库 +- 创建 MySQL/PostgreSQL 数据库 - 修改 `backend/app/core/database.py` 中的数据库连接配置 4. 启动服务 @@ -142,9 +171,30 @@ python schedule_update.py ``` 系统会在每天凌晨2点自动检查并更新数据。 -#### 其他说明 -- 首页、API、前端等所有"最新开奖"展示均以 `open_time` 最大值为准,保证数据准确。 -- 数据库不会因期号异常导致遗漏或重复,所有唯一性、顺序均以开奖日期为核心。 +### 高级分析功能 ⭐ +1. 进入"高级分析"页面 +2. 选择彩票类型(双色球/大乐透) +3. 选择分析类型: + - **遗漏值分析**: 查看各号码的遗漏期数 + - **和值分析**: 分析红球和值的分布规律 + - **AC值分析**: 邻号差值分析 + - **质合比分析**: 质数与合数比例 + - **012路分析**: 除3余数分布 + - **跨度分析**: 号码跨度统计 + - **综合分析**: 多维度整合分析 +4. 设置分析期数(10-500期) +5. 点击"分析"按钮查看结果 + +### 智能预测功能 ⭐ +1. 进入"智能预测"页面 +2. 选择彩票类型 +3. 设置训练期数(建议100-500期) +4. 点击"训练模型"按钮 +5. 训练完成后,可选择以下预测方法: + - **机器学习预测**: 基于AI算法的预测 + - **模式预测**: 基于统计模式的预测 + - **集成预测**: 多方法综合预测 +6. 查看预测结果和置信度 ### 数据查询 1. 在查询表单中输入查询条件 @@ -167,6 +217,23 @@ python schedule_update.py - Swagger UI: http://localhost:8000/docs - ReDoc: http://localhost:8000/redoc +### 新增API端点 ⭐ + +#### 高级分析API +- `GET /api/v1/advanced-analysis/missing-value/{lottery_type}` - 遗漏值分析 +- `GET /api/v1/advanced-analysis/sum-value/{lottery_type}` - 和值分析 +- `GET /api/v1/advanced-analysis/ac-value/{lottery_type}` - AC值分析 +- `GET /api/v1/advanced-analysis/prime-composite/{lottery_type}` - 质合比分析 +- `GET /api/v1/advanced-analysis/road-012/{lottery_type}` - 012路分析 +- `GET /api/v1/advanced-analysis/span/{lottery_type}` - 跨度分析 +- `GET /api/v1/advanced-analysis/comprehensive/{lottery_type}` - 综合分析 + +#### 预测API +- `POST /api/v1/prediction/train/{lottery_type}` - 训练预测模型 +- `GET /api/v1/prediction/predict/{lottery_type}` - 机器学习预测 +- `GET /api/v1/prediction/pattern/{lottery_type}` - 模式预测 +- `GET /api/v1/prediction/ensemble/{lottery_type}` - 集成预测 + ## 常见问题 1. 数据库连接失败 - 检查数据库服务是否启动 @@ -180,12 +247,21 @@ python schedule_update.py - 检查 JSON 文件格式是否正确 - 确认数据库表结构是否完整 +4. 机器学习预测失败 + - 确保历史数据充足(建议至少100期) + - 检查训练参数设置是否合理 + ## 开发计划 +- [x] 添加高级数据分析功能 +- [x] 实现机器学习预测系统 +- [x] 优化数据可视化展示 - [ ] 添加用户认证功能 - [ ] 实现数据备份和恢复 - [ ] 优化数据导入性能 - [ ] 添加更多统计分析功能 - [ ] 实现自定义选号策略 +- [ ] 添加移动端适配 +- [ ] 实现数据导出功能增强 ## 贡献指南 1. Fork 项目 @@ -197,84 +273,20 @@ python schedule_update.py ## 许可证 MIT License -## 数据更新功能 +## 更新日志 -系统支持自动从聚合数据API获取最新的开奖数据并更新到本地数据库。更新功能包括: +### v2.0.0 (2024-01-XX) +- ✨ 新增高级数据分析功能 +- ✨ 新增机器学习预测系统 +- ✨ 新增遗漏值、和值、AC值等分析 +- ✨ 新增质合比、012路、跨度分析 +- ✨ 优化数据可视化展示 +- 🔧 更新依赖包版本 +- 📝 完善API文档 -1. 手动更新 - ```bash - cd backend - python update_lottery.py - ``` - -2. 自动更新 - ```bash - cd backend - python schedule_update.py - ``` - 系统会在每天凌晨2点自动检查并更新数据。 - -### 数据更新说明 - -- 系统会自动检查本地数据库中最新的开奖日期 -- 只获取并更新比本地数据更新的开奖记录 -- 更新过程会记录日志到 `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 -``` \ No newline at end of file +### v1.0.0 (2024-01-XX) +- 🎉 初始版本发布 +- ✨ 基础数据管理功能 +- ✨ 基础统计分析 +- ✨ 智能选号功能 +- ✨ 前后端分离架构 \ No newline at end of file diff --git a/backend/app/api/endpoints/advanced_analysis.py b/backend/app/api/endpoints/advanced_analysis.py new file mode 100644 index 0000000..1d440d5 --- /dev/null +++ b/backend/app/api/endpoints/advanced_analysis.py @@ -0,0 +1,105 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session +from typing import Dict +from ...core.database import get_db +from ...services.advanced_analysis import AdvancedAnalysisService + +router = APIRouter() + + +@router.get("/missing-value/{lottery_type}") +def get_missing_value_analysis( + lottery_type: str, + periods: int = 100, + db: Session = Depends(get_db) +): + """获取遗漏值分析""" + if lottery_type not in ['ssq', 'dlt']: + raise HTTPException(status_code=400, detail="Invalid lottery type") + + service = AdvancedAnalysisService(db) + return service.get_missing_value_analysis(lottery_type, periods) + + +@router.get("/sum-value/{lottery_type}") +def get_sum_value_analysis( + lottery_type: str, + periods: int = 100, + db: Session = Depends(get_db) +): + """获取和值分析""" + if lottery_type not in ['ssq', 'dlt']: + raise HTTPException(status_code=400, detail="Invalid lottery type") + + service = AdvancedAnalysisService(db) + return service.get_sum_value_analysis(lottery_type, periods) + + +@router.get("/ac-value/{lottery_type}") +def get_ac_value_analysis( + lottery_type: str, + periods: int = 100, + db: Session = Depends(get_db) +): + """获取AC值分析""" + if lottery_type not in ['ssq', 'dlt']: + raise HTTPException(status_code=400, detail="Invalid lottery type") + + service = AdvancedAnalysisService(db) + return service.get_ac_value_analysis(lottery_type, periods) + + +@router.get("/prime-composite/{lottery_type}") +def get_prime_composite_analysis( + lottery_type: str, + periods: int = 100, + db: Session = Depends(get_db) +): + """获取质合比分析""" + if lottery_type not in ['ssq', 'dlt']: + raise HTTPException(status_code=400, detail="Invalid lottery type") + + service = AdvancedAnalysisService(db) + return service.get_prime_composite_analysis(lottery_type, periods) + + +@router.get("/road-012/{lottery_type}") +def get_road_012_analysis( + lottery_type: str, + periods: int = 100, + db: Session = Depends(get_db) +): + """获取012路分析""" + if lottery_type not in ['ssq', 'dlt']: + raise HTTPException(status_code=400, detail="Invalid lottery type") + + service = AdvancedAnalysisService(db) + return service.get_road_012_analysis(lottery_type, periods) + + +@router.get("/span/{lottery_type}") +def get_span_analysis( + lottery_type: str, + periods: int = 100, + db: Session = Depends(get_db) +): + """获取跨度分析""" + if lottery_type not in ['ssq', 'dlt']: + raise HTTPException(status_code=400, detail="Invalid lottery type") + + service = AdvancedAnalysisService(db) + return service.get_span_analysis(lottery_type, periods) + + +@router.get("/comprehensive/{lottery_type}") +def get_comprehensive_analysis( + lottery_type: str, + periods: int = 100, + db: Session = Depends(get_db) +): + """获取综合分析""" + if lottery_type not in ['ssq', 'dlt']: + raise HTTPException(status_code=400, detail="Invalid lottery type") + + service = AdvancedAnalysisService(db) + return service.get_comprehensive_analysis(lottery_type, periods) diff --git a/backend/app/api/endpoints/prediction.py b/backend/app/api/endpoints/prediction.py new file mode 100644 index 0000000..074267a --- /dev/null +++ b/backend/app/api/endpoints/prediction.py @@ -0,0 +1,63 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session +from typing import Dict +from ...core.database import get_db +from ...services.prediction_service import PredictionService + +router = APIRouter() + + +@router.post("/train/{lottery_type}") +def train_prediction_model( + lottery_type: str, + periods: int = 100, + db: Session = Depends(get_db) +): + """训练预测模型""" + if lottery_type not in ['ssq', 'dlt']: + raise HTTPException(status_code=400, detail="Invalid lottery type") + + service = PredictionService(db) + return service.train_model(lottery_type, periods) + + +@router.get("/predict/{lottery_type}") +def predict_next_numbers( + lottery_type: str, + periods: int = 10, + db: Session = Depends(get_db) +): + """预测下一期号码""" + if lottery_type not in ['ssq', 'dlt']: + raise HTTPException(status_code=400, detail="Invalid lottery type") + + service = PredictionService(db) + return service.predict_next_numbers(lottery_type, periods) + + +@router.get("/pattern/{lottery_type}") +def get_pattern_prediction( + lottery_type: str, + periods: int = 100, + db: Session = Depends(get_db) +): + """基于模式的预测""" + if lottery_type not in ['ssq', 'dlt']: + raise HTTPException(status_code=400, detail="Invalid lottery type") + + service = PredictionService(db) + return service.get_pattern_based_prediction(lottery_type, periods) + + +@router.get("/ensemble/{lottery_type}") +def get_ensemble_prediction( + lottery_type: str, + periods: int = 100, + db: Session = Depends(get_db) +): + """集成预测""" + if lottery_type not in ['ssq', 'dlt']: + raise HTTPException(status_code=400, detail="Invalid lottery type") + + service = PredictionService(db) + return service.get_ensemble_prediction(lottery_type, periods) diff --git a/backend/app/main.py b/backend/app/main.py index dbb17ec..520df1d 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -3,6 +3,8 @@ 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.api.endpoints.advanced_analysis import router as advanced_analysis_router +from app.api.endpoints.prediction import router as prediction_router from app.core.database import Base, engine # 创建数据库表 @@ -27,6 +29,10 @@ 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"]) +app.include_router( + advanced_analysis_router, prefix=f"{settings.API_V1_STR}/advanced-analysis", tags=["advanced-analysis"]) +app.include_router( + prediction_router, prefix=f"{settings.API_V1_STR}/prediction", tags=["prediction"]) if __name__ == "__main__": import uvicorn diff --git a/backend/app/services/advanced_analysis.py b/backend/app/services/advanced_analysis.py new file mode 100644 index 0000000..cabd3a3 --- /dev/null +++ b/backend/app/services/advanced_analysis.py @@ -0,0 +1,201 @@ +from typing import Dict +import numpy as np +from collections import defaultdict +from sqlalchemy.orm import Session +from ..models.lottery import SSQLottery, DLTLottery + + +class AdvancedAnalysisService: + def __init__(self, db: Session): + self.db = db + + def get_missing_value_analysis(self, lottery_type: str, periods: int = 100) -> Dict: + model = SSQLottery if lottery_type == 'ssq' else DLTLottery + recent_draws = self.db.query(model).order_by( + model.open_time.desc()).limit(periods).all() + if lottery_type == 'ssq': + red_range = 33 + blue_range = 16 + else: + red_range = 35 + blue_range = 12 + red_missing = {i: 0 for i in range(1, red_range + 1)} + blue_missing = {i: 0 for i in range(1, blue_range + 1)} + for draw in reversed(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] + 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 num in range(1, red_range + 1): + if num not in red_numbers: + red_missing[num] += 1 + for num in range(1, blue_range + 1): + if num not in blue_numbers: + blue_missing[num] += 1 + return { + 'red_missing': red_missing, + 'blue_missing': blue_missing, + 'max_red_missing': max(red_missing.values()), + 'max_blue_missing': max(blue_missing.values()), + 'avg_red_missing': sum(red_missing.values()) / len(red_missing), + 'avg_blue_missing': sum(blue_missing.values()) / len(blue_missing) + } + + def get_sum_value_analysis(self, lottery_type: str, periods: int = 100) -> 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)) + sum_distribution = defaultdict(int) + for s in sums: + sum_distribution[s] += 1 + return { + 'sums': sums, + 'sum_distribution': dict(sum_distribution), + 'min_sum': min(sums), + 'max_sum': max(sums), + 'avg_sum': float(np.mean(sums)), + 'median_sum': float(np.median(sums)), + 'std_sum': float(np.std(sums)), + 'most_common_sums': sorted(sum_distribution.items(), key=lambda x: x[1], reverse=True)[:10] + } + + def get_ac_value_analysis(self, lottery_type: str, periods: int = 100) -> Dict: + model = SSQLottery if lottery_type == 'ssq' else DLTLottery + recent_draws = self.db.query(model).order_by( + model.open_time.desc()).limit(periods).all() + ac_values = [] + for draw in recent_draws: + if lottery_type == 'ssq': + red_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: + red_numbers = sorted([draw.front_ball_1, draw.front_ball_2, draw.front_ball_3, + draw.front_ball_4, draw.front_ball_5]) + ac = sum(abs(red_numbers[i+1] - red_numbers[i]) + for i in range(len(red_numbers)-1)) + ac_values.append(ac) + ac_distribution = defaultdict(int) + for ac in ac_values: + ac_distribution[ac] += 1 + return { + 'ac_values': ac_values, + 'ac_distribution': dict(ac_distribution), + 'min_ac': min(ac_values), + 'max_ac': max(ac_values), + 'avg_ac': float(np.mean(ac_values)), + 'median_ac': float(np.median(ac_values)), + 'std_ac': float(np.std(ac_values)), + 'most_common_ac': sorted(ac_distribution.items(), key=lambda x: x[1], reverse=True)[:10] + } + + def get_prime_composite_analysis(self, lottery_type: str, periods: int = 100) -> Dict: + def is_prime(n): + if n < 2: + return False + for i in range(2, int(n**0.5) + 1): + if n % i == 0: + return False + return True + model = SSQLottery if lottery_type == 'ssq' else DLTLottery + recent_draws = self.db.query(model).order_by( + model.open_time.desc()).limit(periods).all() + prime_ratios = [] + 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] + prime_count = sum(1 for num in red_numbers if is_prime(num)) + composite_count = len(red_numbers) - prime_count + prime_ratios.append(f"{prime_count}:{composite_count}") + ratio_distribution = defaultdict(int) + for ratio in prime_ratios: + ratio_distribution[ratio] += 1 + return { + 'prime_ratios': prime_ratios, + 'ratio_distribution': dict(ratio_distribution), + 'most_common_ratios': sorted(ratio_distribution.items(), key=lambda x: x[1], reverse=True)[:10] + } + + def get_road_012_analysis(self, lottery_type: str, periods: int = 100) -> Dict: + model = SSQLottery if lottery_type == 'ssq' else DLTLottery + recent_draws = self.db.query(model).order_by( + model.open_time.desc()).limit(periods).all() + road_012_counts = [] + 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] + road_0 = sum(1 for num in red_numbers if num % 3 == 0) + road_1 = sum(1 for num in red_numbers if num % 3 == 1) + road_2 = sum(1 for num in red_numbers if num % 3 == 2) + road_012_counts.append({ + 'road_0': road_0, + 'road_1': road_1, + 'road_2': road_2, + 'pattern': f"{road_0}:{road_1}:{road_2}" + }) + pattern_distribution = defaultdict(int) + for count in road_012_counts: + pattern_distribution[count['pattern']] += 1 + return { + 'road_012_counts': road_012_counts, + 'pattern_distribution': dict(pattern_distribution), + 'most_common_patterns': sorted(pattern_distribution.items(), key=lambda x: x[1], reverse=True)[:10] + } + + def get_span_analysis(self, lottery_type: str, periods: int = 100) -> Dict: + model = SSQLottery if lottery_type == 'ssq' else DLTLottery + recent_draws = self.db.query(model).order_by( + model.open_time.desc()).limit(periods).all() + spans = [] + 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] + span = max(red_numbers) - min(red_numbers) + spans.append(span) + span_distribution = defaultdict(int) + for span in spans: + span_distribution[span] += 1 + return { + 'spans': spans, + 'span_distribution': dict(span_distribution), + 'min_span': min(spans), + 'max_span': max(spans), + 'avg_span': float(np.mean(spans)), + 'median_span': float(np.median(spans)), + 'std_span': float(np.std(spans)), + 'most_common_spans': sorted(span_distribution.items(), key=lambda x: x[1], reverse=True)[:10] + } + + def get_comprehensive_analysis(self, lottery_type: str, periods: int = 100) -> Dict: + return { + 'missing_value': self.get_missing_value_analysis(lottery_type, periods), + 'sum_value': self.get_sum_value_analysis(lottery_type, periods), + 'ac_value': self.get_ac_value_analysis(lottery_type, periods), + 'prime_composite': self.get_prime_composite_analysis(lottery_type, periods), + 'road_012': self.get_road_012_analysis(lottery_type, periods), + 'span': self.get_span_analysis(lottery_type, periods) + } diff --git a/backend/app/services/prediction_service.py b/backend/app/services/prediction_service.py new file mode 100644 index 0000000..4b5e5c8 --- /dev/null +++ b/backend/app/services/prediction_service.py @@ -0,0 +1,535 @@ +from typing import List, Dict, Tuple, Optional +import numpy as np +import pandas as pd +from sklearn.ensemble import RandomForestRegressor +from sklearn.preprocessing import StandardScaler +from sklearn.model_selection import train_test_split +from collections import defaultdict +from sqlalchemy.orm import Session +from ..models.lottery import SSQLottery, DLTLottery + + +class PredictionService: + # 类级别的字典来存储所有模型 + _models = {} + _scalers = {} + + def __init__(self, db: Session): + self.db = db + + def prepare_features(self, lottery_type: str, periods: int = 100) -> Tuple[np.ndarray, np.ndarray]: + """准备机器学习特征 + + Args: + lottery_type: 彩票类型 ('ssq' 或 'dlt') + periods: 使用期数 + + Returns: + Tuple: (特征矩阵, 标签矩阵) + """ + model = SSQLottery if lottery_type == 'ssq' else DLTLottery + recent_draws = self.db.query(model).order_by( + model.open_time.desc()).limit(periods).all() + + features = [] + labels = [] + + for i in range(len(recent_draws) - 10): # 使用前10期预测下一期 + # 特征:前10期的号码 + feature_row = [] + for j in range(10): + draw = recent_draws[i + j] + if lottery_type == 'ssq': + 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] + feature_row.extend(sorted(numbers)) + feature_row.append(draw.blue_ball) # 添加蓝球 + else: + numbers = [draw.front_ball_1, draw.front_ball_2, draw.front_ball_3, + draw.front_ball_4, draw.front_ball_5] + feature_row.extend(sorted(numbers)) + feature_row.extend( + [draw.back_ball_1, draw.back_ball_2]) # 添加后区号码 + + # 标签:下一期的号码 + next_draw = recent_draws[i + 10] + if lottery_type == 'ssq': + label_numbers = [next_draw.red_ball_1, next_draw.red_ball_2, next_draw.red_ball_3, + next_draw.red_ball_4, next_draw.red_ball_5, next_draw.red_ball_6] + label_numbers.append(next_draw.blue_ball) # 添加蓝球 + else: + label_numbers = [next_draw.front_ball_1, next_draw.front_ball_2, next_draw.front_ball_3, + next_draw.front_ball_4, next_draw.front_ball_5] + label_numbers.extend( + [next_draw.back_ball_1, next_draw.back_ball_2]) # 添加后区号码 + + features.append(feature_row) + # 保持红球排序,蓝球位置不变 + labels.append(sorted(label_numbers[:-1]) + [label_numbers[-1]]) + + return np.array(features), np.array(labels) + + def train_model(self, lottery_type: str, periods: int = 100) -> Dict: + """训练预测模型 + + Args: + lottery_type: 彩票类型 ('ssq' 或 'dlt') + periods: 使用期数 + + Returns: + Dict: 训练结果 + """ + try: + features, labels = self.prepare_features(lottery_type, periods) + + if len(features) < 20: # 数据不足 + return {"success": False, "message": "数据不足,无法训练模型"} + + # 标准化特征 + scaler = StandardScaler() + features_scaled = scaler.fit_transform(features) + self._scalers[lottery_type] = scaler + + # 为每个号码位置训练一个模型 + models = {} + accuracies = [] + + for pos in range(labels.shape[1]): + model = RandomForestRegressor( + n_estimators=100, random_state=42) + X_train, X_test, y_train, y_test = train_test_split( + features_scaled, labels[:, pos], test_size=0.2, random_state=42 + ) + model.fit(X_train, y_train) + accuracy = model.score(X_test, y_test) + + models[f"pos_{pos}"] = model + accuracies.append(accuracy) + + self._models[lottery_type] = models + + return { + "success": True, + "message": "模型训练成功", + "avg_accuracy": np.mean(accuracies), + "accuracies": accuracies, + "training_samples": len(features) + } + + except Exception as e: + return {"success": False, "message": f"训练失败: {str(e)}"} + + def predict_next_numbers(self, lottery_type: str, periods: int = 10) -> Dict: + """预测下一期号码 + + Args: + lottery_type: 彩票类型 ('ssq' 或 'dlt') + periods: 使用期数 + + Returns: + Dict: 预测结果 + """ + if lottery_type not in self._models: + return {"success": False, "message": "模型未训练,请先训练模型"} + + try: + model = SSQLottery if lottery_type == 'ssq' else DLTLottery + recent_draws = self.db.query(model).order_by( + model.open_time.desc()).limit(10).all() # 只需要最近10期 + + if len(recent_draws) < 10: + return {"success": False, "message": "历史数据不足"} + + # 准备特征 + feature_row = [] + for draw in recent_draws: + if lottery_type == 'ssq': + 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] + feature_row.extend(sorted(numbers)) + feature_row.append(draw.blue_ball) # 添加蓝球 + else: + numbers = [draw.front_ball_1, draw.front_ball_2, draw.front_ball_3, + draw.front_ball_4, draw.front_ball_5] + feature_row.extend(sorted(numbers)) + feature_row.extend( + [draw.back_ball_1, draw.back_ball_2]) # 添加后区号码 + + # 标准化特征 + scaler = self._scalers.get(lottery_type) + if not scaler: + return {"success": False, "message": "模型未训练,请先训练模型"} + feature_scaled = scaler.transform([feature_row]) + + # 预测每个位置的号码 + predictions = [] + models = self._models[lottery_type] + + for pos in range(len(models)): + model = models[f"pos_{pos}"] + pred = model.predict(feature_scaled)[0] + predictions.append(round(pred)) + + # 确保预测的号码在有效范围内 + if lottery_type == 'ssq': + max_red = 33 + max_blue = 16 + red_count = 6 + else: + max_red = 35 + max_blue = 12 + red_count = 5 + + # 分离红球和蓝球预测 + if lottery_type == 'ssq': + red_predictions = predictions[:red_count] + blue_prediction = predictions[-1] + else: + red_predictions = predictions[:red_count] + blue_predictions = predictions[red_count:] + + # 处理红球 + red_predictions = [max(1, min(max_red, p)) + for p in red_predictions] + red_predictions = sorted(list(set(red_predictions))) + + # 如果红球不够,补充随机号码 + while len(red_predictions) < red_count: + import random + new_num = random.randint(1, max_red) + if new_num not in red_predictions: + red_predictions.append(new_num) + red_predictions = sorted(red_predictions[:red_count]) + + # 处理蓝球 + if lottery_type == 'ssq': + blue_prediction = max(1, min(max_blue, blue_prediction)) + else: + blue_predictions = [max(1, min(max_blue, p)) + for p in blue_predictions] + blue_predictions = sorted(list(set(blue_predictions))) + while len(blue_predictions) < 2: + new_num = random.randint(1, max_blue) + if new_num not in blue_predictions: + blue_predictions.append(new_num) + blue_predictions = sorted(blue_predictions) + + return { + "success": True, + "predicted_numbers": red_predictions, + "predicted_blue": blue_prediction if lottery_type == 'ssq' else None, + "predicted_blues": blue_predictions if lottery_type == 'dlt' else None, + "confidence": "基于历史数据的机器学习预测" + } + + except Exception as e: + return {"success": False, "message": f"预测失败: {str(e)}"} + + def get_pattern_based_prediction(self, lottery_type: str, periods: int = 100) -> Dict: + """基于模式的预测 + + Args: + lottery_type: 彩票类型 ('ssq' 或 'dlt') + periods: 分析期数 + + 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() + + # 分析最近的开奖模式 + patterns = { + 'sum_range': [], + 'odd_even_ratio': [], + 'zone_distribution': [], + 'consecutive_count': [] + } + + for draw in recent_draws: + if lottery_type == 'ssq': + 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: + numbers = [draw.front_ball_1, draw.front_ball_2, draw.front_ball_3, + draw.front_ball_4, draw.front_ball_5] + + # 和值 + patterns['sum_range'].append(sum(numbers)) + + # 奇偶比 + odd_count = sum(1 for n in numbers if n % 2 == 1) + patterns['odd_even_ratio'].append( + f"{odd_count}:{len(numbers)-odd_count}") + + # 分区分布 + zones = [(n-1)//5 + 1 for n in numbers] + zone_count = len(set(zones)) + patterns['zone_distribution'].append(zone_count) + + # 连号数量 + sorted_nums = sorted(numbers) + consecutive = sum(1 for i in range(len(sorted_nums)-1) + if sorted_nums[i+1] - sorted_nums[i] == 1) + patterns['consecutive_count'].append(consecutive) + + # 计算最常见的模式 + most_common_patterns = {} + for key, values in patterns.items(): + if key == 'sum_range': + # 和值范围 + avg_sum = np.mean(values) + std_sum = np.std(values) + most_common_patterns[key] = { + 'avg': avg_sum, + 'std': std_sum, + 'range': [int(avg_sum - std_sum), int(avg_sum + std_sum)] + } + else: + # 其他模式 + from collections import Counter + counter = Counter(values) + most_common_patterns[key] = counter.most_common(3) + + # 根据模式生成推荐号码 + max_num = 33 if lottery_type == 'ssq' else 35 + target_count = 6 if lottery_type == 'ssq' else 5 + + # 获取推荐的模式 + target_sum_range = most_common_patterns['sum_range']['range'] + target_odd_ratio = most_common_patterns['odd_even_ratio'][0][0].split( + ':')[0] if most_common_patterns['odd_even_ratio'] else "3" + target_zones = most_common_patterns['zone_distribution'][0][ + 0] if most_common_patterns['zone_distribution'] else 4 + target_consecutive = most_common_patterns['consecutive_count'][ + 0][0] if most_common_patterns['consecutive_count'] else 1 + + # 生成符合模式的号码 + import random + best_numbers = None + best_score = -1 + + # 尝试100次生成最符合模式的号码 + for _ in range(100): + # 初始化号码集 + numbers = set() + + # 确保有连号 + if target_consecutive > 0: + start = random.randint(1, max_num - target_consecutive) + for i in range(target_consecutive + 1): + if len(numbers) < target_count: + numbers.add(start + i) + + # 根据奇偶比例添加号码 + target_odd = int(target_odd_ratio) + current_odd = sum(1 for n in numbers if n % 2 == 1) + + while len(numbers) < target_count: + n = random.randint(1, max_num) + if n not in numbers: + if (n % 2 == 1 and current_odd < target_odd) or \ + (n % 2 == 0 and (len(numbers) - current_odd) < (target_count - target_odd)): + numbers.add(n) + if n % 2 == 1: + current_odd += 1 + + numbers = sorted(list(numbers)) + + # 计算当前号码组合的得分 + score = 0 + + # 和值得分 + current_sum = sum(numbers) + if target_sum_range[0] <= current_sum <= target_sum_range[1]: + score += 1 + + # 奇偶比得分 + current_odd = sum(1 for n in numbers if n % 2 == 1) + if current_odd == int(target_odd_ratio): + score += 1 + + # 分区得分 + current_zones = len(set((n-1)//5 + 1 for n in numbers)) + if current_zones == target_zones: + score += 1 + + # 连号得分 + current_consecutive = sum(1 for i in range( + len(numbers)-1) if numbers[i+1] - numbers[i] == 1) + if current_consecutive == target_consecutive: + score += 1 + + if score > best_score: + best_score = score + best_numbers = numbers + + # 如果是双色球,还需要生成蓝球 + predicted_blue = None + if lottery_type == 'ssq': + # 分析蓝球规律 + blue_numbers = [draw.blue_ball for draw in recent_draws] + blue_counter = Counter(blue_numbers) + # 选择最近出现频率适中的蓝球 + common_blues = [num for num, _ in blue_counter.most_common( + )[len(blue_counter)//3:(len(blue_counter)*2)//3]] + if common_blues: + predicted_blue = random.choice(common_blues) + else: + predicted_blue = random.randint(1, 16) + + return { + "success": True, + "patterns": most_common_patterns, + "suggested_criteria": { + "sum_range": most_common_patterns['sum_range']['range'], + "odd_even_ratio": most_common_patterns['odd_even_ratio'][0][0] if most_common_patterns['odd_even_ratio'] else "3:3", + "zone_distribution": most_common_patterns['zone_distribution'][0][0] if most_common_patterns['zone_distribution'] else 4, + "consecutive_count": most_common_patterns['consecutive_count'][0][0] if most_common_patterns['consecutive_count'] else 1 + }, + "predicted_numbers": best_numbers, + "predicted_blue": predicted_blue if lottery_type == 'ssq' else None + } + + def get_ensemble_prediction(self, lottery_type: str, periods: int = 100) -> Dict: + """集成预测(结合多种方法) + + Args: + lottery_type: 彩票类型 ('ssq' 或 'dlt') + periods: 分析期数 + + Returns: + Dict: 预测结果 + """ + # 机器学习预测 + ml_result = self.predict_next_numbers(lottery_type, periods) + + # 模式预测 + pattern_result = self.get_pattern_based_prediction( + lottery_type, periods) + + # 频率预测(基于现有服务) + from .analysis_service import LotteryAnalysisService + analysis_service = LotteryAnalysisService(self.db) + freq_result = analysis_service.get_hot_cold_numbers( + lottery_type, periods) + + # 综合推荐 + recommendations = [] + + if ml_result.get('success'): + recommendations.append({ + 'method': '机器学习', + 'numbers': ml_result['predicted_numbers'], + 'blue': ml_result['predicted_blue'] if lottery_type == 'ssq' else None, + 'blues': ml_result['predicted_blues'] if lottery_type == 'dlt' else None, + 'confidence': '高' + }) + + if freq_result: + 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 + + # 获取热号和冷号 + hot_reds = freq_result['hot_reds'] + cold_reds = freq_result['cold_reds'] + + # 初始化号码集 + selected_numbers = set() + + # 从热号中选择2-3个号码 + hot_count = min(3, len(hot_reds)) + for num in hot_reds[:hot_count]: + selected_numbers.add(num) + + # 从冷号中选择1-2个号码 + cold_count = min(2, len(cold_reds)) + for num in cold_reds[:cold_count]: + if not any(abs(num - x) == 1 for x in selected_numbers): # 避免连号 + selected_numbers.add(num) + + # 计算还需要多少个号码 + remaining = red_count - len(selected_numbers) + + # 获取温号(既不是热号也不是冷号的号码) + all_numbers = set(range(1, max_red + 1)) + warm_numbers = list(all_numbers - set(hot_reds) - set(cold_reds)) + import random + random.shuffle(warm_numbers) + + # 从温号中补充号码 + for num in warm_numbers: + if len(selected_numbers) >= red_count: + break + # 检查是否会形成连号 + consecutive_count = sum( + 1 for x in selected_numbers if abs(num - x) == 1) + if consecutive_count <= 1: # 最多允许两个连号 + selected_numbers.add(num) + + # 如果还不够,从剩余号码中随机选择 + remaining_numbers = list(all_numbers - selected_numbers) + while len(selected_numbers) < red_count: + num = random.choice(remaining_numbers) + consecutive_count = sum( + 1 for x in selected_numbers if abs(num - x) == 1) + if consecutive_count <= 1: + selected_numbers.add(num) + remaining_numbers.remove(num) + + # 生成蓝球 + if lottery_type == 'ssq': + if 'hot_blues' in freq_result and freq_result['hot_blues']: + # 从热门蓝球中随机选择 + blue_prediction = random.choice( + freq_result['hot_blues'][:3]) + else: + blue_prediction = random.randint(1, max_blue) + + recommendations.append({ + 'method': '热冷号分析', + 'numbers': sorted(list(selected_numbers)), + 'blue': blue_prediction, + 'confidence': '中' + }) + else: + # 大乐透后区号码选择 + blue_predictions = [] + if 'hot_blues' in freq_result and freq_result['hot_blues']: + # 从热门后区号码中选择 + available_blues = freq_result['hot_blues'][:4] # 取前4个热门号码 + while len(blue_predictions) < 2 and available_blues: + num = random.choice(available_blues) + blue_predictions.append(num) + available_blues.remove(num) + + # 如果还不够2个,随机补充 + while len(blue_predictions) < 2: + num = random.randint(1, max_blue) + if num not in blue_predictions: + blue_predictions.append(num) + + recommendations.append({ + 'method': '热冷号分析', + 'numbers': sorted(list(selected_numbers)), + 'blues': sorted(blue_predictions), + 'confidence': '中' + }) + + if pattern_result and pattern_result.get('success'): + recommendations.append({ + 'method': '模式分析', + 'numbers': pattern_result['predicted_numbers'], + 'blue': pattern_result['predicted_blue'] if lottery_type == 'ssq' else None, + 'blues': pattern_result['predicted_blues'] if lottery_type == 'dlt' else None, + 'confidence': '中' + }) + + return { + "success": True, + "recommendations": recommendations, + "pattern_analysis": pattern_result.get('suggested_criteria', {}) if pattern_result else {}, + "frequency_analysis": freq_result or {} + } diff --git a/backend/requirements.txt b/backend/requirements.txt index aab9f46..6a021c0 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -12,4 +12,8 @@ python-dotenv==1.0.0 pandas==2.1.3 aiofiles==23.2.1 requests==2.31.0 -schedule==1.2.1 \ No newline at end of file +schedule==1.2.1 +numpy>=1.21.0,<2.0.0 +scikit-learn>=1.0.0,<2.0.0 +matplotlib>=3.5.0,<4.0.0 +seaborn>=0.11.0,<1.0.0 \ No newline at end of file diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 18a0669..4192de0 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,119 +1,144 @@ - \ No newline at end of file diff --git a/frontend/src/api/advancedAnalysis.js b/frontend/src/api/advancedAnalysis.js new file mode 100644 index 0000000..6d3b078 --- /dev/null +++ b/frontend/src/api/advancedAnalysis.js @@ -0,0 +1,83 @@ +import request from '@/utils/request' + +// 获取遗漏值分析 +export function getMissingValueAnalysis(lotteryType, periods = 100) { + return request({ + url: `/api/v1/advanced-analysis/missing-value/${lotteryType}`, + method: 'get', + params: { periods } + }) +} + +// 获取和值分析 +export function getSumValueAnalysis(lotteryType, periods = 100) { + return request({ + url: `/api/v1/advanced-analysis/sum-value/${lotteryType}`, + method: 'get', + params: { periods } + }) +} + +// 获取AC值分析 +export function getAcValueAnalysis(lotteryType, periods = 100) { + return request({ + url: `/api/v1/advanced-analysis/ac-value/${lotteryType}`, + method: 'get', + params: { periods } + }) +} + +// 获取质合比分析 +export function getPrimeCompositeAnalysis(lotteryType, periods = 100) { + return request({ + url: `/api/v1/advanced-analysis/prime-composite/${lotteryType}`, + method: 'get', + params: { periods } + }) +} + +// 获取012路分析 +export function getRoad012Analysis(lotteryType, periods = 100) { + return request({ + url: `/api/v1/advanced-analysis/road-012/${lotteryType}`, + method: 'get', + params: { periods } + }) +} + +// 获取跨度分析 +export function getSpanAnalysis(lotteryType, periods = 100) { + return request({ + url: `/api/v1/advanced-analysis/span/${lotteryType}`, + method: 'get', + params: { periods } + }) +} + +// 获取综合分析 +export function getComprehensiveAnalysis(lotteryType, periods = 100) { + return request({ + url: `/api/v1/advanced-analysis/comprehensive/${lotteryType}`, + method: 'get', + params: { periods } + }) +} + +// 通用高级分析接口 +export function getAdvancedAnalysis(lotteryType, analysisType, periods = 100) { + const urlMap = { + 'missing': `/api/v1/advanced-analysis/missing-value/${lotteryType}`, + 'sum': `/api/v1/advanced-analysis/sum-value/${lotteryType}`, + 'ac': `/api/v1/advanced-analysis/ac-value/${lotteryType}`, + 'prime': `/api/v1/advanced-analysis/prime-composite/${lotteryType}`, + 'road': `/api/v1/advanced-analysis/road-012/${lotteryType}`, + 'span': `/api/v1/advanced-analysis/span/${lotteryType}`, + 'comprehensive': `/api/v1/advanced-analysis/comprehensive/${lotteryType}` + } + + return request({ + url: urlMap[analysisType], + method: 'get', + params: { periods } + }) +} \ No newline at end of file diff --git a/frontend/src/api/prediction.js b/frontend/src/api/prediction.js new file mode 100644 index 0000000..b13151e --- /dev/null +++ b/frontend/src/api/prediction.js @@ -0,0 +1,37 @@ +import request from '@/utils/request' + +// 训练预测模型 +export function trainPredictionModel(lotteryType, periods = 100) { + return request({ + url: `/api/v1/prediction/train/${lotteryType}`, + method: 'post', + params: { periods } + }) +} + +// 预测下一期号码 +export function predictNextNumbers(lotteryType, periods = 10) { + return request({ + url: `/api/v1/prediction/predict/${lotteryType}`, + method: 'get', + params: { periods } + }) +} + +// 基于模式的预测 +export function getPatternPrediction(lotteryType, periods = 100) { + return request({ + url: `/api/v1/prediction/pattern/${lotteryType}`, + method: 'get', + params: { periods } + }) +} + +// 集成预测 +export function getEnsemblePrediction(lotteryType, periods = 100) { + return request({ + url: `/api/v1/prediction/ensemble/${lotteryType}`, + method: 'get', + params: { periods } + }) +} \ No newline at end of file diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index a763a75..344c55b 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -1,36 +1,51 @@ import { createRouter, createWebHistory } from 'vue-router' - -const routes = [ - { - path: '/', - name: 'Home', - component: () => import('../views/Home.vue') - }, - { - path: '/ssq', - name: 'SSQ', - component: () => import('../views/SSQ.vue') - }, - { - path: '/dlt', - name: 'DLT', - component: () => import('../views/DLT.vue') - }, - { - path: '/statistics', - name: 'Statistics', - component: () => import('../views/Statistics.vue') - }, - { - path: '/number-generator', - name: 'NumberGenerator', - component: () => import('../views/NumberGenerator.vue') - } -] +import Home from '../views/Home.vue' +import SSQ from '../views/SSQ.vue' +import DLT from '../views/DLT.vue' +import Statistics from '../views/Statistics.vue' +import AdvancedAnalysis from '../views/AdvancedAnalysis.vue' +import Prediction from '../views/Prediction.vue' +import NumberGenerator from '../views/NumberGenerator.vue' const router = createRouter({ history: createWebHistory(), - routes + routes: [ + { + path: '/', + name: 'Home', + component: Home + }, + { + path: '/ssq', + name: 'SSQ', + component: SSQ + }, + { + path: '/dlt', + name: 'DLT', + component: DLT + }, + { + path: '/statistics', + name: 'Statistics', + component: Statistics + }, + { + path: '/advanced-analysis', + name: 'AdvancedAnalysis', + component: AdvancedAnalysis + }, + { + path: '/prediction', + name: 'Prediction', + component: Prediction + }, + { + path: '/number-generator', + name: 'NumberGenerator', + component: NumberGenerator + } + ] }) export default router \ No newline at end of file diff --git a/frontend/src/views/AdvancedAnalysis.vue b/frontend/src/views/AdvancedAnalysis.vue new file mode 100644 index 0000000..535eedd --- /dev/null +++ b/frontend/src/views/AdvancedAnalysis.vue @@ -0,0 +1,576 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/Prediction.vue b/frontend/src/views/Prediction.vue new file mode 100644 index 0000000..5c84724 --- /dev/null +++ b/frontend/src/views/Prediction.vue @@ -0,0 +1,604 @@ + + + + + \ No newline at end of file