#!/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 开奖后把实际号码发我,帮你回滚迭代校正精度 🍀")