IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Arduino Discussion :

Utilisation de la Fonction random()


Sujet :

Arduino

  1. #1
    Membre éprouvé
    Inscrit en
    Juillet 2004
    Messages
    936
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 936
    Par défaut Utilisation de la Fonction random()
    Bonjour à tous ,

    J'essaie de mettre au point un petit jeu qui consiste entre autre à choisir la couleur Rouge ou Bleu d'un joueur afin de gagner des points
    le jeu se trouve sur Wokwi en test :
    https://wokwi.com/projects/431471414661421057

    et il semble fonctionner correctement néanmoins je trouve que c'est souvent la couleur bleue qui gagne (?)
    je m'interroge donc sur la fonction random()
    est-elle bien adaptée ici ou est-ce moi qui ne l'utilise pas correctement ?
    ou alors existe-t-il une autre façon de faire

    merci par avance pour votre éclairage

    Cordialement
    pascal

  2. #2
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 250
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 250
    Par défaut
    Bonjour,
    Pour le bleu qui gagne souvent c'est un biais statistique, car la suite que tu demandes n'est qu'entre 1 et 2.
    -> Tu peux faire un random entre 1 et 1000 puis considérer c'est une couleur qui gagne à partir d'un seuil et l'autre si c'est inférieur a ce seuil. La valeur du seuil peut aussi être issu d'un random.

    Sauf erreur la fonction random() génère une suite pseudo aléatoire qui s'initialise avec la même valeur au démarrage. Donc a chaque démarrage tu auras toujours la même suite aléatoire. Vérifie mais surement qu'a chaque réinitialisation de ton simulateur tu vas t'en rendre compte.
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  3. #3
    Membre éprouvé
    Inscrit en
    Juillet 2004
    Messages
    936
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 936
    Par défaut
    Salut Vincent

    merci bien

    -> Tu peux faire un random entre 1 et 1000 puis considérer c'est une couleur qui gagne à partir d'un seuil et l'autre si c'est inférieur a ce seuil. La valeur du seuil peut aussi être issu d'un random.
    j'ai fait çà mais çà ne doit aps être la bonne manip car quelque soit le "randNumber"
    la couleur reste rouge (?)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    bool tirageInitial() {
    animationBlanche();
    randNumber = random(1000);
    Serial.println(randNumber);
    if (randNumber <=500){
       bool couleur= 0; // 0 = ROUGE
    }else if (randNumber > 500){
          bool couleur= 1; //1 = BLEU
    }
    clignoter(couleur);
    updateDisplay();
      Serial.print("🔰 Le joueur qui commence est : ");
      Serial.println(couleur ? "BLEU" : "ROUGE");
      return couleur;
    }

  4. #4
    Membre éprouvé
    Inscrit en
    Juillet 2004
    Messages
    936
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 936
    Par défaut
    après plusieurs essais
    j'ai fait çà , j'espère que çà répond à la demande

    https://wokwi.com/projects/431485674440119297

  5. #5
    Membre Expert Avatar de edgarjacobs
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2011
    Messages
    766
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 65
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2011
    Messages : 766
    Par défaut
    Hello,

    Deux erreurs dans le code:
    1) inutile: ligne 8: pas besoin de tester randNumber>500
    2) bien plus grave: tu déclares aux lignes 7 et 9 la variable couleur, mais sorti du bloc (ce qui se trouve entre les accolades), la variable n'existe plus. Donc, je suis prêt à parier que tu as une variable globale couleur qui est initialisée à 0, sinon tu aurais une erreur de compilation. Et donc, c'est toujours le rouge qui commence. Supprime le terme bool aux lignes 7 et 9, et le code fera ce que tu attends de lui.

    >> Sauf erreur la fonction random() génère une suite pseudo aléatoire qui s'initialise avec la même valeur au démarrage. Donc a chaque démarrage tu auras toujours la même suite aléatoire
    c'est tout à fait exact. Pour éviter cela, il existe la fonction randomSeed().
    On écrit "J'ai tort" ; "tord" est la conjugaison du verbre "tordre" à la 3ème personne de l'indicatif présent ;)

  6. #6
    Membre éprouvé
    Inscrit en
    Juillet 2004
    Messages
    936
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 936
    Par défaut
    salut edgarjacobs

    merci d'avoir répondu
    j'ai mis une autre version du programme dans le post précédent, qui j'espère, répond à tes conseils
    je mets ici la dernière version complète

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
     
    #include <Adafruit_NeoPixel.h>
    #include "TM1637Display.h"
     
    #define BUTTON_PIN     2
    #define RESET_PIN      6
    #define LEDA_PIN       3
    #define RED_PIN        4
    #define BLUE_PIN       5
    #define NUM_LEDS       10
     
    #define CLK            7
    #define DIO            8
     
    TM1637Display display(CLK, DIO);
    Adafruit_NeoPixel ledA(16, LEDA_PIN, NEO_GRB + NEO_KHZ800);
    Adafruit_NeoPixel redLED(NUM_LEDS, RED_PIN, NEO_GRB + NEO_KHZ800);
    Adafruit_NeoPixel blueLED(NUM_LEDS, BLUE_PIN, NEO_GRB + NEO_KHZ800);
     
    bool joueurBleuActif = false;
    bool gameStarted = false;
    int redScore = 0;
    int blueScore = 0;
    int totalTurns = 0;
    long randNumber;
    bool lastButtonState = HIGH;
    bool lastResetState = HIGH;
     
     
    void setup() {
      Serial.begin(9600);
      randomSeed(analogRead(0));  // <======
      pinMode(BUTTON_PIN, INPUT_PULLUP);
      pinMode(RESET_PIN, INPUT_PULLUP);
      ledA.begin();
      redLED.begin();
      blueLED.begin();
      ledA.show(); redLED.show(); blueLED.show();
      display.setBrightness(7);
      resetGame();
    }
     
    void loop() {
      bool startState = digitalRead(BUTTON_PIN);
      bool resetState = digitalRead(RESET_PIN);
     
      if (lastResetState == HIGH && resetState == LOW) {
        resetGame();
      }
     
      if (lastButtonState == HIGH && startState == LOW && !isGameOver()) {
        if (!gameStarted) {
          joueurBleuActif = tirageInitial();
          gameStarted = true;
        } else {
          tourDeJeu();
        }
      }
     
      lastButtonState = startState;
      lastResetState = resetState;
    }
     
    void resetGame() {
      joueurBleuActif = false;
      redScore = 0;
      blueScore = 0;
      totalTurns = 0;
      gameStarted = false;
     
      ledA.clear(); ledA.show();
      redLED.clear(); redLED.show();
      blueLED.clear(); blueLED.show();
      display.clear();
      updateDisplay();
      Serial.println("🔄 Jeu réinitialisé !");
    }
     
    bool tirageInitial() {
      bool couleur; // <======
      animationBlanche();
      randNumber = random(0,1000);
      Serial.println(randNumber);
      if (randNumber <=500){
          couleur = 0; // 0 = ROUGE
      }else if (randNumber > 500){
          couleur = 1; //1 = BLEU
      }
     
      Serial.println(couleur);
      clignoter(couleur);
      updateDisplay();
      Serial.print("🔰 Le joueur qui commence est : ");
      Serial.println(couleur ? "BLEU" : "ROUGE");
      return couleur;
    }
     
    void tourDeJeu() {
      bool couleur;
      animationBlanche();
     
      randNumber = random(0,1000);
      Serial.println(randNumber);
      if (randNumber <=500){
          couleur = 0; // 0 = ROUGE
      }else if (randNumber > 500){
          couleur = 1; //1 = BLEU
      }
     
      Serial.println(couleur);
      clignoter(couleur);
     
      Serial.print("🎲 Couleur tirée : ");
      Serial.println(couleur ? "BLEU" : "ROUGE");
     
      if (couleur == joueurBleuActif) {
        if (joueurBleuActif && blueScore < NUM_LEDS) {
          blueScore++;
        } else if (!joueurBleuActif && redScore < NUM_LEDS) {
          redScore++;
        }
        Serial.println("✅ Bon joueur → rejoue");
      } else {
        if (joueurBleuActif) {
          if (blueScore > 0) blueScore--;
          if (redScore < NUM_LEDS) redScore++;
          joueurBleuActif = false;
        } else {
          if (redScore > 0) redScore--;
          if (blueScore < NUM_LEDS) blueScore++;
          joueurBleuActif = true;
        }
        Serial.println("❌ Mauvais joueur → adversaire rejoue");
      }
     
      totalTurns++;
      updateScores();
      updateDisplay();
     
      if (redScore == NUM_LEDS) {
        Serial.println("🏆 Joueur ROUGE GAGNE !");
        flashWin(redLED, redLED.Color(255, 0, 0));
      } else if (blueScore == NUM_LEDS) {
        Serial.println("🏆 Joueur BLEU GAGNE !");
        flashWin(blueLED, blueLED.Color(0, 0, 255));
      }
     
      Serial.print("🔴 ROUGE: "); Serial.println(redScore);
      Serial.print("🔵 BLEU : "); Serial.println(blueScore);
      Serial.print("⏳ Nb Tours : "); Serial.println(totalTurns);
    }
     
    bool isGameOver() {
      return (redScore == NUM_LEDS || blueScore == NUM_LEDS );
    }
     
    void animationBlanche() {
      for (int i = 0; i < 15; i++) {
        ledA.clear();
        ledA.setPixelColor(i, ledA.Color(255, 255, 255));
        ledA.show();
        delay(60);
      }
    }
     
    void clignoter(bool isBlue) {
      for (int i = 0; i < 6; i++) {
        ledA.fill(isBlue ? ledA.Color(0, 0, 255) : ledA.Color(255, 0, 0));
        ledA.show();
        delay(200);
        ledA.clear();
        ledA.show();
        delay(200);
      }
    }
     
    void updateScores() {
      redLED.clear();
      blueLED.clear();
     
      for (int i = 0; i < redScore; i++) {
        redLED.setPixelColor(i, redLED.Color(255, 0, 0));
      }
      for (int i = 0; i < blueScore; i++) {
        blueLED.setPixelColor(i, blueLED.Color(0, 0, 255));
      }
     
      redLED.show();
      blueLED.show();
    }
     
    void updateDisplay() {
      // Affichage des tours restants + joueur actif (r = 0x50, b = 0x7C)
      uint8_t digits[4];
     
      digits[0] = display.encodeDigit(totalTurns / 10);
      digits[1] = display.encodeDigit(totalTurns % 10);
      digits[2] = 0;
      //digits[3] = joueurBleuActif ? 0x3E : 0x50; // 'b' or 'r'
      if (totalTurns ==0 ){
        digits[3] = 0x40; // '-'
      }else{
        digits[3] = joueurBleuActif ? 0x7C : 0x50; // 'b' or 'r'
      }
     
     
      display.setSegments(digits);
    }
     
    void flashWin(Adafruit_NeoPixel &strip, uint32_t color) {
      for (int i = 0; i < 6; i++) {
        strip.fill(color); strip.show();
        delay(250);
        strip.clear(); strip.show();
        delay(250);
      }
    }

  7. #7
    Membre Expert Avatar de edgarjacobs
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2011
    Messages
    766
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 65
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2011
    Messages : 766
    Par défaut
    Hello,

    Je n'ai pas complètement parcouru le code, mais puisque tu écris deux fois la même chose, tu devrais écrire (simplifié, c'est plus clair)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // Renvoie false (0) pour ROUGE, true (1) pour BLEU
    bool DeterminePlayer(void) {
      return(random(0, 1000) > 500);
    }
     
    bool tirageInitial() {
      bool couleur=DeterminePlayer();
      ....
     
    }
     
    void tourDeJeu() {
      bool couleur=DeterminePlayer();
     
      ....
     
    }
    Je sais, ça a l'air idiot une fonction d'une ligne, mais si tu désires un jour modifier l'intervalle pour random(), tu n'auras qu'une seule modification à faire.
    On écrit "J'ai tort" ; "tord" est la conjugaison du verbre "tordre" à la 3ème personne de l'indicatif présent ;)

  8. #8
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 889
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 889
    Par défaut
    Citation Envoyé par Vincent PETIT Voir le message
    Pour le bleu qui gagne souvent c'est un biais statistique, car la suite que tu demandes n'est qu'entre 1 et 2.
    Il n'y a pas de raison que ça fonctionne mieux avec deux valeurs ou avec un seuil. C'est simplement que statistiquement il faut quand même un grand nombre de tirages pour arriver à une conclusion.

    Si vous faites tourner ce code qui imprime le nombre de 1 et le nombre de 2 obtenus tous les 1000 tirages ainsi que le pourcentage de différence entre les deux nombres, vous verrez que l'on tombe assez vite vers les 0.0x% ce qui n'est pas mal. Il ne fait pas juger sur 10 tirages.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    long nb1 = 0, nb2 = 0;
     
    void setup() {
      randomSeed(analogRead(A0));
      Serial.begin(115200);
      Serial.println("nb1\tnb2\∆");
    }
     
    void loop() {
      int v = random(1, 3); // valeur 1 ou 2
      if (v == 1) nb1++; else nb2++;
      if ((nb1 + nb2) % 1000 == 0) { // on imprime tous les 1000 tirages
        Serial.print(nb1);
        Serial.write('\t');
        Serial.print(nb2);
        Serial.write('\t');
        double delta = nb2 - nb1;
        delta /= (nb1 + nb2);
        delta *=100;
        Serial.print(delta , 2);
        Serial.println('%');
      }
    }

  9. #9
    Membre Expert

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 598
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 598
    Par défaut
    Bonjour,

    random(n) renvoie un nombre entre 0 et n-1. Donc random(1000) renvoie un nombre entre 0 et 999. La partition en 2 est alors 0..499 et 500..999. Or le test <= 500 correspond à 0..500 soit 501 valeurs contre 501..999 soit 499 valeurs. Il y a donc un biais.

    Par ailleurs, il est inutile de travailler avec des bornes et un seuil. Il n'y a nul gain alors que l'écriture directe : couleur = random(2); donne directement le résultat attendu.

    Autre chose, tester un front descendant peut être plus simple et plus rapide (sauf si le compilateur est plus malin que nous) que : if (lastResetState == HIGH && resetState == LOW) { ... }.
    Par exemple : if (lastResetState > resetState) { ... } ce qui exprime juste que avant est plus grand qu'après

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  10. #10
    Membre éprouvé
    Inscrit en
    Juillet 2004
    Messages
    936
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 936
    Par défaut
    Citation Envoyé par Jay M Voir le message
    Il n'y a pas de raison que ça fonctionne mieux avec deux valeurs ou avec un seuil. C'est simplement que statistiquement il faut quand même un grand nombre de tirages pour arriver à une conclusion.

    Si vous faites tourner ce code qui imprime le nombre de 1 et le nombre de 2 obtenus tous les 1000 tirages ainsi que le pourcentage de différence entre les deux nombres, vous verrez que l'on tombe assez vite vers les 0.0x% ce qui n'est pas mal. Il ne fait pas juger sur 10 tirages.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    long nb1 = 0, nb2 = 0;
     
    void setup() {
      randomSeed(analogRead(A0));
      Serial.begin(115200);
      Serial.println("nb1\tnb2\∆");
    }
     
    void loop() {
      int v = random(1, 3); // valeur 1 ou 2
      if (v == 1) nb1++; else nb2++;
      if ((nb1 + nb2) % 1000 == 0) { // on imprime tous les 1000 tirages
        Serial.print(nb1);
        Serial.write('\t');
        Serial.print(nb2);
        Serial.write('\t');
        double delta = nb2 - nb1;
        delta /= (nb1 + nb2);
        delta *=100;
        Serial.print(delta , 2);
        Serial.println('%');
      }
    }

    Salut Jay M

    j'ai fait tourner le programme , je trouve en moyenne -0.14%

    mais je comprends pas pourquoi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     int v = random(1, 3); // valeur 1 ou 2
    et pas
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     int v = random(1, 2); // valeur 1 ou 2

  11. #11
    Membre éprouvé
    Inscrit en
    Juillet 2004
    Messages
    936
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 936
    Par défaut
    Citation Envoyé par Guesset Voir le message
    Bonjour,

    random(n) renvoie un nombre entre 0 et n-1. Donc random(1000) renvoie un nombre entre 0 et 999. La partition en 2 est alors 0..499 et 500..999. Or le test <= 500 correspond à 0..500 soit 501 valeurs contre 501..999 soit 499 valeurs. Il y a donc un biais.

    Par ailleurs, il est inutile de travailler avec des bornes et un seuil. Il n'y a nul gain alors que l'écriture directe : couleur = random(2); donne directement le résultat attendu.

    Autre chose, tester un front descendant peut être plus simple et plus rapide (sauf si le compilateur est plus malin que nous) que : if (lastResetState == HIGH && resetState == LOW) { ... }.
    Par exemple : if (lastResetState > resetState) { ... } ce qui exprime juste que avant est plus grand qu'après

    Salutations
    Salut Guesset

    devrais-je mettre plutôt :

  12. #12
    Membre Expert

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 598
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 598
    Par défaut
    Bonjour cobra38,

    Citation Envoyé par cobra38 Voir le message
    devrais-je mettre plutôt :
    En utilisant juste couleur = random(2); couleur vaudra 0 ou 1 sans nécessiter quelque test que ce soit.

    Je n'ai pas regardé l'implémentation de ce générateur, mais en général ils génèrent une séquence de mots de longueur fixe de 32 bits (voire 64) qui sont projetés sur la gamme cible par une opération linéaire. Cette projection peut introduire un léger biais. Cependant dans le cas d'une gamme (ie vmax - vmin) égale à une puissance de 2, il n'y a pas de biais car 232 / 2a avec a < 31 tombe naturellement juste. Donc 4096, 1024, 256 ou 2 n'ont pas de biais.

    Salut
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  13. #13
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 250
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 250
    Par défaut
    Bonjour,
    Mon idée du départ était d'élargir la distribution pseudo aléatoire parce qu'un nombre aléatoire entre 1 et 2 ça me parait peu. Une loi binomial converge vers une loi normal (et je pense que le générateur pseudo aléatoire suit cette loi) si on fait un grand nombre de tirage sauf qu'ici ce n'est pas le cas.
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  14. #14
    Membre Expert

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 598
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 598
    Par défaut
    Bonjour,

    Le principe d'élargir la base serait parfait si le générateur adaptait son fonctionnement à cette base. Plus d'états, moins de biais.

    Mais, en fait, les générateurs travaillent toujours sur la même largeur (32 ou 64 bits) et la mise à l'échelle n'a lieu qu'après la génération d'un nouvel état n par une expression de type ((smax - smin)*n) >> 32 + smin.
    Le décalage peut même être évité en prenant juste la valeur à l'adresse du int32_t suivant du résultat avant ajout de smin. C'est pourquoi les gammes en 2a donnent de meilleurs résultats car la troncature de la pseudo division altère moins de bits.

    Ainsi quitte à étendre l'échelle, il serait plus efficace de la mettre à une échelle en 2a donc 1024 vs 1000. Mais le test opérant une partition en 2 ignorera les bits de poids faible. En effet if(r < 512) est équivalent à ne s'intéresser qu'au 10e bit (le n°9). Alors, à quoi bon ?

    Salutations
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  15. #15
    Membre prolifique Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    Février 2011
    Messages
    6 819
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : Février 2011
    Messages : 6 819
    Par défaut
    Salut à tous.

    Quand j'ai besoin d'un vrai générateur de nombres aléatoires, j'utilise un générateur congruentiel linéaire comme ceux de Derrick Lehmer, où la valeur de c=0 :

    avec a=16807 et m=2147483647 (un grand nombre premier de Mersenne), et pour la graine, une valeur entre 1 et m-1.
    Cela se nomme le minimal standard Lehmer RNG.
    C'est simple à programmer, il n'y a pas de biais dans la production des nombres aléatoires.

  16. #16
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 889
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 889
    Par défaut
    Citation Envoyé par cobra38 Voir le message
    mais je comprends pas pourquoi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     int v = random(1, 3); // valeur 1 ou 2
    et pas
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     int v = random(1, 2); // valeur 1 ou 2
    La fonction random() retourne un nombre entre la première valeur (comprise) et la seconde (non comprise). si vous faites random(1, 2); vous n'aurez que des 1 puisque 2 est exclu. En faisant random(1, 3) on a effectivement 2 choix, soit 1 soit 2 (3 est exclu).

  17. #17
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 889
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 889
    Par défaut
    Citation Envoyé par Artemus24 Voir le message
    Quand j'ai besoin d'un vrai générateur de nombres aléatoires, j'utilise un générateur congruentiel linéaire comme ceux de Derrick Lehmer
    oui c'est une bonne approche. Sur petit arduino 8 bits, ce sera ~20% plus lent que l'appel à random() ça reste généralement supportable. Le ralentissement est dû au fait que l'on doit passer en unsigned long long pour la multiplication de 2 valeurs de 32 bits donc le résultat pourrait déborder ==> on veut éviter ça et on peut appliquer la méthode de Schrage qui consiste à découper le calcul en deux morceaux plus petits, pour qu'aucun produit intermédiaire ne dépasse la taille limite.

    En faisant cela on doit alors être même plus rapide que la fonction random arduino mais la normalisation pour obtenir l'équivalent de random(a,b) pour mettre la valeur dans l'intervalle [a,b[ va ralentir et les mettre sans doute sur le même plan.

  18. #18
    Membre Expert

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 598
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 598
    Par défaut
    Bonjour Jay M

    Citation Envoyé par Jay M Voir le message
    Il n'y a pas de raison que ça fonctionne mieux avec deux valeurs ou avec un seuil. C'est simplement que statistiquement il faut quand même un grand nombre de tirages pour arriver à une conclusion.
    L'importance du nombre de tirages est effectivement primordiale.

    Mais le choix de la gamme n'est pas totalement neutre. Avec 1000, par exemple, il y a 296 valeurs de probabilité 4294968/232 et le reste, 704, de probabilité 4294967/232. Cela induit un biais monstrueux de 2-32. Les 1000 valeurs ne sont pas équiprobables. Ce drame n'arrive pas avec des puissances de 2.

    Salut
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  19. #19
    Membre Expert

    Homme Profil pro
    Directeur de projet
    Inscrit en
    Mai 2013
    Messages
    1 598
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Directeur de projet
    Secteur : Service public

    Informations forums :
    Inscription : Mai 2013
    Messages : 1 598
    Par défaut
    Bonjour Artemus,

    Citation Envoyé par Artemus24 Voir le message
    ...C'est simple à programmer, il n'y a pas de biais dans la production des nombres aléatoires.
    Le biais apparaît, hélas, à la mise à l'échelle. Il va falloir répartir les 2147483647 valeurs en (smax-smin) valeurs. Avec 2 valeurs, il y aura automatiquement l'une des deux qui aura une probabilité supérieure de 1/2147483647. Avec un nombre premier de valeurs, toute mise à l'échelle produira un biais car il semble que les nombres premiers se divisent mal

    Salut
    Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better. (Samuel Beckett)

  20. #20
    Membre éprouvé
    Inscrit en
    Juillet 2004
    Messages
    936
    Détails du profil
    Informations forums :
    Inscription : Juillet 2004
    Messages : 936
    Par défaut
    Citation Envoyé par Jay M Voir le message
    La fonction random() retourne un nombre entre la première valeur (comprise) et la seconde (non comprise). si vous faites random(1, 2); vous n'aurez que des 1 puisque 2 est exclu. En faisant random(1, 3) on a effectivement 2 choix, soit 1 soit 2 (3 est exclu).
    Salut Jay M


    Pour que je comprenne bien , lorsque je fais
    la valeur 1000 n'est jamais prise en compte

    le nombre aléatoire sera donc toujours compris ente 1 et 999

Discussions similaires

  1. Utilisation de la fonction Random
    Par Djemàa dans le forum Langage
    Réponses: 4
    Dernier message: 03/04/2012, 19h50
  2. Utilisation de la fonction randomize
    Par acacia dans le forum C
    Réponses: 7
    Dernier message: 21/01/2008, 00h37
  3. [LG]Utilisation de la fonction Random
    Par chloe95 dans le forum Langage
    Réponses: 1
    Dernier message: 01/03/2005, 14h20
  4. Fonction Random en Assembleur
    Par chidi dans le forum Assembleur
    Réponses: 5
    Dernier message: 21/05/2004, 10h16
  5. [LG]librairies : utiliser seulement quelques fonctions
    Par wwwroom dans le forum Langage
    Réponses: 13
    Dernier message: 14/05/2004, 22h50

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo