Dans mon pays, il existe un jeu de loto hebdomadaire dans lequel les participants sélectionnent 6 numéros dans un pool de 37 et un autre numéro dans un pool de 7. Concentrons-nous sur la première partie du jeu et oublions la sélection d'un numéro supplémentaire dans un pool de 7. 7.
Lorsqu'il s'agit de loteries de la forme k/N, où k est le nombre de sélections souhaitées (dans notre cas, 6) sur un pool total de N numéros (dans notre cas, 37), une question courante est de savoir si chacun des ces numéros ont une chance égale de faire partie de la combinaison gagnante.
Examinons cette question.
J'ai rassemblé des statistiques sur leur site Web pour 1 609 dessins s'étalant de 2009 à 2023.
Par la suite, j'ai converti les données du fichier CSV en objet :
{ '09/09/2023': [13, 17, 24, 30, 35, 37], '07/09/2023': [7, 17, 19, 25, 35, 37], '05/09/2023': [2, 3, 5, 9, 36, 37], '02/09/2023': [4, 12, 22, 27, 30, 34], '29/08/2023': [6, 8, 15, 19, 26, 31], '26/08/2023': [6, 7, 14, 21, 25, 34], '22/08/2023': [2, 6, 10, 23, 24, 29], ... }
La clé de l'objet correspond à la date du tirage et la valeur associée est un tableau de nombres qui sont apparus comme la combinaison gagnante pour ce tirage spécifique.
Ensuite, j'ai créé un tableau contenant tous les nombres obtenus à partir des dessins :
numbers = np.array(list(lotto.values())).flatten() [13, 17, 24, 30, 35, 37, 7, 17, 19, 25, 35, 37, 2, 3, 5, 9, 36, ...]
Suite à cela, j'ai calculé le nombre d'occurrences (fréquence) pour chaque valeur du tableau :
count = np.bincount(numbers)[1:] [268, 256, 257, 242, 255, 273, 247, 277, 260, 267, 289, 294, 271, 239, 254, 255, 263, 243, 246, 271, 265, 254, 252, 243, 291, 271, 258, 264, 275, 258, 251, 244, 263, 256, 267, 251, 264]
Ces résultats indiquent que le numéro 1 a été tiré 268 fois, le numéro 2 a été tiré 256 fois, et ainsi de suite.
Il semble que la répartition des numéros dans les résultats de loterie soit relativement uniforme. Pour confirmer davantage cela, nous pouvons effectuer un test pour valider l’uniformité de la distribution.
Pour tester l'équiprobabilité des N nombres individuels, vous pouvez suivre cette approche :
Effectuez un test statistique, tel qu'un test du chi carré, pour déterminer si la valeur du chi carré calculée est statistiquement significative. Cela vous aidera à évaluer si la distribution des nombres est significativement différente de ce à quoi on pourrait s'attendre en cas d'équiprobabilité.
Si la valeur du chi carré calculée n’est pas statistiquement significative, cela suggère que les nombres sont raisonnablement uniformément répartis, ce qui conforte l’hypothèse d’équiprobabilité. Cependant, si la valeur X^2 est significative, cela indiquerait un écart par rapport à l'équiprobabilité.
Créons une fonction pour effectuer le test du chi carré pour l'équiprobabilité des nombres :
def chi2(data, size, expect, p_value = 0.05): pl = size * 1/expect df = expect - 1 x2_crit_1 = stats.chi2.ppf(p_value, df) x2_crit_2 = stats.chi2.ppf(1 - p_value, df) x2 = 0 for i in range(expect): x2 += ((data[i] - pl) ** 2)/pl accepted = x2_crit_1 < x2 < x2_crit_2 if x2_crit_1 < x2_crit_2 else x2_crit_2 < x2 < x2_crit_1 return x2, accepted
Cette fonction renvoie le tuple constitué de la statistique du chi carré et le résultat de l'équiprobabilité acceptée avec la probabilité 1 - 2 * p-value
, c'est-à-dire que les valeurs extrêmes de cette distribution uniforme discrète ont une faible probabilité.
N = 37 chi2(count, len(numbers), N) (25.183136523720748, True)
Certes, vous pouvez utiliser la fonctionnalité intégrée de la bibliothèque SciPy pour effectuer le test du chi carré pour l'équiprobabilité :
from scipy import stats chi2_statistic, p_value = stats.chisquare(count) (25.18313652372074, 0.9115057832606053)
Explorons les combinaisons de ces nombres, en commençant par des paires :
from itertools import combinations pairs = list(combinations(range(1, N), 2))
Suite à cette étape, nous construisons une matrice 2D qui suit les occurrences de ces paires :
pairs_count = np.zeros([N] * 2, dtype=int) for pair in pairs: for draw in lotto.values(): if pair[0] in draw and pair[1] in draw: pairs_count[pair[0]][pair[1]] += 1 pairs_count = pairs_count[1:, 1:]
Cela forme une matrice triangulaire, car elle tient compte du fait que les paires (a, b) et (b, a) sont équivalentes, et nous ne comptons que les occurrences des paires (a, b).
Ma fonction donne :
counts = pairs_count.flatten() counts = counts[counts > 0] chi2(counts, sum(counts), len(counts)) (589.2721893491138, True)
et SciPy fournit :
chi2_statistic, p_value = stats.chisquare(counts) (589.2721893491124, 0.8698507423203673)
Et si on envisageait des triplés :
comb3 = list(combinations(range(1, N), 3)) comb3_count = np.zeros([N] * 3, dtype=int) for comb in comb3: for draw in lotto.values(): contains = comb[0] in draw and comb[1] in draw and comb[2] in draw if contains: comb3_count[comb[0]][comb[1]][comb[2]] += 1 comb3_count = comb3_count[1:, 1:, 1:] counts = comb3_count.flatten() counts = counts[counts > 0] chi2(counts, sum(counts), len(counts)) (6457.575829383709, False)
Quelque chose ne va pas, probablement à cause de la grande parcimonie de la matrice. La valeur du chi carré tombe en dessous du seuil critique inférieur du chi carré :
6457.575829383709 < 6840.049842653838
Cependant, lorsque vous utilisez SciPy, le résultat est :
chi2_statistic, p_value = stats.chisquare(counts) (6457.575829383886, 0.9999997038479482)
Maintenant, identifions le numéro qui a été tiré le plus fréquemment :
count.argmax() or list(count).index(max(count)) 11
Ne tirons pas de conclusions hâtives pour l'instant. On peut examiner comment ce nombre a évolué au fil des années :
year_result = dict() for year in range(2009, 2024): new_dict = {k:v for (k,v) in lotto.items() if str(year) in k} year_result[year] = np.bincount(np.array(list(new_dict.values())).flatten())[1:].argmax() { 2009: 16, 2010: 10, 2011: 11, 2012: 24, 2013: 32, 2014: 34, 2015: 21, 2016: 25, 2017: 5, 2018: 10, 2019: 24, 2020: 11, 2021: 12, 2022: 14, 2023: 11 }
Ou bien, nous pouvons analyser les changements cumulés au fil du temps :
year_result = dict() arr = [] for year in range(2009, 2024): new_dict = {k:v for (k,v) in lotto.items() if str(year) in k} arr += list(np.array(list(new_dict.values())).flatten()) year_result['2009 - ' + str(year) if year > 2009 else str(year)] = np.bincount(arr)[1:].argmax() { '2009': 16, '2009 - 2010': 10, '2009 - 2011': 11, '2009 - 2012': 20, '2009 - 2013': 20, '2009 - 2014': 20, '2009 - 2015': 34, '2009 - 2016': 20, '2009 - 2017': 10, '2009 - 2018': 10, '2009 - 2019': 10, '2009 - 2020': 10, '2009 - 2021': 10, '2009 - 2022': 24, '2009 - 2023': 11 }
Enfin, nous pouvons également rechercher si des dessins identiques ont déjà eu lieu :
lotto_counts = {} for k, v in lotto.items(): v_str = str(v) if v_str in lotto_counts: lotto_counts[v_str] += [k] else: lotto_counts[v_str] = [k] result = {k: v for k, v in lotto_counts.items() if len(lotto_counts[k]) > 1} { '[13, 14, 26, 32, 33, 36]': ['16/10/2010', '21/09/2010'] }
Il est amusant de constater que ces événements se sont produits presque consécutivement.
Alors que nous terminons notre voyage dans le monde des données de loterie, ce fut une véritable aventure à travers les chiffres et les probabilités. Nous avons découvert quelques informations intéressantes – des paires et triplés à la détection des numéros les plus populaires.
Les loteries sont avant tout imprévisibles, mais c'est amusant de jeter un coup d'œil derrière le rideau et d'explorer les bizarreries de ces jeux. Que vous soyez joueur ou simple observateur curieux, le monde des chiffres réserve toujours une ou deux surprises.