Add popularity score for EV-optimized number selection

Adds _calculate_popularity_score() which favors number combinations
unlikely to be picked by other players (numbers > 31, no consecutive
sequences, avoiding common "lucky numbers"). This doesn't improve hit
probability (lottery draws are i.i.d. random) but increases expected
value by reducing the chance of sharing a jackpot.

- New 20% weight in quality score
- New Popularity_Score column in tip CSV export

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 13:34:43 +02:00
co-authored by Claude Sonnet 4.6
parent 23498767bf
commit e3a022b000
3 changed files with 39 additions and 5 deletions
@@ -440,6 +440,13 @@
"file": "weekly_lotto_tips_20260610_123055.csv",
"avg_confidence": 0.5643409470927386,
"avg_quality": 0.7014208121760903
},
{
"timestamp": "2026-06-10T13:32:43.029205",
"num_tips": 10,
"file": "weekly_lotto_tips_20260610_133243.csv",
"avg_confidence": 0.5643409470927386,
"avg_quality": 0.6788503626336448
}
]
}
+2 -1
View File
@@ -215,7 +215,8 @@ class WeeklyTipGenerator:
'AI_Score': f"{tip['ai_score']:.4f}",
'Pattern_Weight': f"{tip['pattern_weight']:.4f}",
'Confidence': f"{tip['confidence']:.4f}",
'Quality': f"{tip['quality']:.4f}"
'Quality': f"{tip['quality']:.4f}",
'Popularity_Score': f"{UltimateAIMLHybridGenerator._calculate_popularity_score(tip['numbers']):.4f}"
})
df_export = pd.DataFrame(rows)
@@ -563,20 +563,46 @@ class UltimateAIMLHybridGenerator:
return int(sz) if 0 <= int(sz) <= 9 else 7
@staticmethod
def _calculate_popularity_score(numbers):
"""
Schätzt den Erwartungswert-Vorteil durch Vermeidung populärer Zahlenkombinationen.
Höher = unpopulärer = höherer Gewinnanteil bei einem Treffer.
Basis: Spieler bevorzugen Geburtstagszahlen (1-31), Glückszahlen und Zahlenfolgen.
"""
n = len(numbers)
sorted_nums = sorted(numbers)
# Anteil Zahlen > 31 (Geburtstags-Range vermeiden)
above_31_ratio = sum(1 for x in numbers if x > 31) / n
# Aufeinanderfolgende Zahlen vermeiden (visuelle Muster)
consecutive_pairs = sum(1 for i in range(n - 1) if sorted_nums[i + 1] - sorted_nums[i] == 1)
consecutive_ratio = consecutive_pairs / (n - 1) if n > 1 else 0
# Häufig gespielte "Glückszahlen" vermeiden
lucky_numbers = {3, 7, 9, 11, 13, 17, 19, 21, 23}
lucky_ratio = sum(1 for x in numbers if x in lucky_numbers) / n
score = above_31_ratio * 0.5 + (1 - consecutive_ratio) * 0.3 + (1 - lucky_ratio) * 0.2
return min(max(score, 0.0), 1.0)
def _calculate_quality_score(self, numbers, ai_predictions, pattern_weight):
"""Berechnet Qualitäts-Score."""
ai_scores = [ai_predictions.get(n, 0.1) for n in numbers]
ai_quality = np.mean(ai_scores) * (1 + np.std(ai_scores))
pattern_quality = pattern_weight
distances = []
for i, n1 in enumerate(numbers):
for n2 in numbers[i+1:]:
distances.append(abs(n1 - n2))
diversity_quality = min(np.mean(distances) / 8.0, 1.0) if distances else 0.5
quality = (ai_quality * 0.4 + pattern_quality * 0.3 + diversity_quality * 0.3)
popularity_quality = self._calculate_popularity_score(numbers)
quality = (ai_quality * 0.35 + pattern_quality * 0.25 + diversity_quality * 0.2 + popularity_quality * 0.2)
return min(quality, 1.0)
def _print_tip_line(self, tip):