Generate Unique Random Numbers from a range of values

I need to generate a unique random number (only once that number should be forced as output) from a range of values:

Say
I have a lower bound value 0
I have a upper bound value 9

So I need to generate 10 random numbers taking care that if one number is generated once then it should not be generated again.

random function cannot generate unique numbers - is there a way to achieve the same?

If you generate random numbers but do not want a repeat number then they are not random numbers.

So I need to generate 10 random numbers taking care that if one number is generated once then it should be generated again.

The simplest way to do this is to have an array with the numbers 0 to 9 in it. Then generate two random numbers in this range and swap the numbers in the array at those two positions. Do this about 200 times and you will have shuffled the pack so to speak.

So I need to generate 10 random numbers taking care that if one number is generated once then it should be generated again.

Is there a not missing from this sentence?

There is no function that generates a sequence of unique random numbers. You need to iterate through the loop populating the array, testing that each proposed value is unique.

Thanks for pointing the missing not ... changed the question acordingly

I'm not sure why you're doing what you're trying to do, but the following code snippet fills in an array with 10 random values between 0 and 9. The positions in the array tell you the sequence in which they were written:

int randomVals[10];

void setup() { 
  int temp;
  int passCounter = 0;
  int elementsFilled = 0;
  
  randomSeed(analogRead(0));   // Seed generator...

  Serial.begin(9600); 
   
  while (true) {
    temp = random(0, 10);
    if (randomVals[temp] == 0) {         // If this number has not been generated yet...
      randomVals[temp] = passCounter++;  // ...write its value here.
    }
    if (passCounter > 9) {               // If we've filled in all elements, leave...
      break;
    }
  }
  
  for (int i = 0; i < 10; i++)
    Serial.println(randomVals[i]);
}
void loop()
{
}

@econjack
your sketch can (theoretically) have a deadlock when e.g. number 5 does not appear in the random sequence for a long time.

@GrumpyMike
Your idea will work, however swapping 200 times is overkill as swapping 10 times with one parameter going from 0..n-1 will do

If you generate random numbers but do not want a repeat number then they are not random numbers.

Its called a random sequence.

my favourite way to solve this to have an array filled with nr 0.. n-1
loop
{
select one randomly from 0..arraysize-1
swap that one with biggest element
decrease the arraysize by 1
}

//
//    FILE: randomSequence.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// PURPOSE: generate random sequence (optimized)
//    DATE: 
//     URL:
//
// Released to the public domain
//

int randomVals[10];

void setup() 
{
  Serial.begin(115200);
  Serial.println("Start ");
  
  // fill array with values
  for (int i=0; i<10; i++)
  {
   randomVals[i] = i;
  }

  // fetch 10 random numbers
  for (int i=0; i<10; ++i)
  {
    int r = random(0, 9-i);    // select from a decreasing set
    Serial.print(randomVals[r]);
    Serial.print('\t');
    swap(r, 9-i);             // switch the chosen one with the last of the selection set.
  }
  Serial.println("\ndone");
}

void swap(int a, int b)
{
  int t = randomVals[a];
  randomVals[a] = randomVals[b];
  randomVals[b] = t;
}

void loop()
{
}

The advantage of this solution is that it minimizes the number of actions. Every time you draw one random number you call random() once and swap once. - guarantee, no doubles. Works also perfectly if you need e.g. 7 numbers out of 49.

A variation on Grumpy's idea, it minimizes the # swaps.

//
//    FILE: randomizeArray.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// PURPOSE: randomize an Array with in size steps
//    DATE: 2012-11-18
//     URL:
//
// Released to the public domain
//

int numbers[] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13 };
int count = 13;


void setup()
{
  Serial.begin(9600);
  
  scrambleArray(numbers, count);
  for (int i=0; i<count; i++)
  {
    Serial.println(numbers[i]);
  }
  Serial.println("----");
}

void loop()
{}

void scrambleArray(int * array, int size)
{
  int last = 0;
  int temp = array[last];
  for (int i=0; i<size; i++)
  {
    int index = random(size);
    array[last] = array[index];
    last = index;
  }
  array[last] = temp;
}
int numbers[] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13 };
int count = 13;

Why is count assigned 13 when there are 14 values in the array?

  1. to show that the trick can be applied to a partial array?
  2. to see if the reader reads the code?
  3. or just a typo (yes!)

thinking of (1) it would be more generic to have

void scrambleArray(int * array, int lower, int upper)
{
  int last = lower;
  int temp = array[last];
  for (int i = 0; i < (upper-lower); ++i)
  {
    int index = random(lower, upper);
    array[last] = array[index];
    last = index;
  }
  array[last] = temp;
}

which randomizes only a part of the array.

the method is called "random shuffle".
and since we're on c++, you can just use the std::random_shuffle() function from STL.

#include <cstdlib> // for std::srand()
#include <algorithm> // for std::random_shuffle()

char *numbers[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

void setup()
{
  std::srand(analogRead(A0)); // seed the random number generator
  std::random_shuffle(numbers, numbers + (sizeof(numbers) / sizeof(*numbers))); // shuffle the array.
}

this way you don't have to track the number of elements in a array, just give it a pair of [first, last) just like every other STL range.
you can also shuffle part of the array with something like this:

std::random_shuffle(numbers+2, numbers+8);

A problem with the examples are that the built in library random function will produce the exact same sequence of numbers, and using the analog 0 to seed the generator doesn't perform much better. I added a code snippet to the General section of the Arduino playground here; Arduino Playground - SketchList

Or you can access it directly here; The following example demonstrates how to produce the numbers 1 to X in a random order, where each number only appears once, and that all numbers are produced. · GitHub

/*
This sketch picks a random element out of an array (in this case an array of 60 consecutive numbers),
but never takes the same element until we got 'em all. Each time the loop starts over, we need to recreate the array.
There's one element more at the end (0) than we need.
ENJOY ! (and let me know if you find errors and/or improvements !)
*/
int f;
int i;
int n;
int p;
int x;
void setup()
{
randomSeed(analogRead(0));
Serial.begin(9600);
Serial.println();
Serial.println("This is the initial array. Lets start !");
}

void loop()
{
int a[61] = { 0, 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, 0
};

for (f = 0; f < 60; f++) // here we print the "new" array to pick from
{
Serial.print(a[f]);
Serial.print(" ");
}
Serial.println();

Serial.println();
for (n = 60; n > -1; n--) // here we pick random elements in the array until we got 'em all
{
Serial.println();
x = random(n);
p = a[x];
Serial.print("picked the ");
Serial.print(x);
Serial.print("th element out of the array above. So no more |");
//Serial.print("no more |");
Serial.print(p);
Serial.println("| in the array! New array =");
delay(500);

for (i = x; x < n; x++) // here we shift all elements (who come AFTER the chosen one) in the array one place to the left
{
a[x] = a[x + 1];
}

for (f = 0; f < 60; f++) // here we print the "new" array to pick from
{
Serial.print(a[f]);
Serial.print(" ");
delay(30);
}
Serial.println();
}
Serial.println();
Serial.print(" Start all over");
Serial.println();

delay(5000);
}

That's two dead threads you've revived with the same messy code. This particular thread is three years old.
Before you post any more code, read How to post code properly.

Pete

robtillaart:
@econjack
your sketch can (theoretically) have a deadlock when e.g. number 5 does not appear in the random sequence for a long time.

I have yet to have this algorithm end in a deadlock. Indeed, if you test the number of times that it generates a duplicate before filling the array, it's pretty small.

el_supremo:
That's two dead threads you've revived with the same messy code. This particular thread is three years old.
Before you post any more code, read How to post code properly.

Pete

I'm sooo soo sorry, Pete ... I "revived" this thread because there hasn't been a simple answer to the initial question. (please show me where a similar question has been replied with a solid good answer) If it is forbidden to reply to "old" threads, maybe they should be removed or (at least) locked. Maybe I'm not a super expert like you are, but this sketch works perfectly for me. Please tell me where the code is "MESSY" ... I'ts been running now for over 24 hrs (without the delays!) and it hasn't made a duplicate set yet...
so here's the code again :

/*
   This sketch picks a random element out of an array (in this case an array of 60 consecutive numbers),
   but never takes the same element until we got 'em all. Each time the loop starts over, we need to recreate the array.
   There's one element more at the end (0) than we need.
   ENJOY ! (and let me know if you find errors and/or improvements !)
*/
int f;
int i;
int n;
int p;
int x;
void setup()
{
  randomSeed(analogRead(0));
  Serial.begin(9600);
  Serial.println();
  Serial.println("This is the initial array.  Lets start !");
}

void loop()
{
  int a[61] = { 0,  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, 0
              };


  for (f = 0; f < 60; f++) // here we print the "new" array to pick from
  {
    Serial.print(a[f]);
    Serial.print(" ");
  }
  Serial.println();

  Serial.println();
  for (n = 60; n > -1; n--) // here we pick random elements in the array until we got 'em all
  {
    Serial.println();
    x = random(n);
    p = a[x];
    Serial.print("picked the ");
    Serial.print(x+1);
    Serial.print("th element out of the array above. So no more |");
    //Serial.print("no more |");
    Serial.print(p);
    Serial.println("| in the array!  New array =");
    delay(1000);

    for (i = x; x < n; x++) // here we shift all elements (who come AFTER the chosen one) in the array one place to the left
    {
      a[x] = a[x + 1];
    }

    for (f = 0; f < 60; f++) // here we print the "new" array to pick from
    {
      Serial.print(a[f]);
      Serial.print(" ");
      delay(10);
    }
    Serial.println();
  }
  Serial.println();
  Serial.print(" Start all over");
  Serial.println();

  delay(5000);
}
1 Like

Thank you PaulVdB I've been looking for this for a long time.

erubiel:
Thank you PaulVdB I've been looking for this for a long time.

I'm happy it is useful to you. Please inform "el_supremo" about your happiness and my (what he called "messy") code !

The array has 144 elements now. (I use it for programming a WS2812 LED string of 144 Led's)

as an extension here's the code I made if you need a new (scrambled) array.

/*
   This sketch randomizes the elements of an arry a[] and puts 'em in a new one b[].
   It will NEVER pick the same element twice !
   There's one element more at the end (0) than we need.
   ENJOY ! (and let me know if you find errors and/or improvements !)
*/
int f;
int i;
int n;
int p;
int x;
void setup()
{
  randomSeed(analogRead(0));
  Serial.begin(115200);
  Serial.println();
}

void loop()
{
  int a[145] = { 0  , 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,  0
              };
  int b[144] = {0  , 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
              };

  for (n = 144; n > -1; n--) // here we pick random elements in the array until we got 'em all
  {
    x = random(n);
    p = a[x];
    b[n - 1] = p;
    for (i = x; x < n; x++) // here we shift all elements (who come AFTER the chosen one) in the array one place to the left
    {
      a[x] = a[x + 1];
    }
  }

  for (f = 0; f < 144; f++)
  {
    Serial.print(b[f]);
    Serial.print(" ");
    // delay(5);
  }
  Serial.println();
  //delay(20);
}