Improve generator quality and sync data (May 2026)

Data:
- Add missing draw 2026-05-16 (7 11 20 23 27 30 SZ:9) via GitHub archive
- Run retroactive learning for skipped 2026-01-07 draw

Generator improvements (ported from Eurojackpot):
- Add hard structural filters (sum 100-200, min 1 even + 1 odd per tip)
- Add long-term frequency prior as learner dampener (alpha=0.2)
- Fix ENSEMBLE strategy generating duplicate tips (greedy → weighted random)
- Fix Division-by-Zero edge case in set_frequency_prior
- Fix freq_alpha boundary and clip final adjusted value to [0,1]
- Prevent negative weights in random.choices (floor at 0.001)
- Use vectorized stack().value_counts() for frequency calculation

.gitignore:
- Exclude data/backups/, weekly tip CSVs, performance reports, data/data/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-19 13:34:24 +02:00
co-authored by Claude Sonnet 4.6
parent 49c8bffb87
commit 31cfd914c5
9 changed files with 805 additions and 87 deletions
@@ -146,6 +146,9 @@ class UltimateAIMLHybridGenerator:
# 6. Initialize Real-Time Learning
print("📚 Activating Real-Time Learning...", end=" ", flush=True)
self.real_time_learner.initialize(self.features_df)
_counts = self.df[['Z1','Z2','Z3','Z4','Z5','Z6']].stack().value_counts()
freq = {n: int(_counts.get(n, 0)) for n in range(1, 50)}
self.real_time_learner.set_frequency_prior(freq)
print("")
self.is_trained = self.ai_ml_engine.is_trained
@@ -230,6 +233,19 @@ class UltimateAIMLHybridGenerator:
tip_counter += len(tips)
print(f"✅ ({count} tips)")
# Strukturfilter: Summe + Parität korrigieren
fixed_count = 0
for tip in all_tips:
if not self._passes_structural_constraints(tip['numbers']):
tip['numbers'] = self._apply_structural_fix(tip['numbers'], ai_predictions)
tip['ai_score'] = np.mean([ai_predictions.get(n, 0.1) for n in tip['numbers']])
tip['pattern_weight'] = self.pattern_engine.calculate_pattern_weight(tip['numbers'])
tip['confidence'] = (tip['ai_score'] + tip['pattern_weight']) / 2
tip['quality'] = self._calculate_quality_score(tip['numbers'], ai_predictions, tip['pattern_weight'])
fixed_count += 1
if fixed_count:
print(f" 🔧 {fixed_count} Tip(s) via Strukturfilter korrigiert (Summe/Parität)")
# Output all tips
print(f"\n📋 RESULTS:")
print("=" * 95)
@@ -406,29 +422,21 @@ class UltimateAIMLHybridGenerator:
selected = []
for position in range(6):
best_candidate = None
best_score = -1
for candidate in all_candidates:
if candidate not in selected:
ai_s = ai_predictions.get(candidate, 0.1)
pattern_s = self.pattern_engine.get_number_pattern_score(candidate)
diversity_s = self._calculate_diversity_score(candidate, selected) if selected else 0.5
ensemble_score = (ai_s * 0.4 + pattern_s * 0.3 + diversity_s * 0.3)
if ensemble_score > best_score:
best_score = ensemble_score
best_candidate = candidate
if best_candidate:
selected.append(best_candidate)
else:
available = [n for n in range(1, 50) if n not in selected]
if available:
selected.append(random.choice(available))
selected = sorted(selected[:6])
candidates = [c for c in all_candidates if c not in selected]
if not candidates:
candidates = [n for n in range(1, 50) if n not in selected]
scores = []
for c in candidates:
ai_s = ai_predictions.get(c, 0.1)
pattern_s = self.pattern_engine.get_number_pattern_score(c)
diversity_s = self._calculate_diversity_score(c, selected) if selected else 0.5
scores.append(max(0.001, ai_s * 0.4 + pattern_s * 0.3 + diversity_s * 0.3))
choice = random.choices(candidates, weights=scores)[0]
selected.append(choice)
selected = sorted(selected)
superzahl = self._get_smart_superzahl(tip_number)
ai_score = np.mean([ai_predictions.get(n, 0.1) for n in selected])
@@ -447,6 +455,55 @@ class UltimateAIMLHybridGenerator:
'quality': quality
}
def _passes_structural_constraints(self, numbers):
"""Prüft Summenbereich (122-176) und Parität (mind. 1G + 1U)."""
s = sum(numbers)
if s < 100 or s > 200:
return False
even = sum(1 for n in numbers if n % 2 == 0)
if even == 0 or even == 6:
return False
return True
def _apply_structural_fix(self, numbers, ai_preds):
"""Korrigiert Summe/Parität durch minimalen Tausch."""
nums = list(numbers)
even = [n for n in nums if n % 2 == 0]
odd = [n for n in nums if n % 2 != 0]
if len(even) == 0:
worst = min(odd, key=lambda n: ai_preds.get(n, 0))
cands = sorted([n for n in range(2, 50, 2) if n not in nums],
key=lambda n: -ai_preds.get(n, 0))
if cands:
nums.remove(worst)
nums.append(cands[0])
elif len(odd) == 0:
worst = min(even, key=lambda n: ai_preds.get(n, 0))
cands = sorted([n for n in range(1, 50, 2) if n not in nums],
key=lambda n: -ai_preds.get(n, 0))
if cands:
nums.remove(worst)
nums.append(cands[0])
for _ in range(10):
s = sum(nums)
if 100 <= s <= 200:
break
if s > 200:
highest = max(nums)
cands = sorted([n for n in range(1, highest) if n not in nums],
key=lambda n: -ai_preds.get(n, 0))
if cands:
nums.remove(highest)
nums.append(cands[0])
else:
lowest = min(nums)
cands = sorted([n for n in range(lowest + 1, 50) if n not in nums],
key=lambda n: -ai_preds.get(n, 0))
if cands:
nums.remove(lowest)
nums.append(cands[0])
return sorted(nums)
def _calculate_diversity_score(self, candidate, selected):
"""Berechnet Diversitäts-Score."""
if not selected:
@@ -1295,6 +1352,8 @@ class RealTimeLearner:
self.stats = defaultdict(int)
self.cache_path = cache_path
self.learning_state_file = os.path.join(cache_path, 'learning_state.json') if cache_path else None
self.freq_prior = {}
self.freq_alpha = 0.2
# Lade gespeicherten State
if self.learning_state_file and os.path.exists(self.learning_state_file):
@@ -1304,14 +1363,24 @@ class RealTimeLearner:
"""Initialisiert Learning."""
self.features_df = features_df
def adjust_predictions(self, predictions):
"""Passt Predictions an."""
adjusted = {}
def set_frequency_prior(self, freq):
"""Setzt Langzeit-Frequenz als Dämpfer (normalisiert auf [0,1])."""
max_val = max(freq.values(), default=0)
norm = max_val if max_val > 0 else 1
self.freq_prior = {n: v / norm for n, v in freq.items()}
def adjust_predictions(self, predictions):
"""Passt Predictions an, gedämpft durch Langzeit-Frequenz."""
adjusted = {}
for number, pred in predictions.items():
adjustment = self.adjustments.get(number, 0)
adjusted[number] = np.clip(pred + adjustment * self.learning_rate, 0, 1)
learner_val = np.clip(pred + adjustment * self.learning_rate, 0, 1)
if self.freq_prior:
alpha = max(0.0, min(1.0, self.freq_alpha))
freq_val = self.freq_prior.get(number, 0.5)
adjusted[number] = np.clip((1 - alpha) * learner_val + alpha * freq_val, 0, 1)
else:
adjusted[number] = learner_val
return adjusted
def learn_from_result(self, drawing):