Select randomly X numbers in an array

You could also perform a shuffle and then pick the first 6 (or any 6 really).

Code for the first 6 would be much easier. When quantum computers come there might be an 'any' instruction! : o )

Thank you for the explanations !
That's why I love the forums.
Thanks again.

GoForSmoke:
Oh ho! So you only move the top element into the "hole" and reduce the count! Wish I'd caught that the first time!

I saw that trick a couple weeks ago in a another thread about picking from an array in random order with no repeats. Before that my recommendation would have been shuffling the array and picking the first N.
I just thought of a new trick... Instead of just moving the last element to the hole, swap the two. That way the selection can be repeated and all of the numbers will still be there. This eliminates the need to work on a copy of the array.

const int number = 6;
int myH1[] = {21, 22, 23, 24, 25, 26, 33, 34, 35};

const int intervalleTemps = 4000;

void select() {
    int count = sizeof myH1 / sizeof myH1[0];
    Serial.print("(");
    for (int i = 0; i < number; i++) {
        int index = random(count);   // Random integer from 0 to count-1

        Serial.print("Random");
        Serial.print(i+1);
        Serial.print(" = ");
        Serial.print(myH1[index]);
        
        if (i==number-1)   // If last value
            Serial.println(")");   
        else
            Serial.print(", ");

        // Swap the last element with the used element
        int temp = myH1[index];
        myH1[index] = myH1[count-1];
        myH1[count-1] = temp;

        count--;        
    }
}


void setup() {
  // put your setup code here, to run once:
    Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
select();
delay(intervalleTemps);
}

I can't karma you twice in such a short period can I?

Someone else will have to get this one.....

BTW, TYVM John!

So... in English, I have a deck of cards. I shuffle once. Subsequently, I swap each of the first 6 cards with another randomly chosen card in the deck. Then I only have to swap 6 cards each time, not 52. Did I get that right?

const int numberOfRands = 6;
byte myValues[] = {21, 22, 23, 24, 25, 26, 33, 34, 35};
const int count = sizeof myValues;

const int intervalleTemps = 4000;

void select() {
  for (int i = 0; i < numberOfRands; i++)
  {
    swap(myValues + i, myValues + random(count) );
  }
}

void printValues() {
  Serial.print("(");
  for (int i = 0; i < numberOfRands; i++)
  {
    Serial.print("Random");
    Serial.print(i + 1);
    Serial.print(" = ");
    Serial.print(myValues[i]);
    if (i == numberOfRands - 1) // If last value
      Serial.println(")");
    else
      Serial.print(", ");
  }
}

void swap(byte *a, byte *b)
{
  byte c = *a;
  *a = *b;
  *b = c;
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  select();
  printValues();
  delay(intervalleTemps);
}

Hi !

It's now working good but i have just a little problem (Negative numbers or numbers not in my 4Array) and don't know why.

Here is my output :

Il est : 17h56
(25)
(18, 30, 30, 32, 28, 27, 19)
(12, 1, 9, 11, 0)
(15, 7, 5, 16, 4,-29950)

Il est : 17h56
(35)
(30, 19, 28, 18, 29, 32, 31)
(13, 1, 12, 0, 0)
(17, -19704, 16,-29950, 4,256)

Il est : 17h56
(33)
(28, 31, 31, 30, 18, 30, 29)
(12, 0, 13, 2, 11)
(3, 4, -19704, 7, 16, 15)

and my code :

#define ELEMENTS(x)  (sizeof x / sizeof x[0])                                                                                                
#include <virtuabotixRTC.h>     
#include <Adafruit_NeoPixel.h>   

const int ledsPin = 12;
const int nombreLeds = 36;
const int intervalleTemps = 4000;
int counter = 0;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(nombreLeds, ledsPin);

int myH1[] = {21, 22, 23, 24, 25, 26, 33, 34, 35};
int myH2[] = {18, 19, 20, 27, 28, 29, 30, 31, 32};
int myM1[] = {0, 1, 2, 9, 10, 11, 12, 13, 14};
int myM2[] = {3, 4, 5, 6, 7, 8, 15, 16, 17};

int hours = 17;
int minutes = 56;

int firstHourDigit = hours / 10;      
int secondHourDigit = hours % 10;

int firstMinDigit = minutes / 10;
int secondMinDigit = minutes % 10;



// ON défini la couleur des leds par défaut (blanc)
int red = 255;
int green = 255;
int blue = 255;


void select() {
  int myValues[ELEMENTS(myH1)];
  memcpy (myValues, myH1, ELEMENTS(myH1) * sizeof(int));
  int count = ELEMENTS(myH1);

  int myValues2[ELEMENTS(myH2)];
  memcpy (myValues2, myH2, ELEMENTS(myH2) * sizeof(int));
  int count2 = ELEMENTS(myH2);

  int myValues3[ELEMENTS(myM1)];
  memcpy (myValues3, myM1, ELEMENTS(myM1) * sizeof(int));
  int count3 = ELEMENTS(myM1);

  int myValues4[ELEMENTS(myM2)];
  memcpy (myValues4, myM2, ELEMENTS(myM2) * sizeof(int));
  int count4 = ELEMENTS(myM2);

  Serial.print("(");
  for (int i = 0; i < firstHourDigit; i++) {
    int index = random(count);   // Random integer from 0 to count-1
    Serial.print(myValues[index]);
    // A TESTER !!
    //pixels.setPixelColor(myValues[index], red,green,blue);

    if (i == firstHourDigit - 1) // If last value
      Serial.println(")");
    else
      Serial.print(", ");
    myValues[index] = myValues[count - 1];
    count--;
  }

  Serial.print("(");
  for (int i = 0; i < secondHourDigit; i++) {
    int index = random(count2);   // Random integer from 0 to count-1
    Serial.print(myValues2[index]);

    if (i == secondHourDigit - 1) // If last value
      Serial.println(")");
    else
      Serial.print(", ");
    myValues2[index] = myValues2[count - 1];
    count--;
  }

  Serial.print("(");
  for (int i = 0; i < firstMinDigit; i++) {
    int index = random(count3);   // Random integer from 0 to count-1
    Serial.print(myValues3[index]);

    if (i == firstMinDigit - 1) // If last value
      Serial.println(")");
    else
      Serial.print(", ");
    myValues3[index] = myValues3[count - 1];
    count--;
  }

  Serial.print("(");
  for (int i = 0; i < secondMinDigit; i++) {
    int index = random(count4);   // Random integer from 0 to count-1
    
    Serial.print(myValues4[index]);

    if (i == secondMinDigit - 1) // If last value
      Serial.println(")");
    else
      Serial.print(", ");
    myValues4[index] = myValues4[count - 1];
    count--;
  }




  
}

void setup() {
  Serial.begin(9600);
  pixels.begin();
  pixels.setPixelColor(0, 0, 0, 0);
  pixels.show();
  pixels.setBrightness(90);

 randomSeed(analogRead(0));

}


void loop() {

Serial.print("Il est : "); Serial.print(hours);Serial.print("h");Serial.print(minutes);
Serial.print('\n');
select();
delay(intervalleTemps);
}

With those libraries and extra arrays you might have overwritten heap with stack.

Smart things to do would be putting the initial arrays into PROGMEM and allocating a single heap array that can hold the biggest of those then use it to hold the first array from PROGMEM to process and print that, then the next, and so on.

This

  memcpy (myValues, myH1, ELEMENTS(myH1) * sizeof(int));

should be

  memcpy (myValues, myH1, ELEMENTS(myH1));

However you are two suggestions behind ... you don't really need to use memcpy.

You need to make the variable count specific to each of the 4 arrays when you swap things around. Count was going out of range with count-- in each section.

myValues[index] = myValues[count - 1];
    count--;
myValues2[index] = myValues2[count2 - 1];
    count2--;
myValues3[index] = myValues3[count3 - 1];
    count3--;
myValues4[index] = myValues4[count4 - 1];
    count4--;

Anytime you find yourself repeating the same 30 lines of code 4 times, you should be thinking of code factoring.

Fisher-Yates using pointers:

int data[] = {22, 23, 24, 25, 26, 27, 28, 29, 30};


void setup() 
{
  Serial.begin(9600);
  Serial.println(F("enter a value between 1 and 9 inclusive..."));
}

void loop() 
{
  if (Serial.available())
  {
    randomSeed(millis() + micros());
    int newArraySize = Serial.parseInt();
    int* selection[newArraySize];
    int indexShuffle[sizeof(data) / sizeof(data[0])];
    for (int i = 0; i < (sizeof(data) / sizeof(data[0])); i++)
    {
      indexShuffle[i] = i;
    }
    for (int i = 0; i < newArraySize; i++)
    {
      int randomIndex = random(i, (sizeof(data) / sizeof(data[0])));
      int temp = indexShuffle[i];
      indexShuffle[i] = indexShuffle[randomIndex];      
      indexShuffle[randomIndex] = temp;
      selection[i] = &data[indexShuffle[i]];
    }
    for (auto i : selection)
      Serial.println(*i);
  }
}

Thanks ! It's ok :wink:

cattledog:
You need to make the variable count specific to each of the 4 arrays when you swap things around. Count was going out of range with count-- in each section.

myValues[index] = myValues[count - 1];

count--;
myValues2[index] = myValues2[count2 - 1];
    count2--;
myValues3[index] = myValues3[count3 - 1];
    count3--;
myValues4[index] = myValues4[count4 - 1];
    count4--;

I will actually try to optimize the code. Not easy for a beginner but thank you for the advice.

aarg:
Anytime you find yourself repeating the same 30 lines of code 4 times, you should be thinking of code factoring.

istepgueu:
I will actually try to optimize the code. Not easy for a beginner but thank you for the advice.

I admit that when using arrays it's not that easy.

Which as Bulldog did is a good time to introduce pointers.

GoForSmoke:
Which as Bulldog did is a good time to introduce pointers.

...and as I did... :slight_smile:

I arrived to the thread late and it looks like I missed 2 good posts.

Rather then duplicate the contents of 'select()' for each of the four global arrays you can add 'parameters' or 'arguments' to the function and use the same function for all of the arrays. When an array is passed to a function it arrives as a pointer to the first element of the array. THE FUNCTION CAN'T CALCULATE THE SIZE OF THE ARRAY FROM THE POINTER. The size has to be calculated on the original array and passed to the function.

I moved the calculation of firstHourDigit, etc down into loop() so the code would be easier to understand

#define ELEMENTS(x)  (sizeof x / sizeof x[0])
#include <virtuabotixRTC.h>
#include <Adafruit_NeoPixel.h>

const int ledsPin = 12;
const int nombreLeds = 36;
const int intervalleTemps = 4000;
int counter = 0;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(nombreLeds, ledsPin);

int myH1[] = {21, 22, 23, 24, 25, 26, 33, 34, 35};
int myH2[] = {18, 19, 20, 27, 28, 29, 30, 31, 32};
int myM1[] = {0, 1, 2, 9, 10, 11, 12, 13, 14};
int myM2[] = {3, 4, 5, 6, 7, 8, 15, 16, 17};

int hours = 17;
int minutes = 56;

// ON défini la couleur des leds par défaut (blanc)
int red = 255;
int green = 255;
int blue = 255;


void select(int array[], int arrayCount, int numberToSelect) {
  Serial.print("(");
  for (int i = 0; i < numberToSelect; i++) {
    int index = random(arrayCount);   // Random integer from 0 to count-1
    Serial.print(array[index]);
    // A TESTER !!
    //pixels.setPixelColor(array[index], red,green,blue);

    if (i == numberToSelect - 1) // If last value
      Serial.println(")");
    else
      Serial.print(", ");

    int temp = array[index];
    array[index] = array[arrayCount - 1];
    array[arrayCount - 1] = temp;

    arrayCount--;
  }
}

void setup() {
  Serial.begin(9600);

  pixels.begin();
  pixels.setPixelColor(0, 0, 0, 0);
  pixels.show();
  pixels.setBrightness(90);

  randomSeed(analogRead(0));
}


void loop() {
  Serial.print("Il est : "); Serial.print(hours); Serial.print("h"); Serial.print(minutes);
  Serial.print('\n');

  int firstHourDigit = hours / 10;
  int secondHourDigit = hours % 10;
  int firstMinDigit = minutes / 10;
  int secondMinDigit = minutes % 10;
  
  select(myH1, ELEMENTS(myH1), firstHourDigit);
  select(myH2, ELEMENTS(myH2), secondHourDigit);
  select(myM1, ELEMENTS(myM1), firstMinDigit);
  select(myM2, ELEMENTS(myM2), secondMinDigit);
  
  delay(intervalleTemps);
}