Features of the mql5 language, subtleties and tricks - page 136

 
Nikolai Semko:
So you're willing to light a $100 note to find a dime rolled under the bed?

May or may not be a coin

   srand(GetTickCount());
   uint v10 = 0, v19900 = 0;
   for (uint i = 0;  i < UINT_MAX;  ++ i) {
      int cur = rand();
      if (cur % 20000  == 10)
         ++ v10;
      else if (cur % 20000  == 19900)
         ++ v19900;   
   }
   Alert("10 probability = ", (double)v10/UINT_MAX*100, "%;  1990 probability = ", (double)v19900/UINT_MAX*100, "%");
   // Alert: 10 probability = 0.006103515626421085%;  19900 probability = 0.003051757813210543%
        

The probability of a ten is twice as high. And most importantly, the get_rand() costliness claim is sucked out of your finger, so why get random numbers through the back door with a shifted probability (while expecting a uniform distribution) when you can have a normal distribution? You're not saving a $100 note, you're saving matches.

 
Vict:

May or may not be a coin

The probability of a ten is twice as high. And most importantly, the get_rand() costliness claim is sucked out of your hand, so why get random numbers through the back door with a shifted probability (while expecting a uniform distribution) when you can have a normal distribution? You're not saving a $100 note, you're saving matches.

Yes, I was wrong about high slowness of your function. I misunderstood the algorithm. Sorry.
But still my algorithm is the fastest of all the proposed ones, despite the fact that it is more universal and not limited to 32767, like yours.
Code to prove it.

This script randomly generates an array of points with random colour and random coordinates. The size of the array is equal to the number of pixels on the chart chart chart. It is repeated 5 times

  1. with the regular rand() function
  2. with your get_rand() function
  3. using my function ulong randUlong()
  4. with my function uint randUint()
  5. using @AlexeyNavoykov' s ulong RandomLong() function
2019.06.0903:42:25.958 TestSpeedRand (EURGBP,H4)       Время формирования случайных массивов = 9894 микросекунд.  Всего сгенерировано 5203975 случайных чисел rand()
2019.06.0903:42:28.010 TestSpeedRand (EURGBP,H4)       Время формирования случайных массивов = 24899 микросекунд. Всего сгенерировано 5203975 случайных чисел get_rand()
2019.06.0903:42:30.057 TestSpeedRand (EURGBP,H4)       Время формирования случайных массивов = 22172 микросекунд. Всего сгенерировано 5203975 случайных чисел randUlong()
2019.06.0903:42:32.098 TestSpeedRand (EURGBP,H4)       Время формирования случайных массивов = 16013 микросекунд. Всего сгенерировано 5203975 случайных чисел randUint()
2019.06.0903:42:34.145 TestSpeedRand (EURGBP,H4)       Время формирования случайных массивов = 25524 микросекунд. Всего сгенерировано 5203975 случайных чисел RandomLong()

I picked up numbers in such a way to show the essence of the problem, when we apply rand()%20000


as it should be:


//+------------------------------------------------------------------+
uint get_rand(uint max)
  {
   staticbool f=false;
   if(!f) 
     {
      f=true;
      srand(GetTickCount());
     }
   uint limit=(max+1) *((32767+1)/(max+1));
   uint val;
   while((val=rand())>=limit);
   return val % (max+1);
  }
//+------------------------------------------------------------------+
ulong randUlong(ulong max=ULONG_MAX){return(((ulong)rand()<<60)|((ulong)rand()<<45)|((ulong)rand()<<30)|((ulong)rand()<<15)|(ulong)rand())%max;}
//+------------------------------------------------------------------+
uint randUint(uint max=UINT_MAX) {return(((uint)rand()<<30)|((uint)rand()<<15)|(uint)rand())%max;}
//+------------------------------------------------------------------+
ulong RandomLong(ulong range)
  {
#define _MAXRND(rang,rnd_range) ((rnd_range) -((rnd_range)-rang)%rang-1)
#define _RND ulong(rand())
   ulong rnd,max,const bit=1;
   if(range <= bit<<15) { if(!range) return0;  max=_MAXRND(range, 1<<15);  while((rnd=_RND) > max);  return rnd%range; }
   if(range <= bit<<30) { max=_MAXRND(range, bit<<30);  while((rnd=(_RND | _RND<<15)) > max);  return rnd%range; }
   if(range <= bit<<45) { max=_MAXRND(range, bit<<45);  while((rnd=(_RND | _RND<<15 | _RND<<30)) > max);  return rnd%range;  }
   if(range <= bit<<60) { max=_MAXRND(range, bit<<60);  while((rnd=(_RND | _RND<<15 | _RND<<30 | _RND<<45)) > max);  return rnd%range; }
   else  { max=_MAXRND(range,bit<<64);  while((rnd=(_RND|_RND<<15|_RND<<30|_RND<<45|_RND<<60))>max);  return rnd%range; }
#undef _RND
#undef _MAXRND
  }
//+------------------------------------------------------------------+
But 99.9% of the time, this function will work as well:
uint randUint(uint max) {return(((uint)rand()<<15)|(uint)rand())%max;}
it will work even faster.
This function will generate a random number from 0 to 1073741824. This number is even greater than the number of ticks for any instrument over the entire history. The "unfairness" of such a function would be microscopic for 99.9% of tasks.
Files:
 
Nikolai Semko:

But still my algorithm turns out to be the fastest of all proposed algorithms, despite the fact that it is more universal and not limited to 32767 like yours.
The code as a proof.

Thanks for the work, really interesting results. It turns out that rand() function is so fast that it works faster than arithmetic operations.

 
Alexey Navoykov:

Thanks for the effort, really interesting results. It turns out that rand() is so fast that it's faster than arithmetic operations.

No, it's not. About a nanosecond, just like extracting the square root from a double number. The +-*/ operations are performed in fractions of a nanosecond.
But just like the square root, rand() is performed in modern processors at the hardware level, not programmatically.

 
Nikolai Semko:

no, not faster. About a nanosecond, just like extracting the square root from a double number. The +-*/ operations are performed in fractions of a nanosecond.
But just like the square root, rand() is performed in modern processors at the hardware level, not programmatically.

Why no, it is not. Your version differs from mine in that rand() is always called 5 times while mine has an average of 1.64 times at 20 000 range and 1 time at 256. Altogether rand() is called 25 times for each iteration in your code while mine has 1.64*2+3 = 5.3 times. Of course, the situation is strange, we must find out what exactly the reason is. You have a lot of bitwise operations being performed there in addition...

 
Nikolai Semko:

1. Well we do realise that in your functions the problem is not solved but only masked, I won't lose weight, I'll just tighten my belt tighter.

2. In our and Alexey's versions it is the worst case scenario, while in many other situations the speed will almost be at the level of rand(), while you have constant time.

Have you ever wondered why rand() generates numbers in such a narrow range? This is a pseudo-random generator, so it is periodic, so generating a bunch of random numbers in places where it is not needed, with subsequent discarding of them, its quality is deteriorating (it will repeat earlier).

4. Some people mine random data in a more complicated way. I, for example, yank from the net, someone may even buy it. Why would I want to waste hard-won data in order to then dumbly discard it (generating ulong, writing the right algorithm is not our way)?

 
Alexey Navoykov:

Why not, if yes. Your version differs from mine in that it always calls 5 rand() while mine has an average of 1.64 times at 20 000 range and 1 time at 256 range. Altogether your rand() is called 25 times for each iteration, while mine has 1.64*2+3 = 5.3 times. Of course, this situation is strange, we have to find out what exactly the reason is. Because you have a lot of bitwise operations being performed there in addition...

bits are the cheapest operations. Almost free of charge.

But on the whole I agree. I don't understand why either... Maybe it's the wonders of optimization. Although what can be optimized there...

 
Nikolai Semko:

bits are the cheapest operations. Almost free of charge.

But on the whole I agree. I don't understand why either... Maybe it's the wonders of optimization. Although what there is to optimize...

It seems that it's not about arithmetic operations, because there are none, all values are computed at the compilation stage. The reason is the presence of a loop with an unknown number of iterations (although these iterations average less than two). So your code is somehow optimized due to the known number of rand() calls
 
Vict:

1. Well we do realise that in your functions the problem is not solved but only masked, I won't lose weight, I'll tighten my belt tighter.

2. In our and Alexey's variants, this is the worst case scenario, while in many other situations the speed will be almost on the rand() level, while you have constant time.

Have you ever wondered why rand() generates numbers in such a narrow range? This is a pseudo-random generator, so it's periodic, so generating a bunch of random numbers where it is not needed, with subsequent discarding, its quality is deteriorating (it will repeat earlier).

4. Some people mine random data in a more complicated way. I, for example, yank from the net, someone may even buy it. That's why should I waste hard-earned data in order to then stupidly discard it (generating ulong, writing a proper algorithm is not our way, after all)?

well that's nerdy.
To reproduce the situation, when this problem will be noticeable at least by 0.1%, you need to operate with ranges above the following values:

  • (ULONG_MAX)/1000= 18446744073709551 for the function randUlong()
  • (UINT_MAX)/1000= 4294967 for the function randUint()

Have you ever used such ranges? Then why did you put in these checks and loops?
The best is the enemy of the good.

Personally, myrandom generation ranges in practice are limited to 2000, 4000 at most. rand() works quite well for this purpose.
Insert such an option into my code:

ulong t=GetMicrosecondCount();
   for(int i=0;i<size;i++)
     {
      X[i]=ushort(rand()%W.Width);
      Y[i]=ushort(rand()%W.Width);
      clr[i]=XRGB(rand()%256,rand()%256,rand()%256);
     }
   t=GetMicrosecondCount()-t;
   Print("Время формирования случайных массивов = "+string(t)+" микросекунд. Всего сгенерировано "+string(size*5)+" случайных чисел rand()%W.Width");
   Canvas.Erase();
   for(int i=0;i<size;i++) Canvas.PixelSet(X[i],Y[i],clr[i]);
   Canvas.Update();
   Sleep(2000);

So you will not notice "unfairness" of rand() function (as it was with rand()%20000) and points will be situated visually evenly, so it is the fastest and the most efficient function.

It's not for nothing that processor developers limited rand() to 2^15=32768. They're not stupid people. This covers 99% of practical problems.
And for lovers of "extreme" ideas there is more than enough option:

ulong randUlong(ulong max=ULONG_MAX){return(((ulong)rand()<<60)|((ulong)rand()<<45)|((ulong)rand()<<30)|((ulong)rand()<<15)|(ulong)rand())%max;}
Files:
 
Nikolai Semko:

Personally, myrandom number generation ranges in practice are limited to 2000, max 4000. For this, rand() works quite well.
Insert such a variant into my code:

and you won't notice "unfairness" of rand() function (as it was with rand()%20000) and points will be visually uniform, so it is quite working and the fastest.

You're welcome to use it, I don't mind. All the more so when the right side of % is a multiple of RAND_MAX+1 (256 or 1024).

It's not for nothing that processor developers limited rand() to 2^15=32768. They're not stupid people. It covers 99% of practical tasks.
And there is more than enough variant for those who like "out-of-bounds" ideas:

What do processor developers have to do with it? The generator is software implemented. The only requirement is RAND_MAX >= 32767 and a period of at least 2^32. So the µl has a very sparse oscillator at "minimums".

And the most far-sighted will do themselves a fair rand() (if there is no multiplicity), this is even recommended in reference books.