Caratteristiche del linguaggio mql5, sottigliezze e tecniche - pagina 136

 
Nikolai Semko:
Quindi sei disposto ad accendere una banconota da 100 dollari per trovare una monetina arrotolata sotto il letto?

Può essere o non essere una moneta

   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%
        

La probabilità di un dieci è doppia. E soprattutto, l'affermazione di costosità di get_rand() viene succhiata dalla mano, quindi perché ottenere numeri casuali dalla porta di servizio con una probabilità spostata (mentre ci si aspetta una distribuzione uniforme) quando si può avere una distribuzione normale? Non stai salvando una banconota da 100 dollari, stai salvando dei fiammiferi.

 
Vict:

Può essere o non essere una moneta

La probabilità di un dieci è doppia. E soprattutto, l'affermazione di costosità di get_rand() viene succhiata dalla mano, quindi perché ottenere numeri casuali dalla porta di servizio con una probabilità spostata (mentre ci si aspetta una distribuzione uniforme) quando si può avere una distribuzione normale? Non stai salvando una banconota da 100 dollari, stai salvando dei fiammiferi.

Sì, mi sbagliavo sull'alta lentezza della vostra funzione. Ho frainteso l'algoritmo. Mi dispiace.
Ma ancora il mio algoritmo è il più veloce di tutti quelli proposti, nonostante il fatto che sia più universale e non limitato a 32767, come il tuo.
Codice per provarlo.

Questo script genera casualmente un array di punti con colore e coordinate casuali. La dimensione dell'array è uguale al numero di pixel del grafico. Si ripete 5 volte

  1. con la regolare funzione rand()
  2. con la funzione get_rand()
  3. usando la mia funzione ulong randUlong()
  4. con la mia funzione uint randUint()
  5. usando la funzione ulong RandomLong()di@AlexeyNavoykov
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()

Ho raccolto i numeri in modo tale da mostrare l'essenza del problema, quando applichiamo rand()%20000


come dovrebbe essere:


//+------------------------------------------------------------------+
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
  }
//+------------------------------------------------------------------+
Ma il 99,9% delle volte, questa funzione funzionerà ugualmente:
uint randUint(uint max) {return(((uint)rand()<<15)|(uint)rand())%max;}
funzionerà ancora più velocemente.
Questa funzione genererà un numero casuale da 0 a 1073741824. Questo numero è anche maggiore del numero di tick per qualsiasi strumento su tutta la storia. La "slealtà" di una tale funzione sarebbe microscopica per il 99,9% dei compiti.
File:
 
Nikolai Semko:

Ma ancora il mio algoritmo risulta essere il più veloce di tutti gli algoritmi proposti, nonostante il fatto che sia più universale e non limitato a 32767 come il tuo.
Il codice come prova.

Grazie per il lavoro, risultati davvero interessanti. Si scopre che la funzione rand() è così veloce che funziona più velocemente delle operazioni aritmetiche.

 
Alexey Navoykov:

Grazie per lo sforzo, risultati davvero interessanti. Si scopre che rand() è così veloce che è più veloce delle operazioni aritmetiche.

No, non lo è. Circa un nanosecondo, proprio come estrarre la radice quadrata da un numero doppio. Le operazioni +-*/ sono eseguite in frazioni di nanosecondo.
Ma proprio come la radice quadrata, rand() viene eseguita nei processori moderni a livello hardware, non programmaticamente.

 
Nikolai Semko:

No, non più veloce. Circa un nanosecondo, proprio come estrarre la radice quadrata da un numero doppio. Le operazioni +-*/ sono eseguite in frazioni di nanosecondo.
Ma proprio come la radice quadrata, rand() viene eseguita nei processori moderni a livello hardware, non programmaticamente.

La tua versione differisce dalla mia in quanto rand() è sempre chiamata 5 volte mentre la mia ha una media di 1,64 volte a 20 000 range e 1 volta a 256. Complessivamente rand() è chiamata 25 volte per ogni iterazione nel tuo codice mentre la mia ha 1,64*2+3 = 5,3 volte. Certo, la situazione è strana, dobbiamo scoprire qual è esattamente la ragione. Hai un sacco di operazioni bitwise che vengono eseguite lì in aggiunta...

 
Nikolai Semko:

1. Ci rendiamo conto che nelle vostre funzioni il problema non è risolto ma solo mascherato, non perderò peso, stringerò solo di più la cintura.

2. Nella nostra variante e in quella di Alexey è lo scenario peggiore, mentre in molte altre situazioni la velocità sarà quasi al livello di rand(), mentre si ha un tempo costante.

Vi siete mai chiesti perché rand() genera numeri in un intervallo così stretto? Questo è un generatore pseudo-casuale, quindi è periodico, quindi generando un mucchio di numeri casuali in posti dove non è necessario, con successivo scarto di essi, la sua qualità si sta deteriorando (si ripeterà prima).

4. Alcune persone estraggono dati casuali in modo più complicato. Io, per esempio, lo strappo dalla rete, qualcuno potrebbe anche comprarlo. Perché dovrei voler sprecare dati faticosamente conquistati per poi scartarli senza mezzi termini (generare ulong, scrivere un algoritmo adeguato non è la nostra strada, dopo tutto)?

 
Alexey Navoykov:

La tua versione differisce dalla mia in quanto chiama sempre 5 rand() mentre la mia ha una media di 1,64 volte a 20 000 range e 1 volta a 256 range. Complessivamente la tua rand() è chiamata 25 volte per ogni iterazione, mentre la mia ha 1,64*2+3 = 5,3 volte. Naturalmente, questa situazione è strana, dobbiamo scoprire qual è esattamente la ragione. Perché hai un sacco di operazioni bitwise che vengono eseguite lì in aggiunta...

I bit sono le operazioni più economiche. Quasi gratis.

Ma nel complesso sono d'accordo. Non capisco neanche io perché... Forse sono le meraviglie dell'ottimizzazione. Anche se ciò che può essere ottimizzato lì...

 
Nikolai Semko:

I bit sono le operazioni più economiche. Quasi gratis.

Ma nel complesso sono d'accordo. Non capisco neanche io perché... Forse sono le meraviglie dell'ottimizzazione. Anche se quello che c'è da ottimizzare...

Sembra che non si tratti di operazioni aritmetiche, perché non ce ne sono, tutti i valori sono calcolati in fase di compilazione. La ragione è la presenza di un ciclo con un numero sconosciuto di iterazioni (anche se queste iterazioni sono in media meno di due). Quindi il tuo codice è in qualche modo ottimizzato a causa del numero noto di chiamate rand()
 
Vict:

1. Ci rendiamo conto che nelle vostre funzioni il problema non è risolto ma solo mascherato, non perderò peso, stringerò solo di più la cintura.

2. Nella nostra versione e in quella di Alexey è lo scenario peggiore, mentre in molte altre situazioni la velocità sarà quasi al livello di rand(), mentre si ha un tempo costante.

Vi siete mai chiesti perché rand() genera numeri in un intervallo così stretto? Questo è un generatore pseudo-casuale, quindi è periodico, quindi generando un mucchio di numeri casuali in posti dove non è necessario, con successivo scarto di essi, la sua qualità si sta deteriorando (si ripeterà prima).

4. Alcune persone estraggono dati casuali in modo più complicato. Io, per esempio, lo strappo dalla rete, qualcuno potrebbe anche comprarlo. Ecco perché dovrei sprecare dati faticosamente guadagnati per poi scartarli stupidamente (generando ulong, scrivendo un algoritmo adeguato non è la nostra strada, dopo tutto)?

Beh, questo è da nerd.
Per riprodurre la situazione, quando questo problema sarà avvertibile almeno dello 0,1%, è necessario operare con intervalli superiori ai seguenti valori:

  • (ULONG_MAX)/1000= 18446744073709551 per la funzione randUlong()
  • (UINT_MAX)/1000= 4294967 per la funzione randUint()

Hai mai usato questi intervalli? Allora perché hai inserito questi controlli e cicli?
Il meglio è nemico del bene.

Personalmente, i miei intervalli digenerazione casuale in pratica sono limitati a 2000, 4000 al massimo. rand() funziona abbastanza bene per questo scopo.
Inserire una tale opzione nel mio codice:

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);

Così non noterete la "slealtà" della funzione rand() (come era con rand()%20000) e i punti saranno situati visivamente in modo uniforme, quindi è la funzione più veloce ed efficiente.

Non per niente gli sviluppatori del processore hanno limitato rand() a 2^15=32768. Non sono persone stupide. Questo copre il 99% dei problemi pratici.
E per gli amanti delle idee "estreme" c'è un'opzione più che sufficiente:

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

Personalmente, le mie gamme digenerazione di numeri casuali in pratica sono limitate a 2000, massimo 4000. Per questo, rand() funziona abbastanza bene.
Inserire una tale variante nel mio codice:

e non noterete la "slealtà" della funzione rand() (come era con rand()%20000) e i punti saranno visivamente uniformi, quindi è abbastanza funzionante e il più veloce.

Sei il benvenuto a usarlo, non mi dispiace. Tanto più quando il lato destro di % è un multiplo di RAND_MAX+1 (256 o 1024).

Non per niente gli sviluppatori del processore hanno limitato rand() a 2^15=32768. Non sono persone stupide. Copre il 99% dei compiti pratici.
E c'è una variante più che sufficiente per chi ama le idee "fuori dai limiti":

Cosa c'entrano gli sviluppatori di processori? Il generatore è implementato via software. L'unico requisito è RAND_MAX >= 32767 e un periodo di almeno 2^32. Quindi il µl ha un oscillatore molto rado ai "minimi".

E i più lungimiranti si faranno una giusta rand() (se non c'è molteplicità), questo è persino raccomandato nei libri di riferimento.

Motivazione: