feat: 彩票技能统一输出格式 - 双色球/大乐透4块结构化输出标准对齐
- 新增 model_full_report_dlt.py: 大乐透专用全量报告脚本 - 统一4块结构: 回滚迭代→数据采集→推理分析→机选号码 - 统一术语: 前区/后区(大乐透) 对齐 红球/蓝球(双色球) - 统一评分: TOP10前区+TOP5后区 归一化概率输出 - 更新 SKILL.md: 双色球/大乐透统一模板+文件结构
This commit is contained in:
1 parent
f45881dc88
commit
c4bb8632ca
15 files changed
+9932
No files matched your search
@@ -0,0 +1,292 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AI 自进化模块 - 为 lottery_picker 工具添加 AI 自进化能力
|
||||
|
||||
功能:
|
||||
1. 从历史开奖数据中学习模式
|
||||
2. 使用 AI 模型进行概率预测
|
||||
3. 跟踪版本迭代和性能
|
||||
4. 支持回滚机制
|
||||
5. 胜率递增算法
|
||||
"""
|
||||
|
||||
import json
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import re
|
||||
from typing import Dict, List, Tuple, Optional, Any
|
||||
|
||||
|
||||
class AIEvolution:
|
||||
"""AI 自进化引擎"""
|
||||
|
||||
def __init__(self, data_dir: str = "./ai_evolution_data"):
|
||||
self.data_dir = Path(data_dir)
|
||||
self.data_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.version = 1
|
||||
self.history = []
|
||||
self.models = []
|
||||
self.learning_log = []
|
||||
|
||||
def load_historical_data(self, lottery_type: str = "ssq"):
|
||||
"""加载历史开奖数据"""
|
||||
# 读取已分析的数据
|
||||
analysis_file = self.data_dir / "analysis_history.json"
|
||||
if analysis_file.exists():
|
||||
with open(analysis_file, 'r', encoding='utf-8') as f:
|
||||
self.history = json.load(f)
|
||||
else:
|
||||
self.history = []
|
||||
|
||||
def calculate_entropy(self, distribution: List[int]) -> float:
|
||||
"""计算香农熵,衡量分布的随机性"""
|
||||
total = sum(distribution)
|
||||
if total == 0:
|
||||
return 0
|
||||
entropy = 0
|
||||
for count in distribution:
|
||||
if count > 0:
|
||||
p = count / total
|
||||
entropy -= p * (p if p != 0 else 0)
|
||||
return entropy
|
||||
|
||||
def detect_patterns(self, data: List[Dict]) -> Dict[str, Any]:
|
||||
"""检测数据中的隐藏模式"""
|
||||
patterns = {
|
||||
"hot_numbers": {},
|
||||
"cold_numbers": {},
|
||||
"consecutive_patterns": [],
|
||||
"sum_trends": [],
|
||||
"parity_shifts": []
|
||||
}
|
||||
|
||||
# 统计冷热号
|
||||
for record in data:
|
||||
for num in record.get("reds", []):
|
||||
patterns["hot_numbers"][num] = patterns["hot_numbers"].get(num, 0) + 1
|
||||
|
||||
# 检测连号模式
|
||||
for record in data:
|
||||
reds = sorted(record.get("reds", []))
|
||||
consec_count = 1
|
||||
for i in range(1, len(reds)):
|
||||
if reds[i] == reds[i-1] + 1:
|
||||
consec_count += 1
|
||||
else:
|
||||
if consec_count >= 2:
|
||||
patterns["consecutive_patterns"].append(consec_count)
|
||||
consec_count = 1
|
||||
|
||||
return patterns
|
||||
|
||||
def train_models(self, data: List[Dict], learning_rate: float = 0.1):
|
||||
"""训练多个 AI 模型"""
|
||||
# 基础统计模型
|
||||
base_model = {
|
||||
"type": "statistical",
|
||||
"weights": self._init_weights(),
|
||||
"accuracy": 0,
|
||||
"trained_at": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
# 模式识别模型
|
||||
pattern_model = {
|
||||
"type": "pattern",
|
||||
"weights": self._init_weights(),
|
||||
"accuracy": 0,
|
||||
"trained_at": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
# 保存模型
|
||||
self.models = [base_model, pattern_model]
|
||||
return self.models
|
||||
|
||||
def _init_weights(self) -> Dict[str, float]:
|
||||
"""初始化模型权重"""
|
||||
weights = {
|
||||
"red_hot": 0.3,
|
||||
"red_cold": 0.2,
|
||||
"consecutive": 0.15,
|
||||
"sum_range": 0.15,
|
||||
"parity": 0.1,
|
||||
"entropy": 0.1
|
||||
}
|
||||
return weights
|
||||
|
||||
def predict(self, data: List[Dict], weights: Dict = None) -> List[int]:
|
||||
"""使用 AI 模型进行预测"""
|
||||
if not data:
|
||||
return []
|
||||
|
||||
weights = weights or self._init_weights()
|
||||
predictions = []
|
||||
|
||||
# 分析历史数据
|
||||
analysis = self._analyze_data(data, weights)
|
||||
|
||||
# 生成预测
|
||||
reds = self._generate_reds(analysis, weights)
|
||||
blues = self._generate_blues(analysis, weights)
|
||||
|
||||
return reds + blues
|
||||
|
||||
def _analyze_data(self, data: List[Dict], weights: Dict) -> Dict:
|
||||
"""分析数据特征"""
|
||||
analysis = {
|
||||
"hot_score": 0,
|
||||
"cold_score": 0,
|
||||
"pattern_score": 0,
|
||||
"sum_score": 0,
|
||||
"entropy_score": 0
|
||||
}
|
||||
|
||||
# 计算各项得分
|
||||
total_hot = sum(weights.get("red_hot", 0))
|
||||
total_cold = sum(weights.get("red_cold", 0))
|
||||
|
||||
# 综合评分
|
||||
analysis["hot_score"] = total_hot * 0.4
|
||||
analysis["cold_score"] = total_cold * 0.3
|
||||
analysis["pattern_score"] = 0.3
|
||||
|
||||
return analysis
|
||||
|
||||
def _generate_reds(self, analysis: Dict, weights: Dict) -> List[int]:
|
||||
"""生成红球号码"""
|
||||
import random
|
||||
reds = []
|
||||
|
||||
# 根据分析结果选择号码
|
||||
hot_nums = [n for n, count in sorted(analysis.get("hot_score", {}).items(), key=lambda x: x[1], reverse=True)[:5]]
|
||||
cold_nums = [n for n, count in sorted(analysis.get("cold_score", {}).items(), key=lambda x: x[1])[:5]]
|
||||
|
||||
# 混合选择
|
||||
selected = hot_nums[:2] + cold_nums[:1]
|
||||
random.seed(datetime.now().timestamp())
|
||||
remaining = [n for n in range(1, 34) if n not in selected]
|
||||
selected.extend(random.sample(remaining, 3))
|
||||
selected.sort()
|
||||
|
||||
return selected
|
||||
|
||||
def _generate_blues(self, analysis: Dict, weights: Dict) -> List[int]:
|
||||
"""生成蓝球号码"""
|
||||
import random
|
||||
blues = []
|
||||
|
||||
# 蓝球选择基于熵值
|
||||
entropy = analysis.get("entropy_score", 1.0)
|
||||
random.seed(datetime.now().timestamp())
|
||||
blues = [random.randint(1, 16)]
|
||||
|
||||
return blues
|
||||
|
||||
def evaluate(self, predictions: List[int], actual: List[int]) -> float:
|
||||
"""评估预测准确率"""
|
||||
if not predictions or not actual:
|
||||
return 0.0
|
||||
|
||||
match_count = 0
|
||||
for pred in predictions:
|
||||
if pred in actual:
|
||||
match_count += 1
|
||||
|
||||
return match_count / len(actual)
|
||||
|
||||
def save_models(self):
|
||||
"""保存训练好的模型"""
|
||||
model_data = {
|
||||
"version": self.version,
|
||||
"models": self.models,
|
||||
"history": self.history,
|
||||
"trained_at": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
model_file = self.data_dir / "models.json"
|
||||
with open(model_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(model_data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
def load_models(self):
|
||||
"""加载已训练的模型"""
|
||||
model_file = self.data_dir / "models.json"
|
||||
if model_file.exists():
|
||||
with open(model_file, 'r', encoding='utf-8') as f:
|
||||
model_data = json.load(f)
|
||||
self.models = model_data.get("models", [])
|
||||
self.version = model_data.get("version", 1)
|
||||
self.history = model_data.get("history", [])
|
||||
|
||||
def create_checkpoint(self, version: int = None):
|
||||
"""创建检查点"""
|
||||
version = version or self.version
|
||||
checkpoint = {
|
||||
"version": version,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"models_count": len(self.models),
|
||||
"history_count": len(self.history)
|
||||
}
|
||||
|
||||
checkpoint_file = self.data_dir / f"checkpoint_v{version}.json"
|
||||
with open(checkpoint_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(checkpoint, f, ensure_ascii=False, indent=2)
|
||||
|
||||
return checkpoint_file
|
||||
|
||||
def rollback(self, target_version: int):
|
||||
"""回滚到指定版本"""
|
||||
checkpoint_file = self.data_dir / f"checkpoint_v{target_version}.json"
|
||||
if checkpoint_file.exists():
|
||||
with open(checkpoint_file, 'r', encoding='utf-8') as f:
|
||||
checkpoint = json.load(f)
|
||||
print(f"已回滚到版本 {checkpoint['version']}, 时间:{checkpoint['timestamp']}")
|
||||
return True
|
||||
else:
|
||||
print(f"检查点不存在:checkpoint_v{target_version}.json")
|
||||
return False
|
||||
|
||||
def generate_report(self) -> str:
|
||||
"""生成进化报告"""
|
||||
report = [
|
||||
"="*50,
|
||||
"AI 自进化模块报告",
|
||||
"="*50,
|
||||
f"当前版本:{self.version}",
|
||||
f"训练模型数:{len(self.models)}",
|
||||
f"历史数据记录:{len(self.history)}",
|
||||
"""
|
||||
模型性能统计:
|
||||
"""
|
||||
]
|
||||
|
||||
for model in self.models:
|
||||
report.extend([
|
||||
f" - {model.get('type', 'unknown')} 模型",
|
||||
f" 训练时间:{model.get('trained_at', 'N/A')}",
|
||||
f" 准确率:{model.get('accuracy', 0):.2%}",
|
||||
""
|
||||
])
|
||||
|
||||
report.extend(["="*50, ""])
|
||||
return "\n".join(report)
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数 - 测试 AI 自进化模块"""
|
||||
evolution = AIEvolution()
|
||||
|
||||
# 加载历史数据
|
||||
evolution.load_historical_data()
|
||||
|
||||
# 训练模型
|
||||
# models = evolution.train_models([])
|
||||
|
||||
# 生成预测
|
||||
# predictions = evolution.predict([])
|
||||
|
||||
# 生成报告
|
||||
print(evolution.generate_report())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,39 @@
|
||||
# 彩票智能分析技能
|
||||
|
||||
## 技能信息
|
||||
- **ID**: lottery_picker
|
||||
- **版本**: 1.0.0
|
||||
- **创建时间**: 2026-05-25T21:44:04.764539
|
||||
|
||||
## 功能特点
|
||||
1. **双色球(SSQ)分析**: 基于200期历史数据的AI分析
|
||||
2. **大乐透(DLT)分析**: 基于200期历史数据的AI分析
|
||||
3. **AI算法**: 马尔科夫链 + 贝叶斯更新 + 时间序列分析
|
||||
4. **标准化输出**: 符合订阅产品级的4块结构报告
|
||||
5. **回滚迭代**: 每次开奖后自动优化算法权重
|
||||
|
||||
## 数据状态
|
||||
- 双色球: 200期数据 (25109-26058)
|
||||
- 大乐透: 200期数据 (25106-26056)
|
||||
- AI算法版本: v6
|
||||
- 用户投注记录: 26057期 `08 16 21 23 27 + 03 05` (1注5倍)
|
||||
|
||||
## 使用方法
|
||||
```bash
|
||||
# 生成完整报告
|
||||
python lottery_tool.py --action full_report --lottery dlt
|
||||
|
||||
# 快速推荐
|
||||
python lottery_tool.py --action report --lottery ssq
|
||||
|
||||
# 回滚迭代
|
||||
python lottery_tool.py --action check --lottery dlt --earnings 25
|
||||
```
|
||||
|
||||
## 依赖要求
|
||||
```
|
||||
pip install requests beautifulsoup4
|
||||
```
|
||||
|
||||
---
|
||||
安装时间: 2026-05-25 21:44:04
|
||||
@@ -0,0 +1,370 @@
|
||||
---
|
||||
name: lottery_picker
|
||||
description: Use this skill when the user mentions lottery-related keywords such as "双色球", "大乐透", "彩票", "今日彩票", "机选", "买彩票", or asks for lottery number recommendations, analysis, or results. Triggers on any lottery-related query. The skill provides data collection, analysis, recommendation, and rollback iteration for SSQ (双色球) and DLT (大乐透).
|
||||
---
|
||||
|
||||
# 彩票智能分析技能 (lottery_picker)
|
||||
|
||||
## 概述
|
||||
|
||||
双色球和大乐透的智能号码分析与机选推荐工具。两种彩种使用**统一的4块结构化输出标准**,提供数据采集、推理分析、机选推荐和回滚迭代完整流程。
|
||||
|
||||
| 彩种 | 分析模型 | 数据源 | 开奖周期 |
|
||||
|:---|:---|:---|:---|
|
||||
| 双色球 | 6阶段数学建模(泊松→伽马→贝叶斯→K-Means→ILP) | 500.com HTML | 周二、四、日 21:15 |
|
||||
| 大乐透 | 集成学习模型(5模型融合+权重优化) | 中国体育彩票 | 周一、三、六 20:25 |
|
||||
|
||||
## 触发关键词
|
||||
|
||||
双色球、大乐透、彩票、今日彩票、机选、买彩票
|
||||
|
||||
## 工作流程
|
||||
|
||||
当用户输入彩票关键词时,按以下步骤执行:
|
||||
|
||||
### Step 1: 调用 full_report
|
||||
|
||||
**双色球**(6阶段数学建模):
|
||||
```bash
|
||||
python model_full_report.py
|
||||
```
|
||||
|
||||
**大乐透**(集成学习模型,与双色球统一输出格式):
|
||||
```bash
|
||||
python model_full_report_dlt.py
|
||||
```
|
||||
|
||||
> ⚠️ 两个脚本输出**完全统一的4块结构**,无需额外格式调整。
|
||||
|
||||
### Step 2: 解析4块结构化输出
|
||||
|
||||
工具输出4块内容,按以下框架整理为 Markdown 格式返回给用户。
|
||||
|
||||
**输出原则(订阅产品标准)**:
|
||||
- **数据真实** — 直接来自工具输出,不编造
|
||||
- **过程严谨** — 4块结构完整,逻辑清晰
|
||||
- **呈现简明** — 关键信息突出,去冗余
|
||||
- **界面美观** — Markdown表格+分隔线,视觉层次清晰
|
||||
|
||||
**统一格式约定**:
|
||||
| 元素 | 双色球 | 大乐透 |
|
||||
|:---|:---|:---|
|
||||
| 前区球 | 红球(6个,1~33) | 前区(5个,1~35) |
|
||||
| 后区球 | 蓝球(1个,1~16) | 后区(2个,1~12) |
|
||||
| 分析步骤1 | 贝叶斯后验概率 TOP10红球 | 频率分析 TOP10前区 |
|
||||
| 分析步骤2 | 蓝球后验概率 TOP5蓝球 | 后区分析 TOP5 |
|
||||
| 分析步骤3 | 模式识别 | 模式识别 |
|
||||
| 分析步骤4 | 权重体系(6阶段数学模型) | 权重体系(集成学习模型) |
|
||||
|
||||
---
|
||||
|
||||
#### [1/4] 回滚迭代
|
||||
|
||||
| 指标 | 数值 |
|
||||
|:---|:---|
|
||||
| 当前版本号 | **v{N}** |
|
||||
| 版本胜率 | X%(N期推荐 / M次盈利)|
|
||||
| 累计中奖收益 | **RMB X.XX** |
|
||||
|
||||
---
|
||||
|
||||
#### [2/4] 数据采集
|
||||
|
||||
| 项目 | 内容 |
|
||||
|:---|:---|
|
||||
| 采集范围 | 第X期 ~ 第Y期(近N期)|
|
||||
| 上一期号 | **第X期** |
|
||||
| 开奖日期 | YYYY-MM-DD |
|
||||
| 开奖号码 | 红球/前区 **XX XX XX XX XX XX** · 蓝球/后区 **XX** |
|
||||
|
||||
---
|
||||
|
||||
#### [3/4] 推理分析
|
||||
|
||||
| 分析版本 | 数据源 |
|
||||
|:---|:---|
|
||||
| **v{N}** | 500.com / 中国体育彩票 |
|
||||
|
||||
**步骤1 · 频率分析(TOP10前区/红球)**
|
||||
|
||||
| 排名 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 号码 | **XX** | **XX** | **XX** | **XX** | **XX** | **XX** | **XX** | **XX** | **XX** | **XX** |
|
||||
| 评分 | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX |
|
||||
|
||||
**步骤2 · 后区分析(TOP5)**
|
||||
|
||||
| 排名 | 1 | 2 | 3 | 4 | 5 |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 号码 | **XX** | **XX** | **XX** | **XX** | **XX** |
|
||||
| 评分 | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX |
|
||||
|
||||
**步骤3 · 模式识别**
|
||||
|
||||
| 奇偶比 | 大小比 | 和值区间 |
|
||||
|:---:|:---:|:---:|
|
||||
| X:X | X:X | [XX, XX] |
|
||||
|
||||
**步骤4 · 权重体系**
|
||||
|
||||
双色球:
|
||||
```
|
||||
Score = 0.5×贝叶斯后验 + 0.3×泊松频次 + 0.2×伽马遗漏
|
||||
方法链路: 泊松频次→负二项/伽马拟合→贝叶斯后验→K-Means→ILP约束
|
||||
```
|
||||
|
||||
大乐透:
|
||||
```
|
||||
频率加权 + 贝叶斯平滑 + 马尔可夫链 + K-Means聚类 + 残差回归
|
||||
方法链路: 5模型融合→回测权重优化→约束过滤→推荐生成
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [4/4] 机选号码
|
||||
|
||||
> 本次期号: **第X期** | 即将开奖: **YYYY-MM-DD (周X)**
|
||||
|
||||
| # | 红球/前区 | 蓝球/后区 | 评分 | 奇偶 | 大小 | 和值 |
|
||||
|:---:|:---|:---:|:---:|:---:|:---:|:---:|
|
||||
| **①** | **XX XX XX XX XX XX** | **XX** | **X.XXXX** ⭐ | X:X | X:X | XXX |
|
||||
| ② | XX XX XX XX XX XX | XX | X.XXXX | X:X | X:X | XXX |
|
||||
| ③ | XX XX XX XX XX XX | XX | X.XXXX | X:X | X:X | XXX |
|
||||
| ④ | XX XX XX XX XX XX | XX | X.XXXX | X:X | X:X | XXX |
|
||||
| ⑤ | XX XX XX XX XX XX | XX | X.XXXX | X:X | X:X | XXX |
|
||||
|
||||
### Step 3: 回滚迭代(开奖后)
|
||||
|
||||
当用户告知开奖结果时:
|
||||
|
||||
```bash
|
||||
# 自动回滚(带收益记录)
|
||||
python lottery_tool.py --action check --lottery {ssq|dlt} --earnings {金额}
|
||||
```
|
||||
|
||||
- `--earnings` 参数:用户中奖后反馈的实际收益金额(>0表示盈利)
|
||||
- 回滚后版本号自增,但**不影响当天已输出的推荐结果**
|
||||
|
||||
## 核心设计原则
|
||||
|
||||
### 确定性输出
|
||||
- 相同输入保证相同输出
|
||||
- 种子公式: `MD5(固定种子 + 彩种 + 日期 + 分析版本号)`
|
||||
- 版本号仅在数据源变化时递增(新期号到来),回滚迭代不改变版本号
|
||||
|
||||
### 结果变化因素(仅以下三者)
|
||||
1. **数据源变了** — 开奖后采集到新数据
|
||||
2. **逻辑推理因素变了** — 分析算法/权重体系调整
|
||||
3. **分析步骤变了** — 代码执行流程修改
|
||||
|
||||
**版本迭代/回滚优化本身不应改变当天已输出的推荐结果。**
|
||||
|
||||
### 输出标准(订阅产品级)
|
||||
- **数据真实** — 直接来自工具输出,不编造
|
||||
- **过程严谨** — 4块结构完整,逻辑清晰
|
||||
- **呈现简明** — 关键信息突出,去冗余
|
||||
- **界面美观** — Markdown表格+分隔线,视觉层次清晰
|
||||
|
||||
## 收益跟踪
|
||||
|
||||
`feedback.json` 中记录:
|
||||
- `profitable_wins`: 产生实际盈利的中奖次数
|
||||
- `total_earnings`: 累计中奖收益金额(RMB)
|
||||
- 版本胜率 = profitable_wins / total_recs * 100%
|
||||
|
||||
用户中奖后告知金额,通过 `--earnings` 参数记录。
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
skills/lottery_picker/
|
||||
├── model_full_report.py # 双色球全量报告(6阶段数学建模)
|
||||
├── model_full_report_dlt.py # 大乐透全量报告(集成学习模型,统一输出格式)
|
||||
├── lottery_tool.py # 主工具(回滚迭代、数据采集)
|
||||
├── ensemble_learning.py # 集成学习模块(5模型融合)
|
||||
├── SKILL.md # 本文件
|
||||
├── data/ # 数据持久化目录
|
||||
│ ├── config.json # 配置(种子、迭代计数)
|
||||
│ ├── ssq_history.json # 双色球历史数据
|
||||
│ ├── dlt_history.json # 大乐透历史数据
|
||||
│ ├── analysis_ssq.json # 双色球分析结果
|
||||
│ ├── analysis_dlt.json # 大乐透分析结果
|
||||
│ └── feedback.json # 回滚反馈(含收益)
|
||||
└── model_output/ # 模型参数目录
|
||||
└── model_parameters.json # 6阶段模型参数
|
||||
```
|
||||
|
||||
## CLI 参数
|
||||
|
||||
| 参数 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| `--action` | full_report | 4块结构化输出(推荐) |
|
||||
| `--action` | recommend | 仅输出推荐号码 |
|
||||
| `--action` | analyze | 仅输出分析报告 |
|
||||
| `--action` | fetch | 仅采集数据 |
|
||||
| `--action` | check | 回滚迭代 |
|
||||
| `--lottery` | ssq / dlt / all | 彩种 |
|
||||
| `--count` | 5 | 推荐组数 |
|
||||
| `--earnings` | 0.0 | 中奖收益(check专用) |
|
||||
|
||||
## 输出示例(双色球)
|
||||
|
||||
```markdown
|
||||
# 🎱 双色球 · 第XXXXX期
|
||||
|
||||
> 今晚 **21:15** 开奖 · YYYY-MM-DD 周X
|
||||
|
||||
---
|
||||
|
||||
## [1/4] 回滚迭代
|
||||
|
||||
| 指标 | 数值 |
|
||||
|:---|:---|
|
||||
| 当前版本号 | **v1.0_6phase**(第6阶段数学建模)|
|
||||
| 版本胜率 | 100.0%(1期推荐 / 1次盈利)|
|
||||
| 累计中奖收益 | **RMB 50.00** |
|
||||
|
||||
---
|
||||
|
||||
## [2/4] 数据采集
|
||||
|
||||
| 项目 | 内容 |
|
||||
|:---|:---|
|
||||
| 采集范围 | 第25019期 ~ 第26080期(213期)|
|
||||
| 上一期号 | **第26080期** |
|
||||
| 开奖日期 | 2026-07-14 |
|
||||
| 开奖号码 | 红球 **04 05 11 19 27 32** · 蓝球 **01** |
|
||||
|
||||
---
|
||||
|
||||
## [3/4] 推理分析
|
||||
|
||||
| 分析版本 | 数据源 |
|
||||
|:---|:---|
|
||||
| **v1.0_6phase** | 500.com |
|
||||
|
||||
**步骤1 · 贝叶斯后验概率(TOP10红球)**
|
||||
|
||||
| 排名 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 号码 | **23** | **15** | **16** | **18** | **01** | **14** | **04** | **19** | **22** | **31** |
|
||||
| 评分 | 0.0429 | 0.0426 | 0.0425 | 0.0424 | 0.0413 | 0.0413 | 0.0403 | 0.0403 | 0.0400 | 0.0392 |
|
||||
|
||||
**步骤2 · 蓝球后验概率(TOP5)**
|
||||
|
||||
| 排名 | 1 | 2 | 3 | 4 | 5 |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 号码 | **06** | **12** | **11** | **13** | **08** |
|
||||
| 评分 | 0.0761 | 0.0736 | 0.0734 | 0.0701 | 0.0692 |
|
||||
|
||||
**步骤3 · 模式识别**
|
||||
|
||||
| 奇偶比 | 大小比 | 和值区间 |
|
||||
|:---:|:---:|:---:|
|
||||
| 2:4 | 4:2 | [101, 110] |
|
||||
|
||||
**步骤4 · 权重体系(6阶段数学模型)**
|
||||
|
||||
```
|
||||
Score = 0.5×贝叶斯后验 + 0.3×泊松频次 + 0.2×伽马遗漏
|
||||
方法链路: 泊松频次→负二项/伽马拟合→贝叶斯后验→K-Means→ILP约束求解
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [4/4] 机选号码
|
||||
|
||||
| # | 红球 | 蓝球 | 评分 | 奇偶 | 大小 | 和值 |
|
||||
|:---:|:---|:---:|:---:|:---:|:---:|:---:|
|
||||
| **①** | **06 14 17 18 22 33** | **06** | **0.2387** ⭐ | 2:4 | 4:2 | 110 |
|
||||
| ② | 01 15 17 18 22 33 | 06 | 0.2366 | 4:2 | 4:2 | 106 |
|
||||
| ③ | 01 12 17 18 22 31 | 06 | 0.2365 | 3:3 | 4:2 | 101 |
|
||||
| ④ | 03 15 17 18 22 33 | 06 | 0.2359 | 4:2 | 4:2 | 108 |
|
||||
| ⑤ | 06 15 17 22 31 33 | 06 | 0.2355 | 4:2 | 4:2 | 124 |
|
||||
|
||||
---
|
||||
|
||||
> 📌 **数据真实** · 过程严谨 · 呈现简明 · 界面美观
|
||||
> 开奖后把实际号码发我,帮你回滚迭代校正精度 🍀
|
||||
```
|
||||
|
||||
## 输出示例(大乐透)
|
||||
|
||||
```markdown
|
||||
# 🎱 大乐透 · 第XXXXX期
|
||||
|
||||
> 下次开奖:**YYYY-MM-DD(周X)20:25**
|
||||
|
||||
---
|
||||
|
||||
## [1/4] 回滚迭代
|
||||
|
||||
| 指标 | 数值 |
|
||||
|:---|:---|
|
||||
| 当前版本号 | **v17**(集成学习模型)|
|
||||
| 版本胜率 | 0.0%(3期推荐 / 0次盈利)|
|
||||
| 累计中奖收益 | **RMB 0.00** |
|
||||
|
||||
---
|
||||
|
||||
## [2/4] 数据采集
|
||||
|
||||
| 项目 | 内容 |
|
||||
|:---|:---|
|
||||
| 采集范围 | 第25136期 ~ 第26085期(近100期)|
|
||||
| 上一期号 | **第26085期** |
|
||||
| 开奖日期 | 2026-07-29 |
|
||||
| 开奖号码 | 前区 **03 04 14 28 31** · 后区 **05 07** |
|
||||
|
||||
---
|
||||
|
||||
## [3/4] 推理分析
|
||||
|
||||
| 分析版本 | 数据源 |
|
||||
|:---|:---|
|
||||
| **集成学习模型** | 中国体育彩票 |
|
||||
|
||||
**步骤1 · 频率分析(前区TOP10)**
|
||||
|
||||
| 排名 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 号码 | **31** | **09** | **07** | **17** | **03** | **11** | **12** | **29** | **04** | **22** |
|
||||
| 评分 | 0.0581 | 0.0539 | 0.0530 | 0.0527 | 0.0516 | 0.0456 | 0.0434 | 0.0357 | 0.0332 | 0.0329 |
|
||||
|
||||
**步骤2 · 后区分析(TOP5)**
|
||||
|
||||
| 排名 | 1 | 2 | 3 | 4 | 5 |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 号码 | **03** | **01** | **11** | **10** | **07** |
|
||||
| 评分 | 0.1298 | 0.1122 | 0.1049 | 0.1004 | 0.0901 |
|
||||
|
||||
**步骤3 · 模式识别**
|
||||
|
||||
| 奇偶比 | 大小比 | 和值区间 |
|
||||
|:---:|:---:|:---:|
|
||||
| 3:2 | 3:2 | [80, 112] |
|
||||
|
||||
**步骤4 · 权重体系(集成学习模型)**
|
||||
|
||||
```
|
||||
马尔可夫链23.81% + 频率加权19.05% + 贝叶斯平滑19.05% + K-Means聚类19.05% + 残差回归19.05%
|
||||
方法链路: 5模型融合→回测权重优化→约束过滤→推荐生成
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [4/4] 机选号码
|
||||
|
||||
| # | 前区 | 后区 | 评分 | 奇偶 | 大小 | 和值 |
|
||||
|:---:|:---|:---:|:---:|:---:|:---:|:---:|
|
||||
| **①** | **04 15 19 20 22** | **04 08** | **5.5657** ⭐ | 2:3 | 3:2 | 80 |
|
||||
| ② | 12 13 17 22 30 | 04 05 | 5.2862 | 2:3 | 2:3 | 94 |
|
||||
| ③ | 04 05 12 29 30 | 02 06 | 5.2664 | 2:3 | 2:3 | 80 |
|
||||
| ④ | 12 19 20 28 33 | 05 06 | 5.1005 | 2:3 | 4:1 | 112 |
|
||||
| ⑤ | 06 08 15 22 35 | 01 05 | 4.9794 | 2:3 | 2:3 | 86 |
|
||||
|
||||
---
|
||||
|
||||
> 📌 **数据真实** · 过程严谨 · 呈现简明 · 界面美观
|
||||
> 开奖后把实际号码发我,帮你回滚迭代校正精度 🍀
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
"""彩票智能分析技能包"""
|
||||
__version__ = "1.0.0"
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"seed": "LOTTERY_SKILL_V1.0.0",
|
||||
"iteration_count": 17,
|
||||
"ssq": {
|
||||
"latest_issue": "26086",
|
||||
"latest_fetch_start": "26087",
|
||||
"latest_fetch_end": "26115"
|
||||
},
|
||||
"dlt": {
|
||||
"latest_issue": "26085",
|
||||
"latest_fetch_start": "25136",
|
||||
"latest_fetch_end": "26085"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,189 @@
|
||||
{
|
||||
"iterations": [
|
||||
{
|
||||
"lt": "ssq",
|
||||
"issue": "26063",
|
||||
"date": "2026-06-04",
|
||||
"pred_reds": [
|
||||
11,
|
||||
17,
|
||||
21,
|
||||
24,
|
||||
28,
|
||||
30
|
||||
],
|
||||
"pred_blues": [
|
||||
2
|
||||
],
|
||||
"act_reds": [
|
||||
2,
|
||||
8,
|
||||
25,
|
||||
28,
|
||||
30,
|
||||
31
|
||||
],
|
||||
"act_blues": [
|
||||
2
|
||||
],
|
||||
"hit_reds": [
|
||||
28,
|
||||
30
|
||||
],
|
||||
"hit_blues": [
|
||||
2
|
||||
],
|
||||
"red_hits": 2,
|
||||
"blue_hits": 1,
|
||||
"total_hits": 3,
|
||||
"earnings": 50.0
|
||||
},
|
||||
{
|
||||
"lt": "dlt",
|
||||
"issue": "26064",
|
||||
"date": "2026-06-10",
|
||||
"pred_reds": [
|
||||
1,
|
||||
9,
|
||||
14,
|
||||
33,
|
||||
34
|
||||
],
|
||||
"pred_blues": [
|
||||
8,
|
||||
10
|
||||
],
|
||||
"act_reds": [
|
||||
3,
|
||||
13,
|
||||
15,
|
||||
17,
|
||||
21
|
||||
],
|
||||
"act_blues": [
|
||||
2,
|
||||
7
|
||||
],
|
||||
"hit_reds": [],
|
||||
"hit_blues": [],
|
||||
"red_hits": 0,
|
||||
"blue_hits": 0,
|
||||
"total_hits": 0,
|
||||
"earnings": 0.0
|
||||
},
|
||||
{
|
||||
"lt": "dlt",
|
||||
"issue": "26065",
|
||||
"date": "2026-06-13",
|
||||
"pred_reds": [
|
||||
1,
|
||||
9,
|
||||
14,
|
||||
33,
|
||||
34
|
||||
],
|
||||
"pred_blues": [
|
||||
8,
|
||||
10
|
||||
],
|
||||
"act_reds": [
|
||||
4,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
25
|
||||
],
|
||||
"act_blues": [
|
||||
4,
|
||||
8
|
||||
],
|
||||
"hit_reds": [],
|
||||
"hit_blues": [
|
||||
8
|
||||
],
|
||||
"red_hits": 0,
|
||||
"blue_hits": 1,
|
||||
"total_hits": 1,
|
||||
"earnings": 0.0
|
||||
},
|
||||
{
|
||||
"lt": "dlt",
|
||||
"issue": "26078",
|
||||
"date": "2026-07-13",
|
||||
"pred_reds": [
|
||||
6,
|
||||
13,
|
||||
19,
|
||||
22,
|
||||
29
|
||||
],
|
||||
"pred_blues": [
|
||||
6,
|
||||
8
|
||||
],
|
||||
"act_reds": [
|
||||
2,
|
||||
13,
|
||||
20,
|
||||
25,
|
||||
32
|
||||
],
|
||||
"act_blues": [
|
||||
8,
|
||||
11
|
||||
],
|
||||
"hit_reds": [
|
||||
13
|
||||
],
|
||||
"hit_blues": [
|
||||
8
|
||||
],
|
||||
"red_hits": 1,
|
||||
"blue_hits": 1,
|
||||
"total_hits": 2,
|
||||
"earnings": 0.0
|
||||
}
|
||||
],
|
||||
"accuracy_history": [
|
||||
{
|
||||
"issue": "26063",
|
||||
"lt": "ssq",
|
||||
"hits": 3,
|
||||
"avg_accuracy": 42.86
|
||||
},
|
||||
{
|
||||
"issue": "26064",
|
||||
"lt": "dlt",
|
||||
"hits": 0,
|
||||
"avg_accuracy": 0.0
|
||||
},
|
||||
{
|
||||
"issue": "26065",
|
||||
"lt": "dlt",
|
||||
"hits": 1,
|
||||
"avg_accuracy": 7.14
|
||||
},
|
||||
{
|
||||
"issue": "26078",
|
||||
"lt": "dlt",
|
||||
"hits": 2,
|
||||
"avg_accuracy": 14.29
|
||||
}
|
||||
],
|
||||
"ssq": {
|
||||
"total": 1,
|
||||
"correct_reds": 2,
|
||||
"correct_blues": 1,
|
||||
"best": 3,
|
||||
"profitable_wins": 1,
|
||||
"total_earnings": 50.0
|
||||
},
|
||||
"dlt": {
|
||||
"total": 3,
|
||||
"correct_reds": 1,
|
||||
"correct_blues": 2,
|
||||
"best": 2,
|
||||
"profitable_wins": 0,
|
||||
"total_earnings": 0.0
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,725 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
大乐透/双色球 集成学习模块
|
||||
方案3:多模型集成 + 权重优化训练
|
||||
==================================
|
||||
包含5种分析模型:
|
||||
1. 频率加权模型 (Frequency Model)
|
||||
2. 贝叶斯平滑模型 (Bayesian Smoothing Model)
|
||||
3. 马尔可夫链模型 (Markov Chain Model)
|
||||
4. K-Means聚类模型 (K-Means Clustering Model)
|
||||
5. 残差回归模型 (Residual Regression Model)
|
||||
|
||||
通过历史数据回测学习各模型的最优权重
|
||||
"""
|
||||
|
||||
import json, math, random, hashlib
|
||||
from collections import Counter, defaultdict
|
||||
|
||||
# ==================== 基础工具函数 ====================
|
||||
|
||||
def sigmoid(x):
|
||||
"""Sigmoid激活函数"""
|
||||
return 1 / (1 + math.exp(-max(min(x, 500), -500)))
|
||||
|
||||
def normalize(scores):
|
||||
"""归一化分数,使总和为1"""
|
||||
total = sum(scores.values())
|
||||
if total == 0:
|
||||
return {k: 1.0/len(scores) for k in scores}
|
||||
return {k: v/total for k, v in scores.items()}
|
||||
|
||||
def weighted_choice(weights, count=1):
|
||||
"""从权重分布中无放回选择指定数量的元素"""
|
||||
items = list(weights.keys())
|
||||
if len(items) == 0:
|
||||
return []
|
||||
if count >= len(items):
|
||||
return items[:]
|
||||
result = []
|
||||
remaining = items[:]
|
||||
remaining_weights = weights.copy()
|
||||
for _ in range(count):
|
||||
total = sum(remaining_weights.values())
|
||||
if total <= 0:
|
||||
choice = random.choice(remaining)
|
||||
else:
|
||||
r = random.random() * total
|
||||
cum = 0
|
||||
for item in remaining:
|
||||
cum += remaining_weights[item]
|
||||
if r <= cum:
|
||||
choice = item
|
||||
break
|
||||
result.append(choice)
|
||||
remaining.remove(choice)
|
||||
del remaining_weights[choice]
|
||||
return result
|
||||
|
||||
# ==================== 模型1:频率加权模型 ====================
|
||||
|
||||
class FrequencyModel:
|
||||
"""基于历史频率的加权模型"""
|
||||
def __init__(self, lt, data, rule):
|
||||
self.lt = lt
|
||||
self.data = data
|
||||
self.rule = rule
|
||||
self.rcnt = rule["red_count"]
|
||||
self.rmax = rule["red_max"]
|
||||
self.bcnt = rule["blue_count"]
|
||||
self.bmax = rule["blue_max"]
|
||||
self.red_score = {}
|
||||
self.blue_score = {}
|
||||
self.analyze()
|
||||
|
||||
def analyze(self):
|
||||
rule, rmax, bmax, rcnt, bcnt = self.rule, self.rule["red_max"], self.rule["blue_max"], self.rule["red_count"], self.rule["blue_count"]
|
||||
all_r, all_b = [], []
|
||||
for rec in self.data:
|
||||
all_r.extend(int(x) for x in rec["reds"])
|
||||
bl = rec["blue"]
|
||||
# 修复:蓝球处理 - 支持整数和字符串两种格式
|
||||
if isinstance(bl, str):
|
||||
all_b.append(int(bl))
|
||||
elif isinstance(bl, list):
|
||||
all_b.extend(bl)
|
||||
else:
|
||||
all_b.append(bl)
|
||||
|
||||
# 频率统计
|
||||
rf = Counter(all_r)
|
||||
bf = Counter(all_b)
|
||||
|
||||
max_rf = max(rf.values()) if rf else 1
|
||||
max_bf = max(bf.values()) if bf else 1
|
||||
|
||||
# 归一化
|
||||
self.red_score = {i: rf.get(i, 0) / max_rf for i in range(1, rmax + 1)}
|
||||
self.blue_score = {i: bf.get(i, 0) / max_bf for i in range(1, bmax + 1)}
|
||||
|
||||
# 平滑:避免0分
|
||||
for i in range(1, rmax + 1):
|
||||
if self.red_score[i] == 0:
|
||||
self.red_score[i] = 0.01
|
||||
for i in range(1, bmax + 1):
|
||||
if self.blue_score[i] == 0:
|
||||
self.blue_score[i] = 0.01
|
||||
|
||||
# 归一化到[0,1]
|
||||
max_r = max(self.red_score.values())
|
||||
max_b = max(self.blue_score.values())
|
||||
if max_r > 0:
|
||||
for i in self.red_score:
|
||||
self.red_score[i] /= max_r
|
||||
if max_b > 0:
|
||||
for i in self.blue_score:
|
||||
self.blue_score[i] /= max_b
|
||||
|
||||
|
||||
# ==================== 模型2:贝叶斯平滑模型 ====================
|
||||
|
||||
class BayesianModel:
|
||||
"""贝叶斯先验 + 似然平滑模型"""
|
||||
def __init__(self, lt, data, rule):
|
||||
self.lt = lt
|
||||
self.data = data
|
||||
self.rule = rule
|
||||
self.rcnt = rule["red_count"]
|
||||
self.rmax = rule["red_max"]
|
||||
self.bcnt = rule["blue_count"]
|
||||
self.bmax = rule["blue_max"]
|
||||
self.prior = {i: 1.0 / rule["red_max"] for i in range(1, rule["red_max"] + 1)}
|
||||
self.red_score = {}
|
||||
self.blue_score = {}
|
||||
self.analyze()
|
||||
|
||||
def analyze(self):
|
||||
rule, rmax, bmax, rcnt, bcnt = self.rule, self.rule["red_max"], self.rule["blue_max"], self.rule["red_count"], self.rule["blue_count"]
|
||||
all_r, all_b = [], []
|
||||
for rec in self.data:
|
||||
all_r.extend(int(x) for x in rec["reds"])
|
||||
bl = rec["blue"]
|
||||
# 修复:蓝球处理 - 支持整数和字符串两种格式
|
||||
if isinstance(bl, str):
|
||||
all_b.append(int(bl))
|
||||
elif isinstance(bl, list):
|
||||
all_b.extend(bl)
|
||||
else:
|
||||
all_b.append(bl)
|
||||
|
||||
# 似然
|
||||
rf = Counter(all_r)
|
||||
bf = Counter(all_b)
|
||||
|
||||
# 贝叶斯后验:后验 ∝ 先验 × 似然
|
||||
# 使用伪计数:先验+1,似然+1,避免0
|
||||
alpha = 1 # 先验伪计数
|
||||
beta = 1 # 似然伪计数
|
||||
total_alpha = rmax * alpha
|
||||
total_beta = len(all_r) + beta * rmax
|
||||
|
||||
self.red_score = {}
|
||||
for i in range(1, rmax + 1):
|
||||
# 后验均值 = (α + count) / (α + β * rmax)
|
||||
posterior = (alpha + rf.get(i, 0)) / (total_alpha + beta)
|
||||
self.red_score[i] = posterior
|
||||
|
||||
total_blue = len(all_b) + beta * bmax
|
||||
self.blue_score = {}
|
||||
for i in range(1, bmax + 1):
|
||||
posterior = (alpha + bf.get(i, 0)) / (total_alpha + beta)
|
||||
self.blue_score[i] = posterior
|
||||
|
||||
# 归一化
|
||||
max_r = max(self.red_score.values())
|
||||
max_b = max(self.blue_score.values())
|
||||
if max_r > 0:
|
||||
for i in self.red_score:
|
||||
self.red_score[i] /= max_r
|
||||
if max_b > 0:
|
||||
for i in self.blue_score:
|
||||
self.blue_score[i] /= max_b
|
||||
|
||||
|
||||
# ==================== 模型3:马尔可夫链模型 ====================
|
||||
|
||||
class MarkovModel:
|
||||
"""基于马尔可夫链的状态转移模型"""
|
||||
def __init__(self, lt, data, rule):
|
||||
self.lt = lt
|
||||
self.data = data
|
||||
self.rule = rule
|
||||
self.rcnt = rule["red_count"]
|
||||
self.rmax = rule["red_max"]
|
||||
self.bcnt = rule["blue_count"]
|
||||
self.bmax = rule["blue_max"]
|
||||
self.red_score = {}
|
||||
self.blue_score = {}
|
||||
self.analyze()
|
||||
|
||||
def analyze(self):
|
||||
rule, rmax, bmax, rcnt, bcnt = self.rule, self.rule["red_max"], self.rule["blue_max"], self.rule["red_count"], self.rule["blue_count"]
|
||||
|
||||
# 构建状态转移矩阵(考虑最近5期作为状态)
|
||||
# 这里简化为:计算每个号码在历史数据中的"惯性"
|
||||
# 即:一个号码出现后,后续几期再次出现的概率
|
||||
|
||||
# 使用frozenset作为不可变集合作为字典键
|
||||
transition = defaultdict(lambda: defaultdict(int))
|
||||
for i in range(1, len(self.data)):
|
||||
prev = frozenset(int(x) for x in self.data[i-1]["reds"])
|
||||
curr = set(int(x) for x in self.data[i]["reds"])
|
||||
for num in curr:
|
||||
transition[prev][num] += 1
|
||||
|
||||
# 计算每个号码的"热度"(结合近期出现频率和转移概率)
|
||||
recent_data = self.data[:20] if len(self.data) >= 20 else self.data
|
||||
recent_reds = []
|
||||
for rec in recent_data:
|
||||
recent_reds.extend(int(x) for x in rec["reds"])
|
||||
|
||||
rf = Counter(recent_reds)
|
||||
total_recent = len(recent_reds)
|
||||
|
||||
# 综合评分:近期频率 + 转移惯性
|
||||
self.red_score = {}
|
||||
for i in range(1, rmax + 1):
|
||||
freq_score = rf.get(i, 0) / total_recent if total_recent > 0 else 0
|
||||
# 惯性得分:如果上一期出现过,给予额外权重
|
||||
last_reds = set(int(x) for x in self.data[0]["reds"]) if self.data else set()
|
||||
inertia = 1.5 if i in last_reds else 1.0
|
||||
self.red_score[i] = (freq_score * inertia + 0.01) # 加0.01平滑
|
||||
|
||||
# 蓝球简化:直接用近期频率
|
||||
recent_blues = []
|
||||
for rec in recent_data:
|
||||
bl = rec["blue"]
|
||||
recent_blues.append(int(bl) if isinstance(bl, str) else (bl[0] if isinstance(bl, list) else bl))
|
||||
|
||||
bf = Counter(recent_blues)
|
||||
total_blue = len(recent_blues)
|
||||
self.blue_score = {}
|
||||
for i in range(1, bmax + 1):
|
||||
self.blue_score[i] = (bf.get(i, 0) / total_blue * 0.8 + 1.0 / bmax * 0.2) if total_blue > 0 else (1.0 / bmax)
|
||||
|
||||
# 归一化
|
||||
max_r = max(self.red_score.values()) if self.red_score else 1
|
||||
max_b = max(self.blue_score.values()) if self.blue_score else 1
|
||||
for i in self.red_score:
|
||||
self.red_score[i] /= max_r
|
||||
for i in self.blue_score:
|
||||
self.blue_score[i] /= max_b
|
||||
|
||||
|
||||
# ==================== 模型4:K-Means聚类模型 ====================
|
||||
|
||||
class KMeansModel:
|
||||
"""基于历史聚类中心的推荐模型"""
|
||||
def __init__(self, lt, data, rule):
|
||||
self.lt = lt
|
||||
self.data = data
|
||||
self.rule = rule
|
||||
self.rcnt = rule["red_count"]
|
||||
self.rmax = rule["red_max"]
|
||||
self.bcnt = rule["blue_count"]
|
||||
self.bmax = rule["blue_max"]
|
||||
self.red_score = {}
|
||||
self.blue_score = {}
|
||||
self.analyze()
|
||||
|
||||
def analyze(self):
|
||||
rule, rmax, bmax, rcnt, bcnt = self.rule, self.rule["red_max"], self.rule["blue_max"], self.rule["red_count"], self.rule["blue_max"]
|
||||
|
||||
# 提取所有历史红球组合
|
||||
clusters = []
|
||||
for rec in self.data:
|
||||
nums = sorted(int(x) for x in rec["reds"])
|
||||
if len(nums) == rcnt:
|
||||
clusters.append(nums)
|
||||
|
||||
if len(clusters) < 3:
|
||||
# 数据不足,退化为频率模型
|
||||
rf = Counter()
|
||||
for nums in clusters:
|
||||
for n in nums:
|
||||
rf[n] += 1
|
||||
max_f = max(rf.values()) if rf else 1
|
||||
self.red_score = {i: rf.get(i, 0) / max_f for i in range(1, rmax + 1)}
|
||||
return
|
||||
|
||||
# 简化版K-Means:使用质心法
|
||||
# 计算每个位置的平均值(排序后)
|
||||
sorted_clusters = [sorted(c) for c in clusters]
|
||||
n_clusters = min(5, len(sorted_clusters))
|
||||
|
||||
# 计算每个位置的聚类中心
|
||||
position_means = [[] for _ in range(rcnt)]
|
||||
for cluster in sorted_clusters:
|
||||
for pos in range(rcnt):
|
||||
position_means[pos].append(cluster[pos])
|
||||
|
||||
# 计算每个号码的"接近中心"得分
|
||||
position_avg = [sum(p) / len(p) for p in position_means if p]
|
||||
|
||||
self.red_score = {}
|
||||
for i in range(1, rmax + 1):
|
||||
# 计算该号码在所有位置上偏离平均值的程度
|
||||
deviations = []
|
||||
for pos, avg in enumerate(position_avg):
|
||||
if len(sorted_clusters) > pos:
|
||||
cluster_at_pos = [c[pos] for c in sorted_clusters if len(c) > pos]
|
||||
if cluster_at_pos:
|
||||
deviations.append(abs(i - avg))
|
||||
avg_dev = sum(deviations) / len(deviations) if deviations else rmax / 2
|
||||
# 得分:偏离越小得分越高(越接近典型组合)
|
||||
score = max(0, 1 - avg_dev / (rmax * 0.5))
|
||||
self.red_score[i] = max(score, 0.01) # 最小0.01
|
||||
|
||||
# 蓝球:直接用频率
|
||||
all_blues = []
|
||||
for rec in self.data:
|
||||
bl = rec["blue"]
|
||||
all_blues.append(int(bl) if isinstance(bl, str) else (bl[0] if isinstance(bl, list) else bl))
|
||||
bf = Counter(all_blues)
|
||||
max_bf = max(bf.values()) if bf else 1
|
||||
self.blue_score = {i: bf.get(i, 0) / max_bf for i in range(1, bmax + 1)}
|
||||
for i in range(1, bmax + 1):
|
||||
if self.blue_score[i] == 0:
|
||||
self.blue_score[i] = 0.01
|
||||
|
||||
# 归一化
|
||||
max_r = max(self.red_score.values()) if self.red_score else 1
|
||||
max_b = max(self.blue_score.values()) if self.blue_score else 1
|
||||
for i in self.red_score:
|
||||
self.red_score[i] /= max_r
|
||||
for i in self.blue_score:
|
||||
self.blue_score[i] /= max_b
|
||||
|
||||
|
||||
# ==================== 模型5:残差回归模型 ====================
|
||||
|
||||
class ResidualModel:
|
||||
"""残差分析 + 回归预测模型"""
|
||||
def __init__(self, lt, data, rule):
|
||||
self.lt = lt
|
||||
self.data = data
|
||||
self.rule = rule
|
||||
self.rcnt = rule["red_count"]
|
||||
self.rmax = rule["red_max"]
|
||||
self.bcnt = rule["blue_count"]
|
||||
self.bmax = rule["blue_max"]
|
||||
self.red_score = {}
|
||||
self.blue_score = {}
|
||||
self.analyze()
|
||||
|
||||
def analyze(self):
|
||||
rule, rmax, bmax, rcnt, bcnt = self.rule, self.rule["red_max"], self.rule["blue_max"], self.rule["red_count"], self.rule["blue_count"]
|
||||
|
||||
# 计算每期的和值、奇偶比、大小比作为特征
|
||||
sums = []
|
||||
for rec in self.data:
|
||||
s = sum(int(x) for x in rec["reds"])
|
||||
sums.append(s)
|
||||
|
||||
if len(sums) < 10:
|
||||
# 数据不足,退化为均匀分布
|
||||
self.red_score = {i: 1.0 / rmax for i in range(1, rmax + 1)}
|
||||
self.blue_score = {i: 1.0 / bmax for i in range(1, bmax + 1)}
|
||||
return
|
||||
|
||||
# 计算和值的移动平均(最近10期)
|
||||
window = min(10, len(sums))
|
||||
recent_sums = sums[-window:]
|
||||
avg_sum = sum(recent_sums) / len(recent_sums)
|
||||
std_sum = math.sqrt(sum((s - avg_sum) ** 2 for s in recent_sums) / len(recent_sums))
|
||||
|
||||
# 计算每个号码在"和值附近"组合中的出现频率
|
||||
# 思路:哪些号码更常出现在和值接近均值的组合中
|
||||
score_by_num = {i: 0.0 for i in range(1, rmax + 1)}
|
||||
count_by_num = {i: 0 for i in range(1, rmax + 1)}
|
||||
|
||||
for rec in self.data:
|
||||
nums = [int(x) for x in rec["reds"]]
|
||||
s = sum(nums)
|
||||
# 计算该组合和值与均值的距离
|
||||
dist = abs(s - avg_sum)
|
||||
if dist <= std_sum * 2: # 落在2个标准差内
|
||||
for n in nums:
|
||||
score_by_num[n] += 1 / (dist + 1) # 距离越近得分越高
|
||||
count_by_num[n] += 1
|
||||
|
||||
# 归一化
|
||||
max_score = max(score_by_num.values()) if max(score_by_num.values()) > 0 else 1
|
||||
self.red_score = {}
|
||||
for i in range(1, rmax + 1):
|
||||
if count_by_num[i] > 0:
|
||||
self.red_score[i] = (score_by_num[i] / count_by_num[i]) / max_score
|
||||
else:
|
||||
self.red_score[i] = 1.0 / rmax # 均匀分布
|
||||
|
||||
# 蓝球:和值相关性分析
|
||||
blues_by_sum = defaultdict(list)
|
||||
for rec in self.data:
|
||||
bl = rec["blue"]
|
||||
bl_val = int(bl) if isinstance(bl, str) else (bl[0] if isinstance(bl, list) else bl)
|
||||
s = sum(int(x) for x in rec["reds"])
|
||||
blues_by_sum[s].append(bl_val)
|
||||
|
||||
self.blue_score = {}
|
||||
for i in range(1, bmax + 1):
|
||||
# 计算该蓝球在"和值附近"出现的频率
|
||||
total = 0
|
||||
for s, blues in blues_by_sum.items():
|
||||
dist = abs(s - avg_sum)
|
||||
if dist <= std_sum * 2:
|
||||
total += len([b for b in blues if b == i]) / (dist + 1)
|
||||
self.blue_score[i] = max(total / 100, 1.0 / bmax)
|
||||
|
||||
# 归一化
|
||||
max_r = max(self.red_score.values()) if self.red_score else 1
|
||||
max_b = max(self.blue_score.values()) if self.blue_score else 1
|
||||
for i in self.red_score:
|
||||
self.red_score[i] /= max_r
|
||||
for i in self.blue_score:
|
||||
self.blue_score[i] /= max_b
|
||||
|
||||
|
||||
# ==================== 集成学习引擎 ====================
|
||||
|
||||
class EnsembleLearner:
|
||||
"""集成学习引擎:多模型加权 + 回测优化权重"""
|
||||
|
||||
def __init__(self, lt, data, rule):
|
||||
self.lt = lt
|
||||
self.data = data
|
||||
self.rule = rule
|
||||
self.rcnt = rule["red_count"]
|
||||
self.rmax = rule["red_max"]
|
||||
self.bcnt = rule["blue_count"]
|
||||
self.bmax = rule["blue_max"]
|
||||
|
||||
# 初始化5个模型
|
||||
self.models = {
|
||||
'freq': FrequencyModel(lt, data, rule),
|
||||
'bayes': BayesianModel(lt, data, rule),
|
||||
'markov': MarkovModel(lt, data, rule),
|
||||
'kmeans': KMeansModel(lt, data, rule),
|
||||
'residual': ResidualModel(lt, data, rule)
|
||||
}
|
||||
|
||||
# 初始权重(等权)
|
||||
self.weights = {m: 1.0 / len(self.models) for m in self.models}
|
||||
self.initial_weights = self.weights.copy()
|
||||
|
||||
# 回测结果
|
||||
self.backtest_results = None
|
||||
|
||||
def get_combined_score(self):
|
||||
"""获取集成后的综合评分"""
|
||||
red_combined = {}
|
||||
blue_combined = {}
|
||||
|
||||
for model_name, model in self.models.items():
|
||||
w = self.weights[model_name]
|
||||
for i in range(1, self.rmax + 1):
|
||||
red_combined[i] = red_combined.get(i, 0) + w * model.red_score[i]
|
||||
for i in range(1, self.bmax + 1):
|
||||
blue_combined[i] = blue_combined.get(i, 0) + w * model.blue_score[i]
|
||||
|
||||
# 归一化
|
||||
max_r = max(red_combined.values()) if red_combined else 1
|
||||
max_b = max(blue_combined.values()) if blue_combined else 1
|
||||
for i in red_combined:
|
||||
red_combined[i] /= max_r
|
||||
for i in blue_combined:
|
||||
blue_combined[i] /= max_b
|
||||
|
||||
return red_combined, blue_combined
|
||||
|
||||
def _get_combined_for_weights(self, weights):
|
||||
"""根据给定权重获取组合评分"""
|
||||
red_c = {}
|
||||
blue_c = {}
|
||||
for model_name, model in self.models.items():
|
||||
ww = weights[model_name]
|
||||
for i in range(1, self.rmax + 1):
|
||||
red_c[i] = red_c.get(i, 0) + ww * model.red_score[i]
|
||||
for i in range(1, self.bmax + 1):
|
||||
blue_c[i] = blue_c.get(i, 0) + ww * model.blue_score[i]
|
||||
return red_c, blue_c
|
||||
|
||||
def _predict_from_scores(self, red_c, blue_c, rmax, bmax, rcnt, bcnt):
|
||||
"""根据评分预测号码"""
|
||||
sorted_r = sorted(red_c.items(), key=lambda x: x[1], reverse=True)
|
||||
top_r = [n for n, s in sorted_r[:rmax]]
|
||||
sorted_b = sorted(blue_c.items(), key=lambda x: x[1], reverse=True)
|
||||
top_b = [n for n, s in sorted_b[:bmax]]
|
||||
|
||||
sel = []
|
||||
cand = top_r[:]
|
||||
weights_r = [red_c.get(n, 0.5) for n in cand]
|
||||
while len(sel) < rcnt and cand:
|
||||
tw = sum(weights_r)
|
||||
if tw <= 0: pk = random.choice(cand)
|
||||
else:
|
||||
rv = random.random() * tw
|
||||
cum = 0
|
||||
for i, ww in enumerate(weights_r):
|
||||
cum += ww
|
||||
if rv <= cum:
|
||||
pk = cand[i]
|
||||
break
|
||||
if pk not in sel: sel.append(pk)
|
||||
idx = cand.index(pk); cand.pop(idx); weights_r.pop(idx)
|
||||
sel.sort()
|
||||
|
||||
bls = []
|
||||
cand_b = top_b[:]
|
||||
weights_b = [blue_c.get(n, 0.5) for n in cand_b]
|
||||
while len(bls) < bcnt and cand_b:
|
||||
tw = sum(weights_b)
|
||||
if tw <= 0: pk = random.choice(cand_b)
|
||||
else:
|
||||
rv = random.random() * tw
|
||||
cum = 0
|
||||
for i, ww in enumerate(weights_b):
|
||||
cum += ww
|
||||
if rv <= cum:
|
||||
pk = cand_b[i]
|
||||
break
|
||||
if pk not in bls: bls.append(pk)
|
||||
idx = cand_b.index(pk); cand_b.pop(idx); weights_b.pop(idx)
|
||||
bls.sort()
|
||||
|
||||
return sel, bls
|
||||
|
||||
def backtest_optimize(self, test_periods=30):
|
||||
"""
|
||||
通过回测优化各模型权重
|
||||
使用简单网格搜索 + 梯度上升混合方法
|
||||
"""
|
||||
rule = self.rule
|
||||
rmax, bmax = self.rule["red_max"], self.rule["blue_max"]
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"🧠 集成学习权重优化 | 彩种: {rule['name']} | 数据期数: {len(self.data)}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
# 准备回测数据(按时间顺序)
|
||||
sorted_data = sorted(self.data, key=lambda x: int(x["issue"]))
|
||||
if len(sorted_data) < test_periods + 10:
|
||||
test_periods = max(10, len(sorted_data) - 10)
|
||||
|
||||
# 评分函数:计算推荐与实际匹配的分数
|
||||
def evaluate(weights, data_slice):
|
||||
red_comp, blue_comp = self._get_combined_for_weights(weights)
|
||||
pred_r, pred_b = self._predict_from_scores(red_comp, blue_comp, rmax, bmax, rule["red_count"], rule["blue_count"])
|
||||
|
||||
actual = data_slice
|
||||
act_r = [int(x) for x in actual["reds"]]
|
||||
act_b = actual["blue"] if isinstance(actual["blue"], list) else [int(actual["blue"])]
|
||||
|
||||
# 评分:命中红球数*2 + 命中蓝球数
|
||||
hit_r = sum(1 for n in pred_r if n in act_r)
|
||||
hit_b = sum(1 for n in pred_b if n in act_b if (isinstance(act_b, list) and n in act_b) or (isinstance(act_b, int) and n == act_b))
|
||||
return hit_r * 2 + hit_b
|
||||
|
||||
# 简单网格搜索优化权重
|
||||
best_weights = self.weights.copy()
|
||||
best_score = float('-inf')
|
||||
|
||||
# 初始评估
|
||||
current_score = 0
|
||||
for i in range(test_periods):
|
||||
if i + 10 < len(sorted_data):
|
||||
slice_data = sorted_data[i + 10:i + 11] # 下一期作为测试
|
||||
current_score += evaluate(self.weights, slice_data[0])
|
||||
|
||||
print(f" 初始等权总分: {current_score:.1f}")
|
||||
|
||||
# 简单梯度上升(每个模型权重微调)
|
||||
improved = True
|
||||
iterations = 0
|
||||
max_iterations = 20
|
||||
|
||||
while improved and iterations < max_iterations:
|
||||
improved = False
|
||||
iterations += 1
|
||||
|
||||
for model_name in self.weights:
|
||||
for delta in [-0.05, 0.05]: # 微调权重
|
||||
test_w = self.weights.copy()
|
||||
test_w[model_name] = max(0.01, min(0.9, test_w[model_name] + delta))
|
||||
|
||||
# 归一化
|
||||
total = sum(test_w.values())
|
||||
test_w = {k: v/total for k, v in test_w.items()}
|
||||
|
||||
score = 0
|
||||
for i in range(test_periods):
|
||||
if i + 10 < len(sorted_data):
|
||||
slice_data = sorted_data[i + 10:i + 11]
|
||||
score += evaluate(test_w, slice_data[0])
|
||||
|
||||
if score > best_score:
|
||||
best_score = score
|
||||
best_weights = test_w.copy()
|
||||
improved = True
|
||||
|
||||
self.weights = best_weights
|
||||
self.backtest_results = {
|
||||
'iterations': iterations,
|
||||
'best_score': best_score,
|
||||
'test_periods': test_periods,
|
||||
'model_weights': best_weights
|
||||
}
|
||||
|
||||
# 输出各模型权重
|
||||
print(f" 优化后权重:")
|
||||
for m, w in sorted(self.weights.items(), key=lambda x: -x[1]):
|
||||
model_names = {
|
||||
'freq': '频率加权',
|
||||
'bayes': '贝叶斯平滑',
|
||||
'markov': '马尔可夫链',
|
||||
'kmeans': 'K-Means聚类',
|
||||
'residual': '残差回归'
|
||||
}
|
||||
print(f" {model_names.get(m, m):<12s}: {w:.4f}")
|
||||
|
||||
print(f" 最终集成总分: {best_score:.1f} (测试{test_periods}期)")
|
||||
|
||||
# 返回综合评分
|
||||
return self.get_combined_score()
|
||||
|
||||
def get_recommendations(self, count=5):
|
||||
"""基于集成评分生成推荐"""
|
||||
red_comp, blue_comp = self.get_combined_score()
|
||||
rmax, bmax = self.rule["red_max"], self.rule["blue_max"]
|
||||
rcnt, bcnt = self.rule["red_count"], self.rule["blue_count"]
|
||||
|
||||
sorted_r = sorted(red_comp.items(), key=lambda x: x[1], reverse=True)
|
||||
top_r = [n for n, s in sorted_r[:rmax]]
|
||||
sorted_b = sorted(blue_comp.items(), key=lambda x: x[1], reverse=True)
|
||||
top_b = [n for n, s in sorted_b[:bmax]]
|
||||
|
||||
seed = hashlib.md5(f"{self.lt}_ensemble".encode()).hexdigest()
|
||||
res = []
|
||||
for ci in range(count):
|
||||
cs = hashlib.md5(f"ensemble_c{ci}_{seed}".encode()).hexdigest()
|
||||
cr = random.Random(cs)
|
||||
|
||||
sel = []
|
||||
cand = top_r[:]
|
||||
w = [red_comp.get(n, 0.5) for n in cand]
|
||||
while len(sel) < rcnt and cand:
|
||||
tw = sum(w)
|
||||
if tw <= 0: pk = cr.choice(cand)
|
||||
else:
|
||||
rv = cr.random() * tw
|
||||
cum = 0
|
||||
for i, ww in enumerate(w):
|
||||
cum += ww
|
||||
if rv <= cum:
|
||||
pk = cand[i]
|
||||
break
|
||||
if pk not in sel: sel.append(pk)
|
||||
idx = cand.index(pk); cand.pop(idx); w.pop(idx)
|
||||
sel.sort()
|
||||
|
||||
bls = []
|
||||
cand_b = top_b[:]
|
||||
wb = [blue_comp.get(n, 0.5) for n in cand_b]
|
||||
while len(bls) < bcnt and cand_b:
|
||||
tw = sum(wb)
|
||||
if tw <= 0: pk = cr.choice(cand_b)
|
||||
else:
|
||||
rv = cr.random() * tw
|
||||
cum = 0
|
||||
for i, ww in enumerate(wb):
|
||||
cum += ww
|
||||
if rv <= cum:
|
||||
pk = cand_b[i]
|
||||
break
|
||||
if pk not in bls: bls.append(pk)
|
||||
idx = cand_b.index(pk); cand_b.pop(idx); wb.pop(idx)
|
||||
bls.sort()
|
||||
|
||||
score = round(sum(red_comp.get(n, 0) for n in sel) + sum(blue_comp.get(n, 0) for n in bls), 4)
|
||||
mid = rmax / 2
|
||||
res.append({
|
||||
"reds": sel,
|
||||
"blues": bls,
|
||||
"score": score,
|
||||
"odd_even": f"{sum(1 for n in sel if n % 2 == 1)}:{rcnt - sum(1 for n in sel if n % 2 == 1)}",
|
||||
"big_small": f"{sum(1 for n in sel if n > mid)}:{rcnt - sum(1 for n in sel if n > mid)}",
|
||||
"sum": sum(sel)
|
||||
})
|
||||
|
||||
res.sort(key=lambda x: x["score"], reverse=True)
|
||||
return res
|
||||
|
||||
|
||||
# ==================== 主函数 ====================
|
||||
|
||||
def ensemble_recommendation(lt, data, rule, count=5):
|
||||
"""集成学习推荐主接口"""
|
||||
learner = EnsembleLearner(lt, data, rule)
|
||||
red_comp, blue_comp = learner.backtest_optimize(test_periods=30)
|
||||
recommendations = learner.get_recommendations(count=count)
|
||||
|
||||
return {
|
||||
'weights': learner.weights,
|
||||
'backtest': learner.backtest_results,
|
||||
'recommendations': recommendations,
|
||||
'combined_scores': {
|
||||
'red': red_comp,
|
||||
'blue': blue_comp
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 简单测试
|
||||
print("集成学习模块加载成功!")
|
||||
print("可用模型:频率加权、贝叶斯平滑、马尔可夫链、K-Means聚类、残差回归")
|
||||
@@ -0,0 +1,285 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
回滚迭代跟踪工具 - 管理 AI 进化版本和回滚机制
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional, Any
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class EvolutionTracker:
|
||||
"""回滚迭代跟踪器"""
|
||||
|
||||
def __init__(self, data_dir: str = "./evolution_tracking"):
|
||||
self.data_dir = Path(data_dir)
|
||||
self.data_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.version_file = self.data_dir / "versions.json"
|
||||
self.performance_log = self.data_dir / "performance.json"
|
||||
self.rollback_log = self.data_dir / "rollbacks.json"
|
||||
|
||||
def initialize(self):
|
||||
"""初始化跟踪器"""
|
||||
# 初始化版本记录
|
||||
if not self.version_file.exists():
|
||||
initial_version = {
|
||||
"version": 0,
|
||||
"created_at": datetime.now().isoformat(),
|
||||
"performance": {
|
||||
"win_rate": 0.0,
|
||||
"total_bets": 0,
|
||||
"total_winnings": 0.0
|
||||
}
|
||||
}
|
||||
self.save_version(initial_version)
|
||||
|
||||
# 初始化性能日志
|
||||
if not self.performance_log.exists():
|
||||
self.save_performance({
|
||||
"history": [],
|
||||
"current": {
|
||||
"total_bets": 0,
|
||||
"win_rate": 0.0,
|
||||
"earnings": 0.0,
|
||||
"total_winnings": 0.0
|
||||
}
|
||||
})
|
||||
|
||||
# 初始化回滚日志
|
||||
if not self.rollback_log.exists():
|
||||
self.save_rollbacks({
|
||||
"history": [],
|
||||
"current_version": 0
|
||||
})
|
||||
|
||||
def save_version(self, version_data: Dict):
|
||||
"""保存版本信息"""
|
||||
with open(self.version_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(version_data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
def load_version(self) -> Dict:
|
||||
"""加载当前版本"""
|
||||
if self.version_file.exists():
|
||||
with open(self.version_file, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
return {}
|
||||
|
||||
def record_performance(self, version: int, win_rate: float, earnings: float):
|
||||
"""记录性能数据"""
|
||||
performance = self.load_performance()
|
||||
|
||||
entry = {
|
||||
"version": version,
|
||||
"recorded_at": datetime.now().isoformat(),
|
||||
"win_rate": win_rate,
|
||||
"earnings": earnings,
|
||||
"total_bets": performance["current"].get("total_bets", 0),
|
||||
"total_winnings": performance["current"].get("total_winnings", 0.0)
|
||||
}
|
||||
|
||||
performance["history"].append(entry)
|
||||
|
||||
# 更新当前性能
|
||||
current = performance["current"]
|
||||
current["total_bets"] += 1
|
||||
current["win_rate"] = win_rate
|
||||
current["earnings"] = earnings
|
||||
current["total_winnings"] = current.get("total_winnings", 0.0) + earnings
|
||||
|
||||
self.save_performance(performance)
|
||||
|
||||
def load_performance(self) -> Dict:
|
||||
"""加载性能日志"""
|
||||
if self.performance_log.exists():
|
||||
with open(self.performance_log, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
return {"history": [], "current": {}}
|
||||
|
||||
def save_performance(self, performance: Dict):
|
||||
"""保存性能日志"""
|
||||
with open(self.performance_log, 'w', encoding='utf-8') as f:
|
||||
json.dump(performance, f, ensure_ascii=False, indent=2)
|
||||
|
||||
def record_rollback(self, from_version: int, to_version: int, reason: str):
|
||||
"""记录回滚操作"""
|
||||
rollbacks = self.load_rollbacks()
|
||||
|
||||
entry = {
|
||||
"from_version": from_version,
|
||||
"to_version": to_version,
|
||||
"reason": reason,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
rollbacks["history"].append(entry)
|
||||
rollbacks["current_version"] = to_version
|
||||
|
||||
self.save_rollbacks(rollbacks)
|
||||
|
||||
def load_rollbacks(self) -> Dict:
|
||||
"""加载回滚日志"""
|
||||
if self.rollback_log.exists():
|
||||
with open(self.rollback_log, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
return {"history": [], "current_version": 0}
|
||||
|
||||
def save_rollbacks(self, rollbacks: Dict):
|
||||
"""保存回滚日志"""
|
||||
with open(self.rollback_log, 'w', encoding='utf-8') as f:
|
||||
json.dump(rollbacks, f, ensure_ascii=False, indent=2)
|
||||
|
||||
def get_version_history(self) -> List[Dict]:
|
||||
"""获取版本历史"""
|
||||
performance = self.load_performance()
|
||||
return performance.get("history", [])
|
||||
|
||||
def get_best_version(self) -> Optional[Dict]:
|
||||
"""获取性能最好的版本"""
|
||||
history = self.get_version_history()
|
||||
if not history:
|
||||
return None
|
||||
|
||||
best = max(history, key=lambda x: x.get("win_rate", 0))
|
||||
return best
|
||||
|
||||
def compare_versions(self, version1: int, version2: int) -> Dict:
|
||||
"""比较两个版本的性能"""
|
||||
history = self.get_version_history()
|
||||
|
||||
v1_data = next((h for h in history if h["version"] == version1), None)
|
||||
v2_data = next((h for h in history if h["version"] == version2), None)
|
||||
|
||||
if not v1_data or not v2_data:
|
||||
return {"error": "版本不存在"}
|
||||
|
||||
comparison = {
|
||||
"version1": version1,
|
||||
"version2": version2,
|
||||
"v1_win_rate": v1_data.get("win_rate", 0),
|
||||
"v2_win_rate": v2_data.get("win_rate", 0),
|
||||
"v1_earnings": v1_data.get("earnings", 0.0),
|
||||
"v2_earnings": v2_data.get("earnings", 0.0),
|
||||
"better_version": version1 if v1_data.get("win_rate", 0) > v2_data.get("win_rate", 0) else version2
|
||||
}
|
||||
|
||||
return comparison
|
||||
|
||||
def generate_report(self) -> str:
|
||||
"""生成进化报告"""
|
||||
performance = self.load_performance()
|
||||
rollbacks = self.load_rollbacks()
|
||||
|
||||
report = [
|
||||
"="*60,
|
||||
"AI 进化跟踪报告",
|
||||
"="*60,
|
||||
f"当前版本:{rollbacks.get('current_version', 0)}",
|
||||
f"回滚历史数:{len(rollbacks.get('history', []))}",
|
||||
"\n当前性能数据:",
|
||||
f" 胜率:{performance['current'].get('win_rate', 0):.2%}",
|
||||
f" 累计投注:{performance['current'].get('total_bets', 0)}",
|
||||
f" 累计收益:¥{performance['current'].get('total_winnings', 0.0):.2f}",
|
||||
"\n版本历史:",
|
||||
]
|
||||
|
||||
history = performance.get("history", [])
|
||||
if history:
|
||||
for i, entry in enumerate(history[-10:], 1): # 显示最后 10 个
|
||||
report.extend([
|
||||
f" [v{entry['version']}] 胜率:{entry['win_rate']:.2%}, "
|
||||
f"收益:¥{entry['earnings']:.2f}, "
|
||||
f"记录时间:{entry['recorded_at']}",
|
||||
""
|
||||
])
|
||||
else:
|
||||
report.append(" 暂无历史数据")
|
||||
|
||||
report.extend(["\n回滚历史:", ""])
|
||||
|
||||
if rollbacks.get("history"):
|
||||
for i, entry in enumerate(rollbacks["history"]):
|
||||
report.extend([
|
||||
f" 回滚 #{i+1}: v{entry['from_version']} → v{entry['to_version']}, "
|
||||
f"原因:{entry['reason']}, "
|
||||
f"时间:{entry['timestamp']}",
|
||||
""
|
||||
])
|
||||
else:
|
||||
report.append(" 暂无回滚记录")
|
||||
|
||||
report.extend(["="*60, ""])
|
||||
return "\n".join(report)
|
||||
|
||||
def optimize_weights(self, learning_rate: float = 0.1, epochs: int = 100) -> Dict:
|
||||
"""优化模型权重 - 模拟进化过程"""
|
||||
import random
|
||||
|
||||
# 初始化权重
|
||||
weights = {
|
||||
"red_hot": 0.3,
|
||||
"red_cold": 0.2,
|
||||
"consecutive": 0.15,
|
||||
"sum_range": 0.15,
|
||||
"parity": 0.1,
|
||||
"entropy": 0.1
|
||||
}
|
||||
|
||||
# 模拟进化过程
|
||||
for epoch in range(epochs):
|
||||
# 模拟数据输入
|
||||
sample_data = [random.randint(1, 33) for _ in range(10)]
|
||||
|
||||
# 计算损失
|
||||
loss = sum((w - random.random())**2 for w in weights.values())
|
||||
|
||||
# 梯度下降
|
||||
for key in weights:
|
||||
gradient = sum(random.random() for _ in range(10)) / len(sample_data)
|
||||
weights[key] -= learning_rate * gradient
|
||||
|
||||
# 归一化权重
|
||||
total = sum(weights.values())
|
||||
weights = {k: round(v/total, 4) for k, v in weights.items()}
|
||||
|
||||
return {
|
||||
"optimized_weights": weights,
|
||||
"epochs_completed": epochs,
|
||||
"final_loss": round(loss, 4)
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数 - 测试回滚跟踪工具"""
|
||||
tracker = EvolutionTracker()
|
||||
tracker.initialize()
|
||||
|
||||
# 模拟一些版本记录
|
||||
versions = [
|
||||
(1, 0.05, 100.0),
|
||||
(2, 0.12, 150.0),
|
||||
(3, 0.08, 120.0),
|
||||
(4, 0.15, 200.0),
|
||||
(5, 0.18, 250.0)
|
||||
]
|
||||
|
||||
for version, win_rate, earnings in versions:
|
||||
tracker.record_performance(version, win_rate, earnings)
|
||||
|
||||
# 模拟回滚
|
||||
tracker.record_rollback(5, 4, "性能下降")
|
||||
|
||||
# 生成报告
|
||||
print(tracker.generate_report())
|
||||
|
||||
# 优化权重
|
||||
optimized = tracker.optimize_weights()
|
||||
print("\n优化结果:")
|
||||
print(f" 完成轮数:{optimized['epochs_completed']}")
|
||||
print(f" 最终损失:{optimized['final_loss']}")
|
||||
print(f" 优化权重:{optimized['optimized_weights']}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,300 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
新模型集成入口:替代 lottery_tool.py 的 full_report
|
||||
保持4块结构化输出,推理分析和机选号码使用6阶段数学模型
|
||||
"""
|
||||
|
||||
import json, math, os, datetime, hashlib, random, re, sys
|
||||
from collections import Counter
|
||||
import numpy as np
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
DATA_DIR = os.path.join(BASE_DIR, "data")
|
||||
OUTPUT_DIR = os.path.join(BASE_DIR, "model_output")
|
||||
SSQ_FILE = os.path.join(DATA_DIR, "ssq_history.json")
|
||||
FEEDBACK_FILE = os.path.join(DATA_DIR, "feedback.json")
|
||||
CONFIG_FILE = os.path.join(DATA_DIR, "config.json")
|
||||
|
||||
# ================================================================
|
||||
# 加载数据和模型参数
|
||||
# ================================================================
|
||||
data = json.load(open(SSQ_FILE, "r", encoding="utf-8"))
|
||||
records = []
|
||||
historical_combos = set()
|
||||
for r in data:
|
||||
reds = sorted([int(x) for x in r["reds"]])
|
||||
blue = int(r["blue"])
|
||||
if len(reds) == 6 and all(1 <= n <= 33 for n in reds) and 1 <= blue <= 16:
|
||||
records.append({"issue": r["issue"], "reds": reds, "blue": blue, "date": r.get("date", "")})
|
||||
historical_combos.add(tuple(reds))
|
||||
|
||||
records.sort(key=lambda x: int(x["issue"]))
|
||||
N = len(records)
|
||||
latest = records[-1]
|
||||
|
||||
# 加载模型参数
|
||||
mp_file = os.path.join(OUTPUT_DIR, "model_parameters.json")
|
||||
if os.path.exists(mp_file):
|
||||
mp = json.load(open(mp_file, "r"))
|
||||
MODEL_VERSION = "v1.0_6phase"
|
||||
else:
|
||||
mp = None
|
||||
MODEL_VERSION = "v1.0_legacy"
|
||||
|
||||
# ================================================================
|
||||
# 加载回滚统计
|
||||
# ================================================================
|
||||
config = json.load(open(CONFIG_FILE, "r")) if os.path.exists(CONFIG_FILE) else {"iteration_count": 0}
|
||||
feedback = json.load(open(FEEDBACK_FILE, "r")) if os.path.exists(FEEDBACK_FILE) else {
|
||||
"iterations": [], "accuracy_history": [],
|
||||
"ssq": {"total": 0, "correct_reds": 0, "correct_blues": 0, "best": 0, "profitable_wins": 0, "total_earnings": 0.0}
|
||||
}
|
||||
|
||||
# ================================================================
|
||||
# 计算下期信息
|
||||
# ================================================================
|
||||
prev_issue = latest["issue"]
|
||||
prev_date = latest.get("date", "")
|
||||
prev_reds = latest["reds"]
|
||||
prev_blue = latest["blue"]
|
||||
next_issue = str(int(prev_issue) + 1)
|
||||
|
||||
today = datetime.datetime.now()
|
||||
draw_days = [2, 4, 7] # 周二、四、日
|
||||
draw_time = "21:15"
|
||||
draw_hour, draw_min = map(int, draw_time.split(":"))
|
||||
now_hour, now_min = today.hour, today.minute
|
||||
today_draw = today.isoweekday() in draw_days
|
||||
time_past = (now_hour > draw_hour) or (now_hour == draw_hour and now_min >= draw_min)
|
||||
if today_draw and not time_past:
|
||||
delta = 0
|
||||
else:
|
||||
delta = 1
|
||||
while (today + datetime.timedelta(days=delta)).isoweekday() not in draw_days:
|
||||
delta += 1
|
||||
next_draw = today + datetime.timedelta(days=delta)
|
||||
next_draw_str = next_draw.strftime("%Y-%m-%d")
|
||||
wd_map = {1:"周一",2:"周二",3:"周三",4:"周四",5:"周五",6:"周六",7:"周日"}
|
||||
next_wd = wd_map.get(next_draw.isoweekday(), "")
|
||||
|
||||
# ================================================================
|
||||
# 4块输出
|
||||
# ================================================================
|
||||
print("\n" + "=" * 60)
|
||||
print(" [1/4] 回滚迭代")
|
||||
print("=" * 60)
|
||||
|
||||
ver = config.get("iteration_count", 0)
|
||||
stats = feedback.get("ssq", {})
|
||||
total_recs = stats.get("total", 0)
|
||||
profitable = stats.get("profitable_wins", 0)
|
||||
win_rate = "%.1f%%" % (profitable / total_recs * 100) if total_recs > 0 else "0.0%"
|
||||
earnings = stats.get("total_earnings", 0.0)
|
||||
|
||||
print(" 当前版本号: %s (第6阶段数学建模)" % MODEL_VERSION)
|
||||
print(" 版本胜率: %s (%d期推荐 / %d次盈利)" % (win_rate, total_recs, profitable))
|
||||
print(" 累计中奖收益金额: RMB %.2f" % earnings)
|
||||
|
||||
# ==================== 第2块:数据采集 ====================
|
||||
print("\n" + "=" * 60)
|
||||
print(" [2/4] 数据采集")
|
||||
print("=" * 60)
|
||||
print(" 采集期数范围: 第%s期 ~ 第%s期(%d期)" % (records[0]["issue"], prev_issue, N))
|
||||
print(" 上一期号: 第%s期" % prev_issue)
|
||||
print(" 开奖日期: %s" % prev_date)
|
||||
r_str = " ".join("%02d" % n for n in prev_reds)
|
||||
print(" 开奖号码: 红球 %s 蓝球 %02d" % (r_str, prev_blue))
|
||||
|
||||
# ==================== 第3块:推理分析 ====================
|
||||
print("\n" + "=" * 60)
|
||||
print(" [3/4] 推理分析(%s | 数据源: 500.com)" % MODEL_VERSION)
|
||||
print("=" * 60)
|
||||
|
||||
if mp:
|
||||
# --- 6阶段模型输出 ---
|
||||
p3 = mp["phase3_params"]
|
||||
p4 = mp["phase4_params"]
|
||||
p5 = mp["phase5_params"]
|
||||
|
||||
# 步骤1: 贝叶斯后验概率 TOP10
|
||||
red_post = {int(k): float(v) for k, v in p3["red_posterior"].items()}
|
||||
top10 = sorted(red_post.items(), key=lambda x: -x[1])[:10]
|
||||
print(" >> 步骤1 - 贝叶斯后验概率: TOP10红球")
|
||||
print(" %s" % " ".join("%02d(%.4f)" % (n, s) for n, s in top10))
|
||||
|
||||
# 步骤2: 蓝球后验 TOP5
|
||||
blue_post = {int(k): float(v) for k, v in p3["blue_posterior"].items()}
|
||||
top5b = sorted(blue_post.items(), key=lambda x: -x[1])[:5]
|
||||
print(" >> 步骤2 - 蓝球后验概率: TOP5蓝球")
|
||||
print(" %s" % " ".join("%02d(%.4f)" % (n, s) for n, s in top5b))
|
||||
|
||||
# 步骤3: 聚类分组
|
||||
candidate = p4["candidate_pool"]
|
||||
eliminated = p4["eliminated"]
|
||||
print(" >> 步骤3 - K-Means聚类降维")
|
||||
print(" K=%d | 轮廓系数=%.4f" % (p4["red_k"], p4["red_silhouette"]))
|
||||
print(" 核心候选池(%d个): %s" % (len(candidate), " ".join("%02d" % n for n in candidate[:15]) + ("..." if len(candidate) > 15 else "")))
|
||||
|
||||
# 步骤4: 权重体系
|
||||
sw = p5["objective_weights"]
|
||||
print(" >> 步骤4 - 综合得分权重体系(6阶段数学模型)")
|
||||
print(" Score = %.1f×后验 + %.1f×泊松 + %.1f×伽马遗漏" % (sw["w1_posterior"], sw["w2_poisson"], sw["w3_gamma"]))
|
||||
print(" 方法链路: 泊松频次→负二项/伽马拟合→贝叶斯后验→K-Means→ILP约束求解")
|
||||
else:
|
||||
print(" [⚠️ 新模型参数未加载,使用基础统计]")
|
||||
|
||||
# ==================== 第4块:机选号码 ====================
|
||||
print("\n" + "=" * 60)
|
||||
print(" [4/4] 机选号码(第%s期 | %s %s)" % (next_issue, next_draw_str, next_wd))
|
||||
print("=" * 60)
|
||||
|
||||
if mp:
|
||||
# --- 6阶段模型推荐 ---
|
||||
w1 = p3["score_weights"]["alpha1_posterior"]
|
||||
w2 = p3["score_weights"]["alpha2_poisson"]
|
||||
w3 = p3["score_weights"]["alpha3_miss"]
|
||||
|
||||
red_freq = {int(k): v["freq"] for k, v in mp["phase1_params"]["red_freq"].items()}
|
||||
lam = mp["phase1_params"]["poisson_lambda_red"]
|
||||
|
||||
# 泊松权重
|
||||
red_poisson = {}
|
||||
for n in range(1, 34):
|
||||
k = red_freq.get(n, 0)
|
||||
log_p = k * math.log(lam) - lam
|
||||
for i in range(1, k + 1):
|
||||
log_p -= math.log(i)
|
||||
red_poisson[n] = math.exp(log_p)
|
||||
total_pois = sum(red_poisson.values())
|
||||
red_poisson = {n: v / total_pois for n, v in red_poisson.items()}
|
||||
|
||||
# 遗漏权重
|
||||
red_miss = mp["phase1_params"]["red_miss"]
|
||||
red_gamma = {}
|
||||
for n in range(1, 34):
|
||||
miss = red_miss.get(str(n), {"avg_miss": 5, "last_miss": 0})
|
||||
avg = miss["avg_miss"]
|
||||
last = miss["last_miss"]
|
||||
red_gamma[n] = min(1.0, last / (avg * 2)) if avg > 0 else 0.5
|
||||
total_gam = sum(red_gamma.values())
|
||||
red_gamma = {n: v / total_gam for n, v in red_gamma.items()}
|
||||
|
||||
# 综合得分
|
||||
red_score = {}
|
||||
for n in range(1, 34):
|
||||
red_score[n] = w1 * red_post[n] + w2 * red_poisson[n] + w3 * red_gamma[n]
|
||||
|
||||
# 蓝球得分
|
||||
blue_freq = {int(k): v["freq"] for k, v in mp["phase1_params"]["blue_freq"].items()}
|
||||
lam_b = mp["phase1_params"]["poisson_lambda_blue"]
|
||||
blue_poisson = {}
|
||||
for n in range(1, 17):
|
||||
k = blue_freq.get(n, 0)
|
||||
log_p = k * math.log(lam_b) - lam_b
|
||||
for i in range(1, k + 1):
|
||||
log_p -= math.log(i)
|
||||
blue_poisson[n] = math.exp(log_p)
|
||||
total_pois_b = sum(blue_poisson.values())
|
||||
blue_poisson = {n: v / total_pois_b for n, v in blue_poisson.items()}
|
||||
|
||||
blue_score = {}
|
||||
for n in range(1, 17):
|
||||
blue_score[n] = w1 * blue_post[n] + w2 * blue_poisson[n] + w3 * 0.5
|
||||
|
||||
# 约束检查
|
||||
constraints = p5["constraints"]
|
||||
def check_constraints(combo):
|
||||
odd = sum(1 for n in combo if n % 2 == 1)
|
||||
if not (constraints["odd_even"]["min_odd"] <= odd <= constraints["odd_even"]["max_odd"]):
|
||||
return False
|
||||
big = sum(1 for n in combo if n > 16)
|
||||
if not (constraints["big_small"]["min_big"] <= big <= constraints["big_small"]["max_big"]):
|
||||
return False
|
||||
consec = sum(1 for i in range(len(combo) - 1) if combo[i + 1] - combo[i] == 1)
|
||||
if not (constraints["consecutive"]["min_consec"] <= consec <= constraints["consecutive"]["max_consec"]):
|
||||
return False
|
||||
s = sum(combo)
|
||||
if not (constraints["sum_range"]["min_sum"] <= s <= constraints["sum_range"]["max_sum"]):
|
||||
return False
|
||||
hot = sum(1 for n in combo if red_score[n] > 0.03)
|
||||
if not (constraints["hot_cold"]["min_hot"] <= hot <= constraints["hot_cold"]["max_hot"]):
|
||||
return False
|
||||
z1 = sum(1 for n in combo if 1 <= n <= 11)
|
||||
z2 = sum(1 for n in combo if 12 <= n <= 22)
|
||||
z3 = sum(1 for n in combo if 23 <= n <= 33)
|
||||
if not all(constraints["zone_balance"]["min_zone"] <= z <= constraints["zone_balance"]["max_zone"] for z in [z1, z2, z3]):
|
||||
return False
|
||||
if tuple(combo) in historical_combos:
|
||||
return False
|
||||
return True
|
||||
|
||||
# 生成推荐
|
||||
import itertools
|
||||
sorted_nums = sorted(range(1, 34), key=lambda n: -red_score[n])
|
||||
best_combos = []
|
||||
seed = hashlib.md5(("6phase_ssq_%s" % prev_issue).encode()).hexdigest()
|
||||
rng = random.Random(seed)
|
||||
|
||||
for _ in range(30000):
|
||||
weights = np.array([red_score[n] for n in range(1, 34)])
|
||||
weights = weights / weights.sum()
|
||||
selected = np.random.choice(range(1, 34), size=6, replace=False, p=weights)
|
||||
combo = sorted(selected.tolist())
|
||||
if check_constraints(combo):
|
||||
score = sum(red_score[n] for n in combo)
|
||||
if len(best_combos) < 10 or score > best_combos[-1][0]:
|
||||
best_combos.append((score, combo))
|
||||
best_combos.sort(key=lambda x: -x[0])
|
||||
if len(best_combos) > 10:
|
||||
best_combos = best_combos[:10]
|
||||
|
||||
for combo in itertools.combinations(sorted_nums[:15], 6):
|
||||
combo_list = sorted(list(combo))
|
||||
if check_constraints(combo_list):
|
||||
score = sum(red_score[n] for n in combo_list)
|
||||
if len(best_combos) < 10 or score > best_combos[-1][0]:
|
||||
best_combos.append((score, combo_list))
|
||||
best_combos.sort(key=lambda x: -x[0])
|
||||
if len(best_combos) > 10:
|
||||
best_combos = best_combos[:10]
|
||||
|
||||
seen = set()
|
||||
unique = []
|
||||
for s, c in best_combos:
|
||||
if tuple(c) not in seen:
|
||||
seen.add(tuple(c))
|
||||
unique.append((s, c))
|
||||
results = unique[:5]
|
||||
|
||||
blue_ranked = sorted(blue_score.items(), key=lambda x: -x[1])[:3]
|
||||
primary_blue = blue_ranked[0][0]
|
||||
|
||||
for i, (score, combo) in enumerate(results):
|
||||
r_str = " ".join("%02d" % n for n in combo)
|
||||
odd = sum(1 for n in combo if n % 2 == 1)
|
||||
big = sum(1 for n in combo if n > 16)
|
||||
s = sum(combo)
|
||||
marker = " ⭐" if i == 0 else ""
|
||||
print("\n 第%d组: 红球 %s 蓝球 %02d%s" % (i + 1, r_str, primary_blue, marker))
|
||||
print(" 得分:%.4f | 奇偶:%d:%d | 大小:%d:%d | 和:%d" % (
|
||||
score, odd, 6 - odd, big, 6 - big, s))
|
||||
|
||||
# 保存推荐
|
||||
analysis = json.load(open(os.path.join(DATA_DIR, "analysis_ssq.json"), "r")) if os.path.exists(os.path.join(DATA_DIR, "analysis_ssq.json")) else {}
|
||||
analysis["last_recommendation"] = {
|
||||
"reds": results[0][1],
|
||||
"blues": [primary_blue],
|
||||
"generated_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"model_version": MODEL_VERSION
|
||||
}
|
||||
json.dump(analysis, open(os.path.join(DATA_DIR, "analysis_ssq.json"), "w"), ensure_ascii=False, indent=2)
|
||||
|
||||
else:
|
||||
print(" [⚠️ 新模型未加载,请先运行 model_phase1~6.py 训练模型]")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(" ⚠️ 免责声明: 本推荐仅基于历史统计建模的相对概率评分,")
|
||||
print(" 不预测下期开奖,不保证中奖。彩票是独立随机事件。")
|
||||
print("=" * 60)
|
||||
print("\n 开奖后把实际号码发我,帮你回滚迭代校正精度 🍀")
|
||||
@@ -0,0 +1,259 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
大乐透全量报告:与双色球 model_full_report.py 统一4块结构化输出
|
||||
使用集成学习模型(5模型融合),输出格式与双色球完全对齐
|
||||
"""
|
||||
|
||||
import json, math, os, datetime, hashlib, random, sys, io
|
||||
from collections import Counter
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
DATA_DIR = os.path.join(BASE_DIR, "data")
|
||||
OUTPUT_DIR = os.path.join(BASE_DIR, "model_output")
|
||||
DLT_FILE = os.path.join(DATA_DIR, "dlt_history.json")
|
||||
FEEDBACK_FILE = os.path.join(DATA_DIR, "feedback.json")
|
||||
CONFIG_FILE = os.path.join(DATA_DIR, "config.json")
|
||||
|
||||
# ================================================================
|
||||
# 加载数据
|
||||
# ================================================================
|
||||
data = json.load(open(DLT_FILE, "r", encoding="utf-8"))
|
||||
records = []
|
||||
historical_combos = set()
|
||||
for r in data:
|
||||
fronts = sorted([int(x) for x in r["reds"]])
|
||||
backs = sorted([int(x) for x in r["blue"]]) if isinstance(r["blue"], list) else [int(r["blue"])]
|
||||
if len(fronts) == 5 and all(1 <= n <= 35 for n in fronts) and len(backs) == 2 and all(1 <= n <= 12 for n in backs):
|
||||
records.append({"issue": r["issue"], "fronts": fronts, "backs": backs, "date": r.get("date", "")})
|
||||
historical_combos.add(tuple(fronts))
|
||||
|
||||
records.sort(key=lambda x: int(x["issue"]))
|
||||
N = len(records)
|
||||
latest = records[-1]
|
||||
|
||||
# ================================================================
|
||||
# 加载回滚统计
|
||||
# ================================================================
|
||||
config = json.load(open(CONFIG_FILE, "r")) if os.path.exists(CONFIG_FILE) else {"iteration_count": 0}
|
||||
feedback = json.load(open(FEEDBACK_FILE, "r")) if os.path.exists(FEEDBACK_FILE) else {
|
||||
"iterations": [], "accuracy_history": [],
|
||||
"ssq": {"total": 0, "correct_reds": 0, "correct_blues": 0, "best": 0, "profitable_wins": 0, "total_earnings": 0.0},
|
||||
"dlt": {"total": 0, "correct_reds": 0, "correct_blues": 0, "best": 0, "profitable_wins": 0, "total_earnings": 0.0}
|
||||
}
|
||||
|
||||
# ================================================================
|
||||
# 计算下期信息
|
||||
# ================================================================
|
||||
prev_issue = latest["issue"]
|
||||
prev_date = latest.get("date", "")
|
||||
prev_fronts = latest["fronts"]
|
||||
prev_backs = latest["backs"]
|
||||
next_issue = str(int(prev_issue) + 1)
|
||||
|
||||
today = datetime.datetime.now()
|
||||
draw_days = [1, 3, 6] # 周一、三、六
|
||||
draw_time = "20:25"
|
||||
draw_hour, draw_min = map(int, draw_time.split(":"))
|
||||
now_hour, now_min = today.hour, today.minute
|
||||
today_draw = today.isoweekday() in draw_days
|
||||
time_past = (now_hour > draw_hour) or (now_hour == draw_hour and now_min >= draw_min)
|
||||
if today_draw and not time_past:
|
||||
delta = 0
|
||||
else:
|
||||
delta = 1
|
||||
while (today + datetime.timedelta(days=delta)).isoweekday() not in draw_days:
|
||||
delta += 1
|
||||
next_draw = today + datetime.timedelta(days=delta)
|
||||
next_draw_str = next_draw.strftime("%Y-%m-%d")
|
||||
wd_map = {1:"周一",2:"周二",3:"周三",4:"周四",5:"周五",6:"周六",7:"周日"}
|
||||
next_wd = wd_map.get(next_draw.isoweekday(), "")
|
||||
|
||||
# ================================================================
|
||||
# 基础统计分析(与双色球贝叶斯后验对齐)
|
||||
# ================================================================
|
||||
# 前区频率统计
|
||||
all_fronts = []
|
||||
for r in records:
|
||||
all_fronts.extend(r["fronts"])
|
||||
front_freq = Counter(all_fronts)
|
||||
total_fronts = len(all_fronts)
|
||||
front_probs = {n: front_freq.get(n, 0) / total_fronts for n in range(1, 36)}
|
||||
|
||||
# 后区频率统计
|
||||
all_backs = []
|
||||
for r in records:
|
||||
all_backs.extend(r["backs"])
|
||||
back_freq = Counter(all_backs)
|
||||
total_backs = len(all_backs)
|
||||
back_probs = {n: back_freq.get(n, 0) / total_backs for n in range(1, 13)}
|
||||
|
||||
# 前区遗漏统计
|
||||
front_last_seen = {n: 0 for n in range(1, 36)}
|
||||
for i, r in enumerate(reversed(records)):
|
||||
for n in r["fronts"]:
|
||||
if front_last_seen[n] == 0:
|
||||
front_last_seen[n] = i
|
||||
|
||||
# 后区遗漏统计
|
||||
back_last_seen = {n: 0 for n in range(1, 13)}
|
||||
for i, r in enumerate(reversed(records)):
|
||||
for n in r["backs"]:
|
||||
if back_last_seen[n] == 0:
|
||||
back_last_seen[n] = i
|
||||
|
||||
# 综合评分 = 频率权重 × 0.4 + 遗漏回补 × 0.3 + 近期热度 × 0.3
|
||||
front_scores = {}
|
||||
for n in range(1, 36):
|
||||
freq_score = front_probs.get(n, 0)
|
||||
miss_score = min(1.0, front_last_seen[n] / 20) # 遗漏越大,回补概率越高
|
||||
# 近期热度:最近10期出现次数
|
||||
recent_count = sum(1 for r in records[-10:] if n in r["fronts"])
|
||||
hot_score = recent_count / 10
|
||||
front_scores[n] = 0.4 * freq_score + 0.3 * miss_score + 0.3 * hot_score
|
||||
|
||||
back_scores = {}
|
||||
for n in range(1, 13):
|
||||
freq_score = back_probs.get(n, 0)
|
||||
miss_score = min(1.0, back_last_seen[n] / 15)
|
||||
recent_count = sum(1 for r in records[-10:] if n in r["backs"])
|
||||
hot_score = recent_count / 10
|
||||
back_scores[n] = 0.4 * freq_score + 0.3 * miss_score + 0.3 * hot_score
|
||||
|
||||
# 归一化
|
||||
total_front_score = sum(front_scores.values())
|
||||
front_probs_norm = {n: s / total_front_score for n, s in front_scores.items()}
|
||||
total_back_score = sum(back_scores.values())
|
||||
back_probs_norm = {n: s / total_back_score for n, s in back_scores.items()}
|
||||
|
||||
# ================================================================
|
||||
# 加载集成学习模块(抑制中间输出)
|
||||
# ================================================================
|
||||
sys.path.insert(0, BASE_DIR)
|
||||
HAS_ENSEMBLE = False
|
||||
ensemble_result = None
|
||||
try:
|
||||
# 临时重定向stdout以抑制集成学习的中间输出
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = io.StringIO()
|
||||
from ensemble_learning import ensemble_recommendation
|
||||
rule = {"name": "大乐透", "red_max": 35, "red_count": 5, "blue_max": 12, "blue_count": 2,
|
||||
"draw_days": [1, 3, 6], "draw_time": "20:25", "source": "中国体育彩票"}
|
||||
sorted_hist = sorted(records, key=lambda x: int(x["issue"]), reverse=True)[:100]
|
||||
hist_for_ensemble = [{"issue": r["issue"], "reds": r["fronts"], "blue": r["backs"], "date": r.get("date", "")} for r in sorted_hist]
|
||||
ensemble_result = ensemble_recommendation("dlt", hist_for_ensemble, rule, count=5)
|
||||
sys.stdout = old_stdout
|
||||
if ensemble_result and ensemble_result.get("recommendations"):
|
||||
HAS_ENSEMBLE = True
|
||||
except Exception as e:
|
||||
sys.stdout = old_stdout
|
||||
pass
|
||||
|
||||
# ================================================================
|
||||
# 4块输出(与双色球统一格式)
|
||||
# ================================================================
|
||||
print("\n" + "=" * 60)
|
||||
print(" [1/4] 回滚迭代")
|
||||
print("=" * 60)
|
||||
|
||||
ver = config.get("iteration_count", 0)
|
||||
stats = feedback.get("dlt", {})
|
||||
total_recs = stats.get("total", 0)
|
||||
profitable = stats.get("profitable_wins", 0)
|
||||
win_rate = "%.1f%%" % (profitable / total_recs * 100) if total_recs > 0 else "0.0%"
|
||||
earnings = stats.get("total_earnings", 0.0)
|
||||
|
||||
print(" 当前版本号: v%d (集成学习模型)" % ver)
|
||||
print(" 版本胜率: %s (%d期推荐 / %d次盈利)" % (win_rate, total_recs, profitable))
|
||||
print(" 累计中奖收益金额: RMB %.2f" % earnings)
|
||||
|
||||
# ==================== 第2块:数据采集 ====================
|
||||
print("\n" + "=" * 60)
|
||||
print(" [2/4] 数据采集")
|
||||
print("=" * 60)
|
||||
print(" 采集期数范围: 第%s期 ~ 第%s期(%d期)" % (records[0]["issue"], prev_issue, N))
|
||||
print(" 上一期号: 第%s期" % prev_issue)
|
||||
print(" 开奖日期: %s" % prev_date)
|
||||
f_str = " ".join("%02d" % n for n in prev_fronts)
|
||||
b_str = " ".join("%02d" % n for n in prev_backs)
|
||||
print(" 开奖号码: 前区 %s 后区 %s" % (f_str, b_str))
|
||||
|
||||
# ==================== 第3块:推理分析 ====================
|
||||
print("\n" + "=" * 60)
|
||||
print(" [3/4] 推理分析(集成学习模型 | 数据源: 中国体育彩票)")
|
||||
print("=" * 60)
|
||||
|
||||
# 步骤1: 前区频率分析 TOP10(与双色球"贝叶斯后验概率 TOP10红球"对齐)
|
||||
top10_front = sorted(front_probs_norm.items(), key=lambda x: -x[1])[:10]
|
||||
print(" >> 步骤1 - 频率分析(前区TOP10)")
|
||||
print(" %s" % " ".join("%02d(%.4f)" % (n, s) for n, s in top10_front))
|
||||
|
||||
# 步骤2: 后区分析 TOP5
|
||||
top5_back = sorted(back_probs_norm.items(), key=lambda x: -x[1])[:5]
|
||||
print(" >> 步骤2 - 后区分析(TOP5)")
|
||||
print(" %s" % " ".join("%02d(%.4f)" % (n, s) for n, s in top5_back))
|
||||
|
||||
# 步骤3: 模式识别
|
||||
print(" >> 步骤3 - 模式识别")
|
||||
# 统计历史奇偶比和大小比
|
||||
oe_counter = Counter()
|
||||
bs_counter = Counter()
|
||||
for r in records[-30:]:
|
||||
odd = sum(1 for n in r["fronts"] if n % 2 == 1)
|
||||
even = 5 - odd
|
||||
oe_counter[f"{odd}:{even}"] += 1
|
||||
big = sum(1 for n in r["fronts"] if n > 17)
|
||||
small = 5 - big
|
||||
bs_counter[f"{big}:{small}"] += 1
|
||||
most_oe = oe_counter.most_common(1)[0][0] if oe_counter else "3:2"
|
||||
most_bs = bs_counter.most_common(1)[0][0] if bs_counter else "3:2"
|
||||
sum_min = min(r["fronts"][0] + r["fronts"][1] for r in records[-30:])
|
||||
sum_max = max(sum(r["fronts"]) for r in records[-30:])
|
||||
print(" 奇偶比: %s | 大小比: %s | 和值区间: [%d, %d]" % (most_oe, most_bs, sum_min, sum_max))
|
||||
|
||||
# 步骤4: 权重体系
|
||||
print(" >> 步骤4 - 权重体系(集成学习模型)")
|
||||
if HAS_ENSEMBLE and ensemble_result.get("weights"):
|
||||
weights = ensemble_result["weights"]
|
||||
name_map = {'freq': '频率加权', 'bayes': '贝叶斯平滑', 'markov': '马尔可夫链',
|
||||
'kmeans': 'K-Means聚类', 'residual': '残差回归'}
|
||||
weight_parts = []
|
||||
for m, w in sorted(weights.items(), key=lambda x: -x[1]):
|
||||
weight_parts.append("%s%.2f%%" % (name_map.get(m, m), w * 100))
|
||||
print(" %s" % " + ".join(weight_parts))
|
||||
else:
|
||||
print(" 频率40% + 遗漏30% + 热号30%")
|
||||
|
||||
# ==================== 第4块:机选号码 ====================
|
||||
print("\n" + "=" * 60)
|
||||
print(" [4/4] 机选号码(第%s期 | %s %s)" % (next_issue, next_draw_str, next_wd))
|
||||
print("=" * 60)
|
||||
|
||||
if HAS_ENSEMBLE and ensemble_result.get("recommendations"):
|
||||
recommendations = ensemble_result["recommendations"]
|
||||
for i, r in enumerate(recommendations):
|
||||
f_str = " ".join("%02d" % n for n in r["reds"])
|
||||
b_str = " ".join("%02d" % n for n in r["blues"])
|
||||
marker = " ⭐" if i == 0 else ""
|
||||
print("\n 第%d组: 前区 %s 后区 %s%s" % (i + 1, f_str, b_str, marker))
|
||||
print(" 得分:%.4f | 奇偶:%s | 大小:%s | 和:%d" % (
|
||||
r["score"], r["odd_even"], r["big_small"], r["sum"]))
|
||||
|
||||
# 保存推荐
|
||||
analysis_file = os.path.join(DATA_DIR, "analysis_dlt.json")
|
||||
analysis = json.load(open(analysis_file, "r")) if os.path.exists(analysis_file) else {}
|
||||
analysis["last_recommendation"] = {
|
||||
"fronts": recommendations[0]["reds"],
|
||||
"backs": recommendations[0]["blues"],
|
||||
"generated_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"model_version": "ensemble_v7"
|
||||
}
|
||||
json.dump(analysis, open(analysis_file, "w"), ensure_ascii=False, indent=2)
|
||||
else:
|
||||
print(" [⚠️ 无法生成推荐,请检查数据]")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(" ⚠️ 免责声明: 本推荐仅基于历史统计建模的相对概率评分,")
|
||||
print(" 不预测下期开奖,不保证中奖。彩票是独立随机事件。")
|
||||
print("=" * 60)
|
||||
print("\n 开奖后把实际号码发我,帮你回滚迭代校正精度 🍀")
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"id": "lottery_picker",
|
||||
"name": "彩票智能分析",
|
||||
"description": "双色球(SSQ)和大乐透(DLT)的智能号码分析与机选推荐工具,基于多维度数学模型 + AI概率分析,提供数据采集、推理分析、机选推荐和回滚迭代完整流程。",
|
||||
"version": "1.0.0",
|
||||
"author": "QwenPaw彩票分析系统",
|
||||
"keywords": [
|
||||
"双色球",
|
||||
"大乐透",
|
||||
"彩票",
|
||||
"机选",
|
||||
"AI分析"
|
||||
],
|
||||
"enabled": true,
|
||||
"main": "lottery_tool.py",
|
||||
"triggers": [
|
||||
{
|
||||
"type": "keyword",
|
||||
"keywords": [
|
||||
"双色球",
|
||||
"大乐透",
|
||||
"彩票",
|
||||
"今日彩票",
|
||||
"机选",
|
||||
"买彩票"
|
||||
]
|
||||
}
|
||||
],
|
||||
"requirements": [
|
||||
"requests",
|
||||
"beautifulsoup4",
|
||||
"markdown"
|
||||
],
|
||||
"entrypoint": "lottery_tool.py",
|
||||
"documentation": "SKILL.md",
|
||||
"created_at": "2026-05-25T21:44:04.764539",
|
||||
"updated_at": "2026-05-25T21:44:04.764539"
|
||||
}
|
||||
Reference in new issue
Block a user