Select randomly X numbers in an array

Hello everyone.

I try to select X numbers randomly in an array but I do not see how to do it.

Example: retrieve 6 of the 9 digits below randomly.

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

Can someone help me ?

Thank you very much.

Normally you will select an element of an array by its index.

// select 4th element of array
int index = 3;
int selectedValue = myH1[index];

If you now assign a random value to index, you can achieve what you need.

int index = random(sizeof(myH1) / sizeof(myH1[0]));
int selectedValue = myH1[index];
Serial.println(selectedValue);

The construction with sizeof allows you to resize the array without having to update all your code; it will limit the result of random to the number of elements in the array.

Note:
not compiled, I'll leave debugging up to you :wink:

Thanks.

Your code work but I think I was not clear enough in my explanation.

On each loop, if

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

I would like to recover (by exemple)

Random1 = 25;
Random2 = 33;
Random3 = 35;
Random4 = 21;
Random5 = 22;
Random6 = 26

OR if

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

I would like to recover (by exemple)

Random1 = 35;
Random2 = 22;

So from your array, you need to retrieve all numbers in a random sequence?

The problem is to randomly pick a selection, not a permutation? (ie the order of results isn't
important, and they do not include the same entries twice).

This is equivalent to choose a bit pattern with N random 1's, and then masking the array with that.

One fair way is to pick an element at random, remove it from the array, and repeat N times.
However that involves changing the array.

Another way is to keep selecting indices of the array at random until you have N unique ones,
but that could take a long time if you are selecting 99 from 100. This can be improved by
inverting the process if the N is more than half the size of the array - select element indicies
for the ones not to choose.

If the desire is to select a permutation then you have to take account of the order indicies are
chosen (and that last optimization can't be used).

"One fair way is to pick an element at random, remove it from the array, and repeat N times.
However that involves changing the array."

This is exactly what I would like to do. :slight_smile:

https://www.arduino.cc/en/Reference/Random
https://www.arduino.cc/en/Reference/RandomSeed

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

int Output[number];

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

Thanks johnwasser !

Can you tel me how can I recover the results?

(Random1 = xx;Random2 = xx;Random3 = xx;Random4 = xx;Random5 = xx;Random6 = xx)

My actual code is

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


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

      Serial.print(myH1[index]);
        
    }
}


void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:


}

johnwasser:
random() - Arduino Reference
https://www.arduino.cc/en/Reference/RandomSeed

const int number = 6;

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

int Output[number];

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

Don't use individually named variables Random1, Random2 etc., instead create a new array to hold the results, like randomNumbers[]. Use count to index it and you can assign the values using Wasser's code.

Sure:

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

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(", ")
        myH1[index] = myH1[count-1];
        count--;        
    }
}


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

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

Thank Johnwasser !

It works fine except when I loop.
After 6 loops, I find myself with the same number.

(Random1 = 25, Random2 = 22, Random3 = 23, Random4 = 33, Random5 = 21, Random6 = 35)
(Random1 = 33, Random2 = 35, Random3 = 34, Random4 = 34, Random5 = 24, Random6 = 26)
(Random1 = 34, Random2 = 26, Random3 = 35, Random4 = 26, Random5 = 34, Random6 = 24)
(Random1 = 34, Random2 = 35, Random3 = 35, Random4 = 24, Random5 = 35, Random6 = 26)
(Random1 = 35, Random2 = 26, Random3 = 35, Random4 = 35, Random5 = 24, Random6 = 24)
(Random1 = 35, Random2 = 35, Random3 = 35, Random4 = 35, Random5 = 35, Random6 = 35)

Here is my code :

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(", ");
        myH1[index] = myH1[count-1];
        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);
  
}

you need to learn about pseudo random numbers and the use of randomSeed()

Remove each picked number from the array and reduce the # of elements to pick from each time.

When you remove an element that is not last, move higher elements down using memmove() or a loop.

BulldogLowell:
you need to learn about pseudo random numbers and the use of randomSeed()

Perhaps, but it is not the problem:

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

const int intervalleTemps = 4000;

void select() {
  int myValues[9];
  memcpy (myValues, myH1, 9*sizeof(int));
  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(myValues[index]);

    if (i == number - 1) // If last value
      Serial.println(")");
    else
      Serial.print(", ");
    myValues[index] = myValues[count - 1];
    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);

}

Wasser's code modifies the array. You need to operate on a copy to use that method if you plan to repeat the operation.

GoForSmoke:
Remove each picked number from the array and reduce the # of elements to pick from each time.

When you remove an element that is not last, move higher elements down using memmove() or a loop.

If the numbers are always positive, you could mark the elements picked with -1 and avoid moving them. You'd need to experiment to see if that would save either time or memory.

@aarg: why not just use a macro:

#define ELEMENTS(x)  (sizeof x / sizeof x[0])

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

const int intervalleTemps = 4000;

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

  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(myValues[index]);

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

// The rest is the same...

econjack:
@anon57585045: why not just use a macro:

Laziness and haste. :slight_smile: Point taken.

It's more efficient to use bytes anyway:

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

const int intervalleTemps = 4000;

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

    if (i == numberOfRands - 1) // If last value
      Serial.println(")");
    else
      Serial.print(", ");
    myValues[index] = myValues[count - 1];
    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);

}

Edit - in this case... these look suspiciously like Mega pin numbers. :slight_smile:

If the numbers are always positive, you could mark the elements picked with -1 and avoid moving them. You'd need to experiment to see if that would save either time or memory.

I thought of that but removing picked choices means that every pseudorandom will hit a new value. The selection should go faster when not generating more randoms.

Consider a list with 12 elements after 9 have been picked. A random pick will hit a chosen number 3 out of 4 times.

johnwasser:
random() - Arduino Reference
https://www.arduino.cc/en/Reference/RandomSeed

const int number = 6;

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

int Output[number];

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

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