LOTO: analyse et statistiques

publication: 7 mars 2023 / mis à jour 8 mars 2023

Read this page in english

 

Préambule

Le jeu du LOTO est une loterie publique. Le principe de base est simple. Il consiste à tirer un certain nombre de boules, toutes numérotées de 1 à n. En FRANCE, le LOTO historique tire 6 boules, numérotées de 1 à 49. Le LOTO de l'EURO MILLION tire 5 boules numérotées de 1 à 50.

Dans la suite de cet article, nous allons nous intéresser au tirage de l'EURO MILLION.

Les programmes en FORTH sont écrits pour s'adapter aux LOTOs de chaque pays.

Les programmes ont été écrits par modules. Les codes sources complets et actualisés sont disponibles ici:
   MPETREMANN11 / uEforth / lotto

Structure des modules

Le pogramme de base s'appelle main.fs. C'est ce programme qui doit être compilé par eFORTH:

include main.fs 

Le fichier main.fs contient les paramètres essentiels et les appels aux différents modules.

Les chiffres du LOTO

L'idée de départ est de créer une table contenant tous les chiffres du tirage de LOTO. ces données ont été récupérées sur le site La Française des Jeux au format CSV. Ces données ont été nettoyées pour ne garder que les chiffres de chaque tirage, en excluant les chiffres complémentaires. Grâce à un traitement de texte (Libre Office), chaque séparateur CSV a été transformé en c,.

Il ne reste plus qu'à garnir les données avec les mots FORTH permettant de créer la table de données euroMillion:

\ LOTO EURO MILLION FRANCE 
\ from 2004-02-13 to 2023-02-24 
create euroMillion 
    5 ,         \ 5 numbers per grid in EURO MILLION (FR) 
    0 ,         \ init number of grids stored - 16 bits 
07 c, 23 c, 34 c, 42 c, 48 c, 
21 c, 22 c, 34 c, 41 c, 49 c, 
08 c, 23 c, 27 c, 42 c, 45 c, 
24 c, 26 c, 38 c, 43 c, 46 c, 
\ ...... 
07 c, 13 c, 39 c, 47 c, 50 c, 
16 c, 29 c, 32 c, 36 c, 41 c, 

Le fichier euroMillionFR.fs contient tous les tirages de l'EURO MILLION du 2004-02-13 au 2023-02-24.

Les définitions d'usage général

Ces définitions sont dans le fichier generalWords.fs.

La première définition qui nous intéresse est LOTTO:

structures 
struct LOTTO 
    ptr field ->nbsPerGrid       \ quantity of numbers per grid 
    ptr field ->nbgrids          \ number of grids stored 
    ptr field ->datas            \ datas in array 
forth 

La structure LOTTO permet de gérer trois pointeurs. Ces pointeurs sont utilisés pour gérer les données de la table euroMillion.

Pour permettre l'adaptation des données à d'autres tirages, le LOTTO américain par exemple, on va passer par une indirection en créant le mot LOTTOdatas:

\ The LOTTOdatas vector makes it possible to manage any type 
\ of grid, for example 5x50 or 6x49 or other... 
defer LOTTOdatas 

C'est ce mot LOTTOdatas qui va recevoir son vecteur, ici en fin de fichier euroMillionFR.fs:

' euroMillion to LOTTOdatas 
updateLottoDatasFields 

Il sera donc facile, si vous souhaitez gérer des données de tirage du LOTO de votre pays, de créer votre propre fichier, powerballUS.fs par exemple:

\ POWERBALL USA 
\ from 2004-02-13 to 2023-02-24 
create powerBall 
    5 ,         \ 5 numbers per grid in powerball (FR) 
    0 ,         \ init number of grids stored - 16 bits 
\ ...... numbers here 
 
' powerBall to LOTTOdatas 
updateLottoDatasFields 

Passé cette étape, les traitements des données de tirage utiliseront toujours le mot LOTTOdatas. Exemple:

\ fetch the number of numbers in a grid 
: nbsPerGrid@ ( -- n ) 
    LOTTOdatas ->nbsPerGrid @ 
  ; 

Le mot nbsPerGrid@ récupère le nombre de numéros tirés dans un tirage de LOTO.

Le mot nbgrids@ récupère le nombre de grilles compilées dans le tableau des tirages de LOTO.

Gestion des numéros dans la grille

Voyons maintenant comment accéder et gérer ces numéros dans ces grilles de tirage.

Les mots suivants sont définis dans le fichier gridsManage.fs.

Le mot getGridAddr récupère l'adresse d'une grille et le nombre de numéros dans cette grille:

\ fetch addr and len of a grid in LOTTOdatas 
: getGridAddr ( n -- addr n ) 
    nbsPerGrid@ dup >r * 
    getLOTTOdatasAddr + r> 
  ; 

Le mot getNumber récupère un numéreo dans une grille et dépose sur la pile ce numéro et l'adresse incrémentée de une unité. Cette incrémentation permet de préparer l'extraction du prochain numéro:

\ fetch number from addr; leave next addr and fetched number 
: getNumber ( addr -- addr+1 n ) 
    dup 1+ swap c@ 
  ; 

Le mot inGrid? indique si le nombre n figure dans une grille de tirage:

\ search n in grid 
: inGrid? { n gridPos -- fl } 
    0 { fl } 
    gridPos getGridAddr 
    for 
        aft 
            getNumber n = 
            if 
                -1 to fl 
            then 
        then 
    next 
    drop 
    fl 
  ; 

Voyons maintenant comment afficher une grille.

Affichage d'une grille de LOTO

Notre premier mot ## va simplement mettre un chiffre au format NN. Ainsi, tous les chiffres du LOTO seront toujours affichés sur 2 digits:

\ n in form 00..99 
: ## ( n -- addr len ) 
    base @ >r decimal 
    <# # # #> 
    r> base ! 
  ; 

Ensuite, nous allons définir quatre couleurs d'affichage et les mots .nWitheBlack et .nYellowRed qui vont afficher un chiffre sur fond coloré:

 0 constant BLACK 
 1 constant RED 
11 constant YELLOW 
15 constant WHITE 
 
: .nWitheBlack 
 ( n -- ) 
    WHITE fg  BLACK bg 
    ## type 
    space 
  ; 
 
: .nYellowRed ( n -- ) 
    YELLOW fg  RED bg 
    ## type 
    WHITE fg  BLACK bg 
    space 
  ; 

Et pour finir, voici le mot .grid qui affiche une grille de tirage depuis le numéro de grille:

\ display complete grid from position gridPos 
: .grid { gridPos -- } 
    gridPos 1- nbgrids@ >= 
    if 
        FRENCH  ?\ gridPos . ." hors limite" 
        ENGLISH ?\ gridPos . ." out of range" 
        exit 
    then 
    cr 
    NR_RANGE 0 do 
        i 1+ gridPos inGrid? 
        if 
            i 1+ .nYellowRed 
        else 
            i 1+ .nWitheBlack 
        then 
        space 
        i 1+ nbsPerGrid@ mod 0= 
        if 
            cr 
        then 
    loop 
  ; 

Dans cette copie écran, on demande à afficher la grille 44:

Statistiques du LOTO

Le principe du LOTO nous permet d'envisager certaines analyses statistiques. La première analyse statistiques venant à l'esprit, c'est tout simplement étudier la fréquence d'apparition des numéros tirés.

Fréquence de tirage des numéros

Le code complet se trouve dans le fichier numbersFrequency.fs.

On commence par définir la table frequencyTable qui va mémoriser la fréqence de tirage de chaque numéro:

create frequencyTable 
    NR_RANGE cell * allot 

On définir ensuite le mot getFreqAddr qui récupère l'adresse de l'item qui nous intéresse dans la table frequencyTable:

\ get addr from frequency position 
: getFreqAddr { position -- addr }     \ range of position: 0..NR_RANGE-1 
    position NR_RANGE 1- > 
    if 
        FRENCH  ?\ position ." position " . ." hors limite pour frequencyTable" 
        ENGLISH ?\ position ." position " . ." out of range for frequencyTable" 
        cr exit 
    then 
    position cell * frequencyTable + 
  ; 

Dans ce mot getFreqAddr, on vérifie si la position demandée n'est pas hors limite.

On définit ensuite le mot FreqAddr++ qui incrémente le contenu d'une valeur à la position demandée. Par exemple, si la boule 17 sort, on va incrémenter la valeur de position 17 dans la table frequencyTable.

\ increment contenu of frequency number 
: FreqAddr++ ( position -- )            \ range of position: 0..NR_RANGE-1 
    getFreqAddr 1 swap +! 
   ; 

Le mot initFrequency remet toutes les valeurs de la table frequencyTable à zéro:

\ reset content of frequencyTable 
: initFrequency ( -- ) 
    NR_RANGE for 
        aft 
            0  i 1- getFreqAddr ! 
        then 
    next 
  ; 

Le mot analyzeFrequency va lire le contenu de la table pointée par LOTTOdatas. Chaque boule tirée incrémente la position correspondante dans le tableau frequencyTable:

\ count apparition of each number in LOTTOdatas table 
: analyzeFrequency ( -- ) 
    initFrequency 
    nbsPerGrid@ nbgrids@ * 
    for 
        aft 
            i getLOTTOdatasAddr + c@ 
            1- FreqAddr++ 
        then 
    next 
  ; 

Et pour finir, on définit le mot .frequency qui analyse et affiche la fréquence de tirage de chaque boule du LOTO:

\ display frequency of numbers distribution 
: .frequency ( -- ) 
    analyzeFrequency 
    NR_RANGE for 
        aft 
            NR_RANGE i - dup ## type ."  :" 
            1- getFreqAddr @ 5 .r 6 spaces 
            i 5 mod 0= if cr then 
        then 
    next 
  ; 

Toutes ces valeurs analysées en FORTH correspondent à celles données par le site de la Française des Jeux.

Comme il est assez fastidieux de lire ces valeurs pour trouver la plus grande et la plus petite, on va demander à FORTH de faire ce travail pour nous.

Numbers drawn most and least often

The word searchHighLowFrequency will search for the highest and lowest value in our frequencyTable table.

This definition uses local values to make the program more readable on the one hand, to avoid complex stack manipulations on the other hand.

0 value LOW_NUM 
0 value HIGH_NUM 
 
: searchHighLowFrequency ( -- ) 
           1 { lowest_num  } 
 1 32 lshift { lowest_val  } 
    NR_RANGE { highest_num } 
           0 { highest_val } 
    NR_RANGE for 
        aft 
            \ searches for the most frequently dialed number 
            i getFreqAddr @ dup highest_val > 
            if 
                to highest_val 
                i 1+ to highest_num 
            else 
                drop 
            then 
            \ searches for the number issued the least often 
            i getFreqAddr @ dup lowest_val < 
            if 
                to lowest_val 
                i 1+ to lowest_num 
            else 
                drop 
            then 
        then 
    next 
    highest_num to HIGH_NUM 
     lowest_num to LOW_NUM 
  ; 

Pour afficher le résultat de la recherche de la plus grande et plus petite valeur de fréquence de sortie de chaque numéro, on crée le mot .frequHighLow qui va afficher le résultat proprement:

: .frequHighLow ( -- ) 
    searchHighLowFrequency 
    cr 
    FRENCH  ?\ ." numéro sorti le plus souvent....: " 
    ENGLISH ?\ ." number issued most often........: " 
    HIGH_NUM ## type ."  (" 
    HIGH_NUM 1- getFreqAddr @ . ."  x)" 
    cr 
    FRENCH  ?\ ." numéro sorti le moins souvent...: " 
    ENGLISH ?\ ." number issued least often.......: " 
    LOW_NUM ## type ."  (" 
    LOW_NUM 1- getFreqAddr @ . ."  x)" 
  ; 

Voici le résultat de l'exécution de .frequHighLow:

--> .frequHighLow
number issued most often........: 50 (356  x)
number issued least often.......: 22 (133  x)

Définition d'un interface

L'interface est la partie de code permettant un accès simple aux fonctionnalités du programme.

Notre premier mot interfaceTitle permet d'afficher à la position x y le titre de la page d'interface:

\ interface title 
: interfaceTitle ( x y -- ) 
    to _DY   to _DX 
    _DX _DY at-xy  ." +-----------------------------+" 1 _DY+ 
    _DX _DY at-xy 
    FRENCH  ?\ ." | ANALYSE CHIFFRES DU LOTO    |" 
    ENGLISH ?\ ." | LOTTO NUMBERS ANALYSIS      |" 
    1 _DY+ 
    _DX _DY at-xy  ." +-----------------------------+" 
  ; 

Le mot interfaceOptions affiche les options:

\ display options 
: interfaceOptions ( x y -- ) 
    to _DY   to _DX 
    _DX _DY  at-xy 
    FRENCH  ?\ ." D .. affiche la distribution des numeros du LOTO" 
    ENGLISH ?\ ." D .. display frequency of LOTTO numbers distribution" 
    1 _DY+ 
    _DX _DY at-xy 
    FRENCH  ?\ ." Q .. quitter" 
    ENGLISH ?\ ." Q .. quit" 
    2 _DY+ 
    5 _DX+ 
    _DX _DY at-xy 
    FRENCH  ?\ ." Appui touche: " 
    ENGLISH ?\ ." Press key: " 
  ; 

Le mot interfaceActions gère les actions selon la touche clavier activée:

\ execute options 
: interfaceActions ( key -- ) 
    2 _DY+   0 to _DX 
    _DX _DY  at-xy 
    case 
        [char] D  of 
            .frequency  cr 
            .frequHighLow 
        endof 
    endcase 
  ; 

Et enfin, le mot interface regroupe les précédentes définitions:

\ main interface 
: interface ( -- ) 
    page 
    begin 
        8 2 interfaceTitle 
        2 6 interfaceOptions 
        Xkey 
        dup [char] Q = 
        if 
            drop exit 
        else 
            interfaceActions 
        then 
    again 
  ; 

L'exécution de interface affiche ceci:

        +-----------------------------+
        | LOTTO NUMBERS ANALYSIS      |
        +-----------------------------+

  D .. display frequency of LOTTO numbers distribution
  Q .. quit

       Press key:

L'appui sur la touche D affiche ceci:

        +-----------------------------+
        | LOTTO NUMBERS ANALYSIS      |
        +-----------------------------+

  D .. display frequency of LOTTO numbers distribution
  Q .. quit

       Press key:

01 :  162      02 :  150      03 :  162      04 :  163      05 :  169
06 :  158      07 :  161      08 :  145      09 :  151      10 :  171
11 :  161      12 :  166      13 :  158      14 :  164      15 :  169
16 :  152      17 :  177      18 :  137      19 :  186      20 :  168
21 :  181      22 :  133      23 :  183      24 :  161      25 :  174
26 :  176      27 :  172      28 :  157      29 :  171      30 :  165
31 :  154      32 :  150      33 :  134      34 :  155      35 :  160
36 :  149      37 :  170      38 :  177      39 :  159      40 :  147
41 :  138      42 :  182      43 :  156      44 :  179      45 :  166
46 :  136      47 :  144      48 :  153      49 :  160      50 :  534


number issued most often........: 50 (534  x)
number issued least often.......: 22 (133  x)

Les définitions de la partie interface sont faciles à compléter si on rajoute d'autres traitement de données sur les numéros du LOTO.

Mais là n'est pas le but de cet article.

En passant en revue notre programme, on constate que les mots sont définis dans différents fichiers. Le contenu de chaque fichier regroupe les mots par fonctionnalités. Ceci permet un développement facile. Le contenu des différents fichier est chargé et compilé depuis le fichier main.fs.

Il est fortemùent conseillé de grouper les fichiers d'un projet dans un répertoire dédié. Dans notre exemple, ce répertoire s'appelle lotto.

Bonne programmation.


Legal: site web personnel sans commerce / personal site without seling