758 lines
29 KiB
Python
758 lines
29 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
增强版彩票智能分析系统 v2.0
|
||
===============================
|
||
核心升级:
|
||
1. 采集近200期数据(原100期)
|
||
2. AI逐期回滚推演:从第200期开始->模拟分析机选->比对实开->回滚优化
|
||
3. 方法论自优化:每轮回滚后总结方法论并迭代升级
|
||
4. 目标:胜出率(概率占比) >= 51%
|
||
|
||
用法:
|
||
python enhanced_lottery.py --action full_report --lottery ssq --count 5
|
||
python enhanced_lottery.py --action rollback --lottery ssq
|
||
python enhanced_lottery.py --action self_evolve --lottery ssq --iterations 10
|
||
"""
|
||
|
||
import json, os, sys, math, random, copy, datetime
|
||
from collections import Counter
|
||
|
||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
DATA_DIR = os.path.join(BASE_DIR, "data")
|
||
os.makedirs(DATA_DIR, exist_ok=True)
|
||
|
||
LOTTERY_RULES = {
|
||
"ssq": {
|
||
"name": "双色球",
|
||
"red_count": 6, "red_max": 33, "blue_count": 1, "blue_max": 16,
|
||
"draw_days": [2, 4, 7], "draw_time": "21:15",
|
||
},
|
||
"dlt": {
|
||
"name": "大乐透",
|
||
"red_count": 5, "red_max": 35, "blue_count": 2, "blue_max": 12,
|
||
"draw_days": [1, 3, 6], "draw_time": "20:25",
|
||
}
|
||
}
|
||
|
||
DEFAULT_WEIGHTS = {
|
||
"frequency": 0.25,
|
||
"hot_number": 0.20,
|
||
"missing": 0.15,
|
||
"tail": 0.10,
|
||
"zone": 0.08,
|
||
"odd_even": 0.06,
|
||
"big_small": 0.05,
|
||
"sum_value": 0.04,
|
||
"probability": 0.04,
|
||
"consecutive": 0.03,
|
||
}
|
||
|
||
|
||
# ============================================================
|
||
# 增强版分析引擎(200期 + 10维度深度分析)
|
||
# ============================================================
|
||
|
||
def _normalize_rec(rec):
|
||
"""标准化记录,兼容新旧格式(blue vs blues)"""
|
||
if "blues" not in rec or not rec["blues"]:
|
||
if "blue" in rec:
|
||
rec["blues"] = rec["blue"] if isinstance(rec["blue"], list) else [rec["blue"]]
|
||
return rec
|
||
|
||
|
||
def _get_blues(rec):
|
||
"""获取blue值,兼容新旧格式"""
|
||
if "blues" in rec and rec["blues"]:
|
||
raw = rec["blues"]
|
||
elif "blue" in rec:
|
||
raw = rec["blue"]
|
||
else:
|
||
raw = []
|
||
return raw if isinstance(raw, list) else [raw]
|
||
|
||
|
||
class EnhancedAnalyzer:
|
||
"""使用近200期数据,10维度权重可调,输出结构化分析结果"""
|
||
|
||
def __init__(self, data, lt, weights=None):
|
||
self.lt = lt
|
||
self.rule = LOTTERY_RULES[lt]
|
||
# 标准化并取近200期
|
||
normed = [_normalize_rec(d) for d in data]
|
||
self.data = sorted(normed, key=lambda x: int(x["issue"]), reverse=True)[:200]
|
||
self.weights = weights if weights else copy.deepcopy(DEFAULT_WEIGHTS)
|
||
self.rmax = self.rule["red_max"]
|
||
self.bmax = self.rule["blue_max"]
|
||
self.rcnt = self.rule["red_count"]
|
||
self.bcnt = self.rule["blue_count"]
|
||
self.mid = self.rmax / 2
|
||
|
||
def analyze(self):
|
||
"""完整分析,返回结构化分析结果"""
|
||
if not self.data:
|
||
return None
|
||
|
||
# 1. 频率分析
|
||
all_r, all_b = [], []
|
||
for rec in self.data:
|
||
all_r.extend(int(x) for x in rec["reds"])
|
||
bl = _get_blues(rec)
|
||
all_b.extend(int(x) for x in bl)
|
||
|
||
rf = Counter(all_r)
|
||
bf = Counter(all_b)
|
||
max_rf = max(rf.values()) or 1
|
||
max_bf = max(bf.values()) or 1
|
||
rf_score = {i: rf.get(i, 0) / max_rf for i in range(1, self.rmax + 1)}
|
||
bf_score = {i: bf.get(i, 0) / max_bf for i in range(1, self.bmax + 1)}
|
||
|
||
# 2. 热号分析(最近20期)
|
||
hot = self.data[:20]
|
||
hot_r = [int(x) for r in hot for x in r["reds"]]
|
||
hot_rf = Counter(hot_r)
|
||
max_hr = max(hot_rf.values()) or 1
|
||
hot_score = {i: hot_rf.get(i, 0) / max_hr for i in range(1, self.rmax + 1)}
|
||
|
||
# 3. 遗漏值分析
|
||
r_miss = {}
|
||
for n in range(1, self.rmax + 1):
|
||
cnt = 0
|
||
for r in self.data:
|
||
if n in [int(x) for x in r["reds"]]:
|
||
break
|
||
cnt += 1
|
||
r_miss[n] = cnt
|
||
max_rm = max(r_miss.values()) or 1
|
||
rm_score = {n: math.sin(m / max_rm * math.pi) * 0.8 + 0.2 for n, m in r_miss.items()}
|
||
|
||
b_miss = {}
|
||
for n in range(1, self.bmax + 1):
|
||
cnt = 0
|
||
for r in self.data:
|
||
ns = [int(x) for x in _get_blues(r)]
|
||
if n in ns:
|
||
break
|
||
cnt += 1
|
||
b_miss[n] = cnt
|
||
max_bm = max(b_miss.values()) or 1
|
||
bm_score = {n: math.sin(m / max_bm * math.pi) * 0.8 + 0.2 for n, m in b_miss.items()}
|
||
|
||
# 4. 奇偶模式
|
||
oe_counter = Counter()
|
||
for rec in self.data:
|
||
ns = [int(x) for x in rec["reds"]]
|
||
odd_count = sum(1 for n in ns if n % 2 == 1)
|
||
oe_counter[f"{odd_count}:{self.rcnt - odd_count}"] += 1
|
||
most_oe = oe_counter.most_common(1)[0][0] if oe_counter else "3:3"
|
||
|
||
# 5. 大小模式
|
||
bs_counter = Counter()
|
||
for rec in self.data:
|
||
ns = [int(x) for x in rec["reds"]]
|
||
big_count = sum(1 for n in ns if n > self.mid)
|
||
bs_counter[f"{big_count}:{self.rcnt - big_count}"] += 1
|
||
most_bs = bs_counter.most_common(1)[0][0] if bs_counter else "3:3"
|
||
|
||
# 6. 和值分析
|
||
sums = [sum(int(x) for x in r["reds"]) for r in self.data]
|
||
avg_sum = sum(sums) / len(sums) if sums else 0
|
||
std_sum = math.sqrt(sum((s - avg_sum) ** 2 for s in sums) / len(sums)) if sums else 0
|
||
|
||
# 7. 尾数分布
|
||
tail_counter = Counter()
|
||
for rec in self.data:
|
||
for ns in rec["reds"]:
|
||
tail_counter[int(ns) % 10] += 1
|
||
max_tf = max(tail_counter.values()) or 1
|
||
tail_score = {t: tail_counter.get(t, 0) / max_tf for t in range(10)}
|
||
|
||
# 8. 区间分布
|
||
zones = [(1, 11), (12, 22), (23, self.rmax)] if self.lt == "ssq" else [(1, 12), (13, 24), (25, self.rmax)]
|
||
zone_balance = []
|
||
for zs, ze in zones:
|
||
count = sum(1 for r in self.data for n in [int(x) for x in r["reds"]] if zs <= n <= ze)
|
||
expected = len(self.data) * self.rcnt / len(zones)
|
||
zone_balance.append(count / expected if expected else 1.0)
|
||
|
||
# 9. 连号分析
|
||
consec_counter = Counter()
|
||
for rec in self.data:
|
||
ns = sorted(int(x) for x in rec["reds"])
|
||
consec = sum(1 for i in range(len(ns) - 1) if ns[i + 1] - ns[i] == 1)
|
||
consec_counter[consec] += 1
|
||
most_consec = consec_counter.most_common(1)[0][0] if consec_counter else 0
|
||
|
||
# 10. 综合评分
|
||
r_comp = {}
|
||
for n in range(1, self.rmax + 1):
|
||
s = rf_score.get(n, 0) * self.weights["frequency"]
|
||
s += hot_score.get(n, 0) * self.weights["hot_number"]
|
||
s += rm_score.get(n, 0.5) * self.weights["missing"]
|
||
s += tail_score.get(n % 10, 0.5) * self.weights["tail"]
|
||
for i, (zs, ze) in enumerate(zones):
|
||
if zs <= n <= ze and zone_balance[i] < 0.85:
|
||
s += 0.10
|
||
break
|
||
r_comp[n] = round(s, 4)
|
||
|
||
# 蓝球复合评分
|
||
recent_blue = []
|
||
for r in self.data[:20]:
|
||
recent_blue.extend(int(x) for x in (r["blues"] if isinstance(r["blues"], list) else [r["blues"]]))
|
||
rbf = Counter(recent_blue)
|
||
max_rbf = max(rbf.values()) or 1
|
||
b_comp = {}
|
||
for n in range(1, self.bmax + 1):
|
||
s = bf_score.get(n, 0) * 0.35 + bm_score.get(n, 0.5) * 0.40 + (rbf.get(n, 0) / max_rbf) * 0.25
|
||
b_comp[n] = round(s, 4)
|
||
|
||
result = {
|
||
"data_range": {
|
||
"total": len(self.data),
|
||
"start": self.data[-1]["issue"],
|
||
"end": self.data[0]["issue"]
|
||
},
|
||
"red_composite": r_comp,
|
||
"blue_composite": b_comp,
|
||
"patterns": {
|
||
"most_oe": most_oe,
|
||
"most_bs": most_bs,
|
||
"avg_sum": round(avg_sum, 1),
|
||
"std_sum": round(std_sum, 1),
|
||
"sum_range": [round(avg_sum - std_sum, 1), round(avg_sum + std_sum, 1)],
|
||
"zone_balance": {f"Z{i+1}": round(zb, 3) for i, zb in enumerate(zone_balance)},
|
||
"most_consec": most_consec,
|
||
},
|
||
"weights_used": copy.deepcopy(self.weights),
|
||
}
|
||
return result
|
||
|
||
def recommend(self, analysis, count=5):
|
||
"""基于分析结果生成推荐号码"""
|
||
if not analysis:
|
||
return []
|
||
|
||
r_comp = analysis["red_composite"]
|
||
b_comp = analysis["blue_composite"]
|
||
pat = analysis["patterns"]
|
||
|
||
sorted_r = sorted(r_comp.keys(), key=lambda n: r_comp[n], reverse=True)
|
||
sorted_b = sorted(b_comp.keys(), key=lambda n: b_comp[n], reverse=True)
|
||
|
||
target_oe = [int(x) for x in pat["most_oe"].split(":")]
|
||
target_bs = [int(x) for x in pat["most_bs"].split(":")]
|
||
sum_min, sum_max = pat["sum_range"]
|
||
|
||
seed = int(datetime.datetime.now().timestamp() * 1000) % 1000000
|
||
rng = random.Random(seed)
|
||
|
||
results = []
|
||
for _ in range(count * 3):
|
||
top_r = sorted_r[:20]
|
||
weights_r = [max(r_comp[n], 0) for n in top_r]
|
||
total_w = sum(weights_r)
|
||
if total_w <= 0:
|
||
weights_r = [1] * len(top_r)
|
||
total_w = len(top_r)
|
||
probs_r = [w / total_w for w in weights_r]
|
||
|
||
reds = set()
|
||
attempts = 0
|
||
while len(reds) < self.rcnt and attempts < 100:
|
||
n = rng.choices(top_r, weights=probs_r, k=1)[0]
|
||
reds.add(n)
|
||
attempts += 1
|
||
if len(reds) < self.rcnt:
|
||
remaining = [n for n in range(1, self.rmax + 1) if n not in reds]
|
||
rng.shuffle(remaining)
|
||
reds.update(remaining[:self.rcnt - len(reds)])
|
||
|
||
reds = sorted(reds)
|
||
|
||
top_b = sorted_b[:8]
|
||
weights_b = [max(b_comp[n], 0) for n in top_b]
|
||
total_bw = sum(weights_b) or 1
|
||
probs_b = [w / total_bw for w in weights_b]
|
||
if self.bcnt > 1:
|
||
blues = list(rng.choices(top_b, weights=probs_b, k=self.bcnt))
|
||
# 确保后区号码不重复
|
||
while len(set(blues)) < len(blues):
|
||
for j in range(len(blues)):
|
||
if blues.count(blues[j]) > 1:
|
||
remaining = [n for n in range(1, self.bmax + 1) if n not in blues[:j] + blues[j+1:]]
|
||
blues[j] = rng.choice(remaining)
|
||
else:
|
||
blues = list(rng.choices(top_b, weights=probs_b, k=self.bcnt))
|
||
|
||
score = sum(r_comp.get(n, 0) for n in reds) / self.rcnt
|
||
score += sum(b_comp.get(n, 0) for n in blues) / self.bcnt
|
||
score /= 2
|
||
|
||
oe = sum(1 for n in reds if n % 2 == 1)
|
||
bs = sum(1 for n in reds if n > self.mid)
|
||
total_sum = sum(reds)
|
||
|
||
bonus = 0
|
||
if oe == target_oe[0]:
|
||
bonus += 0.05
|
||
if bs == target_bs[0]:
|
||
bonus += 0.05
|
||
if sum_min <= total_sum <= sum_max:
|
||
bonus += 0.05
|
||
|
||
results.append({
|
||
"reds": reds, "blues": blues,
|
||
"score": round(score + bonus, 4),
|
||
"oe": f"{oe}:{self.rcnt - oe}",
|
||
"bs": f"{bs}:{self.rcnt - bs}",
|
||
"sum": total_sum,
|
||
})
|
||
|
||
seen = set()
|
||
unique = []
|
||
for r in sorted(results, key=lambda x: x["score"], reverse=True):
|
||
key = tuple(r["reds"]) + tuple(r["blues"])
|
||
if key not in seen:
|
||
seen.add(key)
|
||
unique.append(r)
|
||
|
||
return unique[:count]
|
||
|
||
|
||
# ============================================================
|
||
# AI 逐期回滚推演引擎
|
||
# ============================================================
|
||
|
||
class AIRollbackEngine:
|
||
"""
|
||
AI逐期回滚推演:
|
||
从第200期开始 -> 使用当时可用的历史数据进行分析 -> 生成推荐
|
||
-> 与实开号码比对 -> 记录命中率 -> 自动调整权重
|
||
-> 进入下一期... 直至最新一期
|
||
"""
|
||
|
||
def __init__(self, lt, data, initial_weights=None):
|
||
self.lt = lt
|
||
self.rule = LOTTERY_RULES[lt]
|
||
# 标准化所有记录(兼容 blue/blues 混用)
|
||
normed = [_normalize_rec(d) for d in data]
|
||
self.all_data = sorted(normed, key=lambda x: int(x["issue"])) # 从旧到新
|
||
self.weights = initial_weights if initial_weights else copy.deepcopy(DEFAULT_WEIGHTS)
|
||
self.rollback_history = []
|
||
self.methodology_log = []
|
||
|
||
def run(self):
|
||
"""执行完整回滚推演:从第200期开始逐期推演至最新一期"""
|
||
total_periods = len(self.all_data)
|
||
if total_periods < 200:
|
||
return {"error": f"数据不足200期,当前仅{total_periods}期", "available": total_periods}
|
||
|
||
start_idx = 199 # 第200期(0-based索引)
|
||
total_simulations = 0
|
||
total_red_hits = 0
|
||
total_blue_hits = 0
|
||
possible_red = 0
|
||
possible_blue = 0
|
||
methodology_snapshots = []
|
||
|
||
print(f"{'='*60}")
|
||
print(f" AI逐期回滚推演引擎 - {self.rule['name']}")
|
||
print(f" 数据总量: {total_periods}期 | 推演范围: 第200期 ~ 第{total_periods}期")
|
||
print(f" 推演总轮次: {total_periods - start_idx} 期")
|
||
print(f"{'='*60}")
|
||
|
||
for i in range(start_idx, total_periods):
|
||
current_issue = self.all_data[i]["issue"]
|
||
historical = self.all_data[:i]
|
||
|
||
actual = self.all_data[i]
|
||
actual_reds = [int(x) for x in actual["reds"]]
|
||
actual_blues = [int(x) for x in _get_blues(actual)]
|
||
|
||
analyzer = EnhancedAnalyzer(historical, self.lt, self.weights)
|
||
analysis = analyzer.analyze()
|
||
if not analysis:
|
||
continue
|
||
|
||
recommendations = analyzer.recommend(analysis, count=5)
|
||
if not recommendations:
|
||
continue
|
||
|
||
best_pick = recommendations[0]
|
||
pred_reds = best_pick["reds"]
|
||
pred_blues = best_pick["blues"]
|
||
|
||
red_hit = len(set(pred_reds) & set(actual_reds))
|
||
blue_hit = len(set(pred_blues) & set(actual_blues))
|
||
|
||
total_red_hits += red_hit
|
||
total_blue_hits += blue_hit
|
||
possible_red += self.rule["red_count"]
|
||
possible_blue += self.rule["blue_count"]
|
||
total_simulations += 1
|
||
|
||
self.rollback_history.append({
|
||
"issue": current_issue,
|
||
"iteration": total_simulations,
|
||
"pred_reds": pred_reds,
|
||
"pred_blues": pred_blues,
|
||
"actual_reds": actual_reds,
|
||
"actual_blues": actual_blues,
|
||
"red_hit": red_hit,
|
||
"blue_hit": blue_hit,
|
||
"score": best_pick["score"],
|
||
"weights_at_time": copy.deepcopy(self.weights),
|
||
})
|
||
|
||
self._optimize_weights(red_hit, blue_hit, pred_reds, actual_reds)
|
||
|
||
if total_simulations % 5 == 0:
|
||
red_acc = total_red_hits / possible_red * 100 if possible_red else 0
|
||
blue_acc = total_blue_hits / possible_blue * 100 if possible_blue else 0
|
||
methodology_snapshots.append({
|
||
"iteration": total_simulations,
|
||
"issue": current_issue,
|
||
"weights": copy.deepcopy(self.weights),
|
||
"red_accuracy": round(red_acc, 2),
|
||
"blue_accuracy": round(blue_acc, 2),
|
||
})
|
||
|
||
if total_simulations % 10 == 0 or total_simulations == 1:
|
||
red_acc = total_red_hits / possible_red * 100 if possible_red else 0
|
||
blue_acc = total_blue_hits / possible_blue * 100 if possible_blue else 0
|
||
overall_acc = (total_red_hits + total_blue_hits) / (possible_red + possible_blue) * 100
|
||
print(f" [推演 {total_simulations:3d}] 期{current_issue} "
|
||
f"红{red_hit}/{self.rule['red_count']} 蓝{blue_hit}/{self.rule['blue_count']} "
|
||
f"| 累计 红{red_acc:.1f}% 蓝{blue_acc:.1f}% 综合{overall_acc:.1f}%")
|
||
|
||
# 最终统计
|
||
avg_red = total_red_hits / possible_red * 100 if possible_red else 0
|
||
avg_blue = total_blue_hits / possible_blue * 100 if possible_blue else 0
|
||
overall_hits = total_red_hits + total_blue_hits
|
||
overall_possible = possible_red + possible_blue
|
||
overall_acc = overall_hits / overall_possible * 100 if overall_possible else 0
|
||
win_probability = overall_acc
|
||
|
||
hit_dist = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0}
|
||
for entry in self.rollback_history:
|
||
total_hit = entry["red_hit"] + entry["blue_hit"]
|
||
if total_hit in hit_dist:
|
||
hit_dist[total_hit] += 1
|
||
|
||
result = {
|
||
"lottery": self.lt,
|
||
"total_simulations": total_simulations,
|
||
"start_issue": self.all_data[start_idx]["issue"],
|
||
"end_issue": self.all_data[-1]["issue"],
|
||
"final_weights": self.weights,
|
||
"accuracy": {
|
||
"red_accuracy": round(avg_red, 2),
|
||
"blue_accuracy": round(avg_blue, 2),
|
||
"overall_accuracy": round(overall_acc, 2),
|
||
"win_probability": round(win_probability, 2),
|
||
},
|
||
"hits": {
|
||
"total_red_hits": total_red_hits,
|
||
"total_blue_hits": total_blue_hits,
|
||
"possible_red": possible_red,
|
||
"possible_blue": possible_blue,
|
||
},
|
||
"hit_distribution": hit_dist,
|
||
"methodology_snapshots": methodology_snapshots,
|
||
"weight_evolution": self._get_weight_evolution(),
|
||
}
|
||
return result
|
||
|
||
def _optimize_weights(self, red_hit, blue_hit, pred_reds, actual_reds):
|
||
"""根据推演结果动态调整10维度权重"""
|
||
iteration = len(self.rollback_history) + 1
|
||
lr = max(0.003, 0.01 - iteration * 0.0001)
|
||
|
||
hit_ratio = (red_hit + blue_hit) / (self.rule["red_count"] + self.rule["blue_count"])
|
||
|
||
if hit_ratio >= 0.5:
|
||
self.weights["frequency"] = min(0.35, self.weights["frequency"] * (1 + lr))
|
||
self.weights["hot_number"] = min(0.30, self.weights["hot_number"] * (1 + lr * 0.8))
|
||
else:
|
||
self.weights["missing"] = min(0.30, self.weights["missing"] * (1 + lr))
|
||
self.weights["probability"] = min(0.15, self.weights["probability"] * (1 + lr * 1.2))
|
||
self.weights["frequency"] = max(0.15, self.weights["frequency"] * (1 - lr * 0.5))
|
||
|
||
pred_oe = sum(1 for n in pred_reds if n % 2 == 1)
|
||
actual_oe = sum(1 for n in actual_reds if n % 2 == 1)
|
||
if pred_oe == actual_oe:
|
||
self.weights["odd_even"] = min(0.12, self.weights["odd_even"] * (1 + lr))
|
||
|
||
pred_sum = sum(pred_reds)
|
||
actual_sum = sum(actual_reds)
|
||
if abs(pred_sum - actual_sum) <= 15:
|
||
self.weights["sum_value"] = min(0.10, self.weights["sum_value"] * (1 + lr))
|
||
|
||
pred_consec = sum(1 for i in range(len(pred_reds) - 1) if pred_reds[i + 1] - pred_reds[i] == 1)
|
||
actual_consec = sum(1 for i in range(len(actual_reds) - 1) if actual_reds[i + 1] - actual_reds[i] == 1)
|
||
if pred_consec == actual_consec:
|
||
self.weights["consecutive"] = min(0.08, self.weights["consecutive"] * (1 + lr))
|
||
|
||
total = sum(self.weights.values())
|
||
if total > 0:
|
||
for k in self.weights:
|
||
self.weights[k] /= total
|
||
|
||
def _get_weight_evolution(self):
|
||
if not self.rollback_history:
|
||
return []
|
||
interval = max(1, len(self.rollback_history) // 20)
|
||
evolution = []
|
||
for i, entry in enumerate(self.rollback_history):
|
||
if i % interval == 0 or i == len(self.rollback_history) - 1:
|
||
evolution.append({
|
||
"iteration": entry["iteration"],
|
||
"issue": entry["issue"],
|
||
"weights": entry["weights_at_time"],
|
||
})
|
||
return evolution
|
||
|
||
|
||
# ============================================================
|
||
# 自进化系统(多轮迭代 + 方法论总结升级)
|
||
# ============================================================
|
||
|
||
class SelfEvolvingSystem:
|
||
"""多轮迭代运行,每轮完成后总结方法论并升级,目标胜出率 >= 51%"""
|
||
|
||
def __init__(self, lt, data):
|
||
self.lt = lt
|
||
self.data = data
|
||
self.rule = LOTTERY_RULES[lt]
|
||
self.best_weights = copy.deepcopy(DEFAULT_WEIGHTS)
|
||
self.best_accuracy = 0
|
||
self.iteration_results = []
|
||
self.methodology_log = []
|
||
|
||
def run_iterations(self, max_iterations=20):
|
||
print(f"\n{'='*60}")
|
||
print(f" 🔬 自进化系统启动 - {self.rule['name']}")
|
||
print(f" 目标:胜出率(概率占比) >= 51%")
|
||
print(f" 最大迭代轮次: {max_iterations}")
|
||
print(f"{'='*60}")
|
||
|
||
for iteration in range(1, max_iterations + 1):
|
||
print(f"\n{'─'*60}")
|
||
print(f" 第 {iteration}/{max_iterations} 轮迭代")
|
||
print(f"{'─'*60}")
|
||
|
||
if iteration == 1:
|
||
current_weights = copy.deepcopy(DEFAULT_WEIGHTS)
|
||
else:
|
||
current_weights = self._mutate_weights(self.best_weights, iteration)
|
||
|
||
engine = AIRollbackEngine(self.lt, self.data, current_weights)
|
||
result = engine.run()
|
||
|
||
if "error" in result:
|
||
print(f" ❌ 错误: {result['error']}")
|
||
break
|
||
|
||
accuracy = result["accuracy"]["win_probability"]
|
||
self.iteration_results.append({
|
||
"iteration": iteration,
|
||
"accuracy": accuracy,
|
||
"weights": result["final_weights"],
|
||
"simulations": result["total_simulations"],
|
||
})
|
||
|
||
self._summarize_methodology(iteration, result)
|
||
|
||
if accuracy > self.best_accuracy:
|
||
self.best_accuracy = accuracy
|
||
self.best_weights = result["final_weights"]
|
||
print(f" 🏆 新最佳胜出率: {accuracy:.2f}%")
|
||
else:
|
||
print(f" 本轮胜出率: {accuracy:.2f}% (最佳: {self.best_accuracy:.2f}%)")
|
||
|
||
if self.best_accuracy >= 51.0:
|
||
print(f"\n{'='*60}")
|
||
print(f" 🎯 目标达成!胜出率 {self.best_accuracy:.2f}% >= 51%")
|
||
print(f"{'='*60}")
|
||
break
|
||
|
||
return self._final_report()
|
||
|
||
def _mutate_weights(self, base_weights, iteration):
|
||
mutated = copy.deepcopy(base_weights)
|
||
mutation_rate = max(0.005, 0.03 - iteration * 0.001)
|
||
for k in mutated:
|
||
delta = random.uniform(-mutation_rate, mutation_rate)
|
||
mutated[k] = max(0.01, mutated[k] + delta)
|
||
total = sum(mutated.values())
|
||
for k in mutated:
|
||
mutated[k] /= total
|
||
return mutated
|
||
|
||
def _summarize_methodology(self, iteration, result):
|
||
acc = result["accuracy"]
|
||
weights = result["final_weights"]
|
||
dist = result.get("hit_distribution", {})
|
||
|
||
sorted_w = sorted(weights.items(), key=lambda x: x[1], reverse=True)
|
||
top_dims = sorted_w[:3]
|
||
weak_dims = sorted_w[-2:]
|
||
|
||
summary_lines = [
|
||
f"\n === 方法论总结 - 第{iteration}轮 ===",
|
||
f" 胜出率: {acc['win_probability']:.2f}% (红球{acc['red_accuracy']:.1f}% 蓝球{acc['blue_accuracy']:.1f}%)",
|
||
f" 最优三维度:",
|
||
]
|
||
for dim, w in top_dims:
|
||
summary_lines.append(f" - {dim}: {w:.4f}")
|
||
summary_lines.append(f" 最弱二维度:")
|
||
for dim, w in weak_dims:
|
||
summary_lines.append(f" - {dim}: {w:.4f}")
|
||
summary_lines.append(f" 命中分布:")
|
||
for level in sorted(dist.keys()):
|
||
if dist.get(level, 0) > 0:
|
||
pct = dist[level] / result["total_simulations"] * 100
|
||
summary_lines.append(f" - {level}个命中: {dist[level]}次 ({pct:.1f}%)")
|
||
|
||
summary = "\n".join(summary_lines)
|
||
print(summary)
|
||
self.methodology_log.append(summary)
|
||
|
||
mpath = os.path.join(DATA_DIR, f"methodology_{self.lt}_iter{iteration}.json")
|
||
with open(mpath, "w", encoding="utf-8") as f:
|
||
json.dump({
|
||
"iteration": iteration,
|
||
"accuracy": acc,
|
||
"weights": weights,
|
||
"hit_distribution": dist,
|
||
"snapshots": result.get("methodology_snapshots", []),
|
||
}, f, ensure_ascii=False, indent=2)
|
||
print(f" [方法论已保存] {mpath}")
|
||
|
||
def _final_report(self):
|
||
report = {
|
||
"lottery": self.lt,
|
||
"total_iterations": len(self.iteration_results),
|
||
"best_accuracy": self.best_accuracy,
|
||
"best_weights": self.best_weights,
|
||
"iteration_summary": self.iteration_results,
|
||
}
|
||
rpath = os.path.join(DATA_DIR, f"self_evolve_{self.lt}_final.json")
|
||
with open(rpath, "w", encoding="utf-8") as f:
|
||
json.dump(report, f, ensure_ascii=False, indent=2)
|
||
return report
|
||
|
||
|
||
# ============================================================
|
||
# CLI 主入口
|
||
# ============================================================
|
||
|
||
def load_history(lt):
|
||
path = os.path.join(DATA_DIR, f"{lt}_history.json")
|
||
if os.path.exists(path):
|
||
try:
|
||
with open(path, "r", encoding="utf-8") as f:
|
||
return json.load(f)
|
||
except:
|
||
pass
|
||
return []
|
||
|
||
|
||
def print_header():
|
||
print("\n" + "*" * 60)
|
||
print(" 双色球 & 大乐透 * 增强智能分析系统 v2.0")
|
||
print(" AI回滚推演 | 方法论自优化 | 200期深度分析")
|
||
print(" " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M"))
|
||
print("*" * 60)
|
||
|
||
|
||
def show_full_report(lt, count=5):
|
||
rule = LOTTERY_RULES[lt]
|
||
data = load_history(lt)
|
||
if not data:
|
||
print(f"\n ⚠️ {rule['name']}暂无数据,请先采集")
|
||
print(f" python3 lottery_tool.py --action fetch --lottery {lt}")
|
||
return
|
||
|
||
analyzer = EnhancedAnalyzer(data, lt)
|
||
analysis = analyzer.analyze()
|
||
if not analysis:
|
||
print(f"\n ⚠️ {rule['name']}分析失败")
|
||
return
|
||
|
||
print(f"\n{'─'*60}")
|
||
print(f" {rule['name']} - 增强分析报告 (200期)")
|
||
print(f"{'─'*60}")
|
||
dr = analysis["data_range"]
|
||
print(f" 数据范围: {dr['total']}期 ({dr['start']} ~ {dr['end']})")
|
||
w = analyzer.weights
|
||
print(f" 权重体系: freq={w['frequency']:.3f} hot={w['hot_number']:.3f} miss={w['missing']:.3f}")
|
||
|
||
pat = analysis["patterns"]
|
||
print(f"\n 模式分析:")
|
||
print(f" 奇偶比: {pat['most_oe']} | 大小比: {pat['most_bs']}")
|
||
print(f" 和值范围: {pat['sum_range'][0]} ~ {pat['sum_range'][1]} (均{pat['avg_sum']})")
|
||
print(f" 区间分布: {pat['zone_balance']}")
|
||
|
||
recs = analyzer.recommend(analysis, count=count)
|
||
print(f"\n 🎯 AI推荐号码 ({count}组):")
|
||
for i, rec in enumerate(recs, 1):
|
||
rs = " ".join(f"{n:02d}" for n in rec["reds"])
|
||
bs = " ".join(f"{n:02d}" for n in rec["blues"])
|
||
print(f" [{i}] 红球: {rs} 蓝球: {bs} (评分:{rec['score']:.3f} OE:{rec['oe']} BS:{rec['bs']})")
|
||
|
||
|
||
def main():
|
||
import argparse
|
||
parser = argparse.ArgumentParser(description="增强版彩票智能分析系统 v2.0")
|
||
parser.add_argument("--action",
|
||
choices=["report", "rollback", "self_evolve"],
|
||
default="report",
|
||
help="report: 200期分析推荐 | rollback: 逐期回滚推演 | self_evolve: 自进化")
|
||
parser.add_argument("--lottery", choices=["ssq", "dlt", "all"], default="ssq")
|
||
parser.add_argument("--count", type=int, default=5, help="推荐组数")
|
||
parser.add_argument("--iterations", type=int, default=10, help="自进化迭代轮次")
|
||
args = parser.parse_args()
|
||
|
||
print_header()
|
||
lotteries = ["ssq", "dlt"] if args.lottery == "all" else [args.lottery]
|
||
|
||
if args.action == "report":
|
||
for lt in lotteries:
|
||
show_full_report(lt, args.count)
|
||
return
|
||
|
||
if args.action == "rollback":
|
||
for lt in lotteries:
|
||
data = load_history(lt)
|
||
if len(data) < 200:
|
||
print(f"\n ⚠️ {LOTTERY_RULES[lt]['name']}数据不足200期 (当前{len(data)}期)")
|
||
print(f" 请先采集: python3 lottery_tool.py --action fetch --lottery {lt}")
|
||
continue
|
||
engine = AIRollbackEngine(lt, data)
|
||
result = engine.run()
|
||
if "error" not in result:
|
||
acc = result["accuracy"]
|
||
print(f"\n === 回滚推演完成 ===")
|
||
print(f" 综合胜出率: {acc['win_probability']:.2f}%")
|
||
print(f" 红球准确率: {acc['red_accuracy']:.1f}% | 蓝球: {acc['blue_accuracy']:.1f}%")
|
||
|
||
if args.action == "self_evolve":
|
||
for lt in lotteries:
|
||
data = load_history(lt)
|
||
if len(data) < 200:
|
||
print(f"\n ⚠️ {LOTTERY_RULES[lt]['name']}数据不足200期 (当前{len(data)}期)")
|
||
continue
|
||
system = SelfEvolvingSystem(lt, data)
|
||
final = system.run_iterations(max_iterations=args.iterations)
|
||
print(f"\n === 自进化最终结果 ===")
|
||
print(f" 最佳胜出率: {final['best_accuracy']:.2f}%")
|
||
print(f" 迭代总轮次: {final['total_iterations']}")
|
||
print(f" 最优权重:")
|
||
for k, v in sorted(final['best_weights'].items(), key=lambda x: x[1], reverse=True):
|
||
print(f" {k}: {v:.4f}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |