Trying to make a random array ... getting same numbers and shouldnt be

So since I dont know how to push or pop with an array, I am trying a VERY KLUDGE way of setting my 3 piece random array ... I have an enum called Inputs ( button, photo, toggle) and I want an array to randomly be set to these three items (all unique).

What I am TRYING to do below is call the random function and set the first element.

Then do a WHILE statement (while the new random number is equal to the first number...keep trying for a random number)

AND THEN one more time with a while that says while the new random number doesnt equal one OR TWO in the array ... keep going.

Sometimes it works (probably luckily) ... but I usually get 0,0,1 or 1,2,2 or something like that.

Help?

void setOrder() {
   randomSeed(analogRead(0));
   int randomNum = (inputs)random(3);
   answerOrder[0] = randomNum;
   
   randomNum = (inputs)random(3);
   while (randomNum == answerOrder[0])
   {
     randomNum = (inputs)random(3);
   }
   answerOrder[1] = randomNum;
   
   randomNum = (inputs)random(3);
   while (randomNum == answerOrder[0] && randomNum == answerOrder[1])
   {
     randomNum = (inputs)random(3);
   }
   answerOrder[2] = randomNum;   
   Serial.println(String(answerOrder[0]) + ',' + String(answerOrder[1]) + ',' + String(answerOrder[2]));
   Serial.println(pushOrder);
}
while (randomNum == answerOrder[0] && randomNum == answerOrder[1])

If you were testing this with an if to see if they were both NOT equal then you would use AND. But here you want to keep looking if randomNum equals either or. Since answerOrder[0] and answerOrder[1] aren't equal, then there can never be a case where randomNum equals both of them.

Change the && to ||.

OMGosh... :-[

Its too late... thank you very much.

Any ideas on a better/faster way of doing this?

Any ideas on a better/faster way of doing this?

If there really are only a few possibilities (0,1,2 or 0, 2, 1, or 1, 2, 0, or 1, 0, 2, or 2, 0, 1, or 2, 1, 0), you could just create an array that contains the index order to use, and choose which element of the array to use. One call to random... (Using bit math, you only need to store 6 byte values.)

The easiest way to get this done for ANY size array is to initialize the array with the values you want and then swap each element with one other element.

//
//    FILE: randomizeArray.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// PURPOSE: demo
//    DATE: 2014-dec-30
//     URL:
//
// Released to the public domain
//

int ar[500];

uint32_t start;
uint32_t stop;

void setup()
{
  Serial.begin(115200);
  Serial.println("Start ");

  // initialize
  for (int i = 0; i < 500; i++) ar[i] = i;

  start = micros();
  randomizeArray(500);
  stop = micros();
  Serial.println(stop - start);

  for (int i = 0; i < 500; i++)
  {
    Serial.print(i);
    Serial.print("\t");
    Serial.println(ar[i]);
  }
}

void loop()
{
}

void randomizeArray(int size)
{
  for (int i = 0; i < size; i++)
  {
    int n = random(size);
    int temp = ar[n];
    ar[n] = ar[i];
    ar[i] = temp;
  }
}

less than 50 milliseconds for an 500 element array.

You can change the strategy in randomizeArray to get another ‘randomness’, e.g. only swap even and odd indices. Increasing speed of this algorithm is left as an exercise (not trivial).

a slightly different version does swapping a bit more efficient, but it is not really faster.

void scrambleArray(int size)
{
  int last = 0;
  int temp = ar[last];
  for (int i = 0; i < size; i++)
  {
    int index = random(size);
    ar[last] = ar[index];
    last = index;
  }
  ar[last] = temp;
}

Note: 99% of the time is spend in the random() function. So replacing random with a faster “newRandom()” will speed up this algorithm far more.

PaulS: If there really are only a few possibilities (0,1,2 or 0, 2, 1, or 1, 2, 0, or 1, 0, 2, or 2, 0, 1, or 2, 1, 0), you could just create an array that contains the index order to use, and choose which element of the array to use. One call to random... (Using bit math, you only need to store 6 byte values.)

Or if you get really cute, just 3 byte values... :sunglasses:

Fantastic everyone!!! Thank you!

yaafm: Or if you get really cute, just 3 byte values... :sunglasses:

@yaafm can you show how?

played a bit with this performance “challenge”, the original randomizeArray took about 48092 (IDE1.5.8 win7)

this new version takes 34332 microsecond for 500. (~28% less time)
Trick is that it skips indices that are already swapped.

void randomizeArray(int size)
{
  for (int i = 0; i < size; i++)
  {
    int n = random(size);
    while (i < size && ar[i] != i) i++;
    int temp = ar[n];
    ar[n] = ar[i];
    ar[i] = temp;
  }
}

34292, another 40 micros gain (not much but still)

void randomizeArray(int size)
{
  for (int i = 0; i < size; i++)
  {
    while (i < size && ar[i] != i) i++;
    int n = random(size);
    int temp = ar[n];
    ar[n] = ar[i];
    ar[i] = temp;
  }
}

for loop reverse - 34096 - gain 196 micros

void randomizeArray(int size)
{
  int temp, n;
  for (int i = size - 1; i >= 0; i--)
  {
    while (i > 0 && ar[i] != i) i--;
    n = random(size);
    temp = ar[n];
    ar[n] = ar[i];
    ar[i] = temp;
  }
}

using George Marsaglia’s fast random generator - 10292!
(recall we came from 48092)

void randomizeArray(int size)
{
  for (int i = size - 1; i >= 0; i--)
  {
    while (i > 0 && ar[i] != i) i--;
    int n = ((uint16_t)getRandom()) % size; // 16 bit % is faster than 32 bit
    int temp = ar[n];
    ar[n] = ar[i];
    ar[i] = temp;
  }
}

unsigned long m_w = 1;
unsigned long m_z = 2;

unsigned long getRandom()
{
  m_z = 36969L * (m_z & 65535L) + (m_z >> 16);
  m_w = 18000L * (m_w & 65535L) + (m_w >> 16);
  return (m_z << 16) + m_w;  /* 32-bit result */
}

enough time gained :slight_smile:

Here is one that generate random numbers without duplicates. Im not sure of the speed tho.

int randNumber;
#define Count 10
int list[Count];
byte i = 0;
boolean found = false;

void setup()
{
  Serial.begin(115200);
  for (byte K = 0; K < 5; K++)
  {
    for (int j = 0; j < Count; j++)
      list[j] = -1;

    randomSeed(K + 1);
    delay(50);
    i = 0;
    found = false;
    while (i < Count)
    {
      randNumber = random(0, Count);
      for (byte L = 0; L < i; L++)
      {
        found = false;
        if (list[L] == randNumber)
        {
          found = true;
          break;
        }
      }
      if (!found)
      {
        list[i] = randNumber;
        Serial.print(list[i]);
        Serial.print(",");
        i++;
      }
    }
    Serial.println();
  }
}

void loop() {
}

You can take out the delay and serial prints to speed it up.

@hazardsmind, played with your sketch, works in practice with small array sizes, but when I used 500 for Count (and modded several bytes to int) one could see it becomes slower in the end. THis is caused by the fact that it has to look up every random number in the array. And if there are 500 in the array chances a new one is not in the array is one in 500....

robtillaart:
@yaafm
can you show how?

//the six possible triples as suggested by PaulS
//0,1,2 or 0, 2, 1, or 1, 2, 0, or 1, 0, 2, or 2, 0, 1, or 2, 1, 0

//Store only the first two numbers of each triple
//00 01
//00 10
//01 10
//01 00
//10 00
//10 01

//6 nibbles so 3 bytes
byte arr[3] = {B00010010, B01100100, B10001001};

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

  //extract our 6 triples
  for (byte rnd=0; rnd<6; rnd++)
  {
    //get the appropriate byte and then the nibble
    byte aByte = arr[rnd >> 1];
    byte nibble = rnd & 0x01 ? aByte & 0x0F : aByte >> 4;
    
    //get the 2 stored 2 bit numbers
    byte a = nibble >> 2;
    byte b = nibble & 0x03;
    
    //now deduce the third
    byte c = ((1 << a | 1 << b) ^ 0x07) >> 1;

    //print them out
    Serial.print(a, DEC);
    Serial.print(' ');
    Serial.print(b, DEC);
    Serial.print(' ');
    Serial.println(c, DEC);
  }
}

void loop(){}

Of course the weight of the decompression code far exceeds the data bytes saved. But then I said you COULD get it down to 3 bytes, not that you SHOULD.

Great trick,

however the code increase does not match the RAM decrease - your sketch 2288Flash 186 RAM

using one byte per combination - similar coding - sketch 2232Flash 188RAM

//the six possible triples as suggested by PaulS
//0,1,2 or 0, 2, 1, or 1, 2, 0, or 1, 0, 2, or 2, 0, 1, or 2, 1, 0

//Store only the first two numbers of each triple
//00 01
//00 10
//01 10
//01 00
//10 00
//10 01

//6 nibbles so 3 bytes
//byte arr[3] = {B00010010, B01100100, B10001001};
byte arr[6] = { B00000110, B00001001, B00010010, B00011000,B00100001, B00100100}; 

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

  //extract our 6 triples
  for (byte rnd=0; rnd<6; rnd++)
  {
    byte a = (arr[rnd] & B00110000) >> 4;
    byte b = (arr[rnd] & B00001100) >> 2;
    byte c = (arr[rnd] & B00000011);
//    //get the appropriate byte and then the nibble
//    byte aByte = arr[rnd >> 1];
//    byte nibble = rnd & 0x01 ? aByte & 0x0F : aByte >> 4;
//    
//    //get the 2 stored 2 bit numbers
//    byte a = nibble >> 2;
//    byte b = nibble & 0x03;
//    
//    //now deduce the third
//    byte c = ((1 << a | 1 << b) ^ 0x07) >> 1;

    //print them out
    Serial.print(a, DEC);
    Serial.print(' ');
    Serial.print(b, DEC);
    Serial.print(' ');
    Serial.println(c, DEC);
  }
}

void loop(){}

still nice trick!

robtillaart:
Great trick,

Cheers

robtillaart:
however the code increase does not match the RAM decrease - your sketch 2288Flash 186 RAM

using one byte per combination - similar coding - sketch 2232Flash 188RAM

Absolutely - but I did cop to that at the end of my previous post.

That said, when I tried it here, even bit-packing to 6 bytes is not cost effective compared to a plain vanilla 18 byte array. Not really surprising though; compression is rarely worth it on small data sets.

try this and see what you get.

byte arr[][3] = {0,1,2,0,2,1,1,2,0,1,0,2,2,0,1,2,1,0};

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

  for (byte rnd=0; rnd<6; rnd++)
  {
    byte a = arr[rnd][0];
    byte b = arr[rnd][1];
    byte c = arr[rnd][2];

    Serial.print(a, DEC);
    Serial.print(' ');
    Serial.print(b, DEC);
    Serial.print(' ');
    Serial.println(c, DEC);
  }
}

void loop(){}

Absolutely - but I did cop to that at the end of my previous post. Missed that at first sight, sorry

latest sketch results in: Sketch uses 2,230 bytes (6%) of program storage space. Maximum is 32,256 bytes. Global variables use 200 bytes (9%) of dynamic memory, leaving 1,848 bytes for local variables. Maximum is 2,048 bytes.

==> 2 bytes less FLASH and 12 more RAM ....

update: tested on IDE1.5.8 win7

hmmm - not the same numbers here. I guess it’s platform differences. Here the plain vanilla 18 byte array is the most economic at 2480 bytes. The 6 byte bit-packed array is next at 2502 and bringing up the rear, my 3 byte array at 2580.

Anyway - all good clean fun.