Tirer une série de variables aléatoires sans remises

Bonjour à toustes,

Je suis sur un petit projet très simple : Allumer aléatoirement 3 LED différentes parmi 9 LED NeoPixel.
Jusque là rien de bien compliqué. Sauf que dans ma version (cf code ci dessous), l'arduino allume parfois 2 Led parfois 3. Cela est du au fait je pense que deux variables peuvent prendre la même valeur...
Comment donc exclure une valeur dans la fonction random ? Est-ce possible ?
Où la meilleure solution serait de faire des boucles : si la valeur est égal à une précédente, relancer le dé jusqu'à ce que cette dernière soit différentes des autres ?

Merci par avance pour votre aide

Guillaume

c++
/*Le projet Trobar est réalisé dans le cadre du marathon créatif paserelle.infini eyant eu lieu à la paserelle à Brest le 9 et 10 mars 2023.
Il est le programme du second prototype réalisé au moyen d'une Arduino Nano 
Ce code alume 9 led à la pression d'un bouton poussoir et choisis au hasard une des 9 cases qu'il allumera en blanc*/

#include <Adafruit_NeoPixel.h> //Nécessite de préinstaller dans votre IDE la librairie Adafruit_NeoPixel.h
#ifdef __AVR__
#include <avr/power.h>
#endif

// Défini le numéro de broche où sera connecté le fil de datas du ruban LED
#define PIN 3 

// Combien de DEL y a t-il sur votre trobar ? 
#define NUMPIXELS 9

Adafruit_NeoPixel strip(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

//Défini le numéro de broche où sera connecté le bouton poussoir
int bouton=2;

//Défini une variable qui servira à choisir un nombre aléatoire. Ne pas modifier cette variable.
int nbreAleatoire1=0;
int nbreAleatoire2=0;
int nbreAleatoire3=0;


void setup(){  
  pinMode(bouton, INPUT_PULLUP);  
  strip.begin(); // Initialise le ruban LED
}

void loop(){
if(!digitalRead(bouton)){
  strip.clear();
  depart(50);
  delay(1000);
  strip.clear();
  nbreAleatoire1 = random(0, 8);
  nbreAleatoire2 = random(0, 8);
  nbreAleatoire3 = random(0, 8);
  strip.setPixelColor(nbreAleatoire1, strip.Color(150, 150, 150));
  strip.setPixelColor(nbreAleatoire2, strip.Color(150, 150, 150));
  strip.setPixelColor(nbreAleatoire3, strip.Color(150, 150, 150));
  strip.show();
}
}

void depart(int wait){
  int firstPixelHue = 0;     // Premier pixel démarre sur rouge
  for(int a=0; a<60; a++) {  // Répète 60 fois
    for(int b=0; b<3; b++) {
      strip.clear();         
      for(int c=b; c<strip.numPixels(); c += 3) {
        int      hue   = firstPixelHue + c * 65536L / strip.numPixels();
        uint32_t color = strip.gamma32(strip.ColorHSV(hue)); 
        strip.setPixelColor(c, color); 
      }
      strip.show();                
      delay(wait);                 
      firstPixelHue += 65536 / 90;
    }
  }
}

Le plus directe est effectivement de boucler tant que tes variables ne sont pas initialisées avec une valeur différente supérieur à 0.

Ce n'est pas possible avec random

Ce n'est pas une mauvaise solution et c'est la plus simple à programmer (si on joue de malchance, ça peut durer très longtemps)....

Pour éviter de jouer de malchance (quelqu'un qui ne tirerait que des "six" pendant un temps fou)
, l'algorithme https://www.irem.univ-mrs.fr/IMG/pdf/tirage_sans_remise-2.pdf s'effectue à temps constant et est un peu plus difficile à programmer que votre solution (nécessite un petit tableau en plus)...

Bonjour gheleguen

Peut être avec un tableau de booléens qui indique si le chiffre est déjà tiré:

const byte hasMaximum = 9;     // Valeur maximum
const byte hasNombre = 3;     // Nombre de chiffres au hasard

boolean hasChiffreTire[hasMaximum];     // Marquage des chiffres tirés

void setup()
{
	Serial.begin(115200);
}

void loop()
{
	hasardTirage();
	hasardTirageAffichage();
	delay(1000);


}

void hasardTirageAffichage()
{
	Serial.print(F("LED a allumer\t"));

	for (byte t = 0; t < hasMaximum; t ++)     // Lister tout le tirage
	{
		if (hasChiffreTire[t])     // Si le chiffrre a été tiré
		{
			Serial.print(String(t) + "-");
		}
	}
	Serial.println("");
}

void hasardTirage()
{
	for (byte t = 0; t < hasMaximum; t ++)     // Mettre le tableau tout à false
	{
		hasChiffreTire[t] = false;
	}

	for (byte t = 0; t < hasNombre; t ++)
	{
		while (1)     // Boucle infinie
		{
			byte tirage = random(0, hasMaximum);     // Tirage aléatoire
			if (!hasChiffreTire[tirage])     // Si pas encore tiré
			{
				hasChiffreTire[tirage] = true;     // Tirage enregistré
				break;     // On quitte la boucle
			}
		}
	}
}

Cordialement
jpbbricole

Bonjour jphbricole
Votre hazardTirage a un temps d'execution potentiellement infini (que se passe-t-il si random devient très bègue? elle prendra un temps fou... (ça fait partie de sa nature aleatoire)

Une solution (mange 8 bytes de plus) qui évite de jouer de malchance (testée sur PC; a adapter) est:

// adapté de https://en.wikipedia.org/wiki/Random_permutation
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
uint8_t uniform(uint8_t maxi) { // emule grossièrement random(0, maxi) de Arduino
  return (uint8_t) (rand()  % maxi);
}
// fin des initialisations specifiques au PC

void initialize_and_permute(uint8_t permutation[], uint8_t n) {
  uint8_t aux;
  for (uint8_t i = 0; i <= n - 2; i++) {
    uint8_t j = i + uniform(n - i); /* A random integer such that i ≤ j < n */
    aux = permutation[i];
    permutation[i] = permutation[j];
    permutation[j] = aux;
  }
}
/* specifique au PC
sur arduino a_permuter[ ] = {0,1,2,3,4,5,6,7};
initialize_and_permute( a_permuter, 8);
puis nbreAleatoire1 = apermuter[0];
... nbreAleatoire3 = a_permuter[2]:
*/


int main() {
uint8_t a_permuter[ ] = {10,24,3,4,5,6,7,8};
initialize_and_permute( a_permuter, 8);
for (uint8_t i =0; i < 8; i ++ ) printf("%d \n", a_permuter[i]);

}

Bonjour à toustes,
Merci pour vos réponses. J'ai opté pour la solution de boucle.
Voilà mon code :

c++
/*Le projet Trobar est réalisé dans le cadre du marathon créatif paserelle.infini eyant eu lieu à la paserelle à Brest le 9 et 10 mars 2023.
Il est le programme du second prototype réalisé au moyen d'une Arduino Nano 
Ce code alume 9 led à la pression d'un bouton poussoir et choisis au hasard une des 9 cases qu'il allumera en blanc*/

#include <Adafruit_NeoPixel.h> //Nécessite de préinstaller dans votre IDE la librairie Adafruit_NeoPixel.h
#ifdef __AVR__
#include <avr/power.h>
#endif

// Défini le numéro de broche où sera connecté le fil de datas du ruban LED
#define PIN 3 

// Combien de DEL y a t-il sur votre trobar ? 
#define NUMPIXELS 9

Adafruit_NeoPixel strip(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

//Défini le numéro de broche où sera connecté le bouton poussoir
int bouton=2;

//Défini une variable qui servira à choisir un nombre aléatoire. Ne pas modifier cette variable.
int nbreAleatoire1=0;
int nbreAleatoire2=0;
int nbreAleatoire3=0;


void setup(){  
  Serial.begin(9600);
  pinMode(bouton, INPUT_PULLUP);  
  strip.begin(); // Initialise le ruban LED
}

void loop(){
if(!digitalRead(bouton)){
  strip.clear();
  depart(50);
  delay(1);
  strip.clear();
  nbreAleatoire1 = random(0, 8);
  nbreAleatoire2 = random(0, 8);
  nbreAleatoire3 = random(0, 8);
  while (nbreAleatoire2 == nbreAleatoire1){
    nbreAleatoire2 = random(0, 8);
  }
  while (nbreAleatoire3 == nbreAleatoire1 || nbreAleatoire3 == nbreAleatoire2) {
    nbreAleatoire3 = random(0, 8);
  }
  strip.setPixelColor(nbreAleatoire1, strip.Color(150, 0, 0));
  strip.setPixelColor(nbreAleatoire2, strip.Color(0, 0, 150));
  strip.setPixelColor(nbreAleatoire3, strip.Color(0, 150, 0));
  strip.show();
  Serial.println(nbreAleatoire1);
  Serial.println(nbreAleatoire2);
  Serial.println(nbreAleatoire3);
}
}

void depart(int wait){
  int firstPixelHue = 0;     // Premier pixel démarre sur rouge
  for(int a=0; a<60; a++) {  // Répète 60 fois
    for(int b=0; b<3; b++) {
      strip.clear();         
      for(int c=b; c<strip.numPixels(); c += 3) {
        int      hue   = firstPixelHue + c * 65536L / strip.numPixels();
        uint32_t color = strip.gamma32(strip.ColorHSV(hue)); 
        strip.setPixelColor(c, color); 
      }
      strip.show();               
      delay(wait);                 
      firstPixelHue += 65536 / 90;
    }
  }
}

Oui c'est nickel, simple et efficace :slight_smile:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.