Características del lenguaje mql5, sutilezas y técnicas - página 136

 
Nikolai Semko:
¿Así que estás dispuesto a encender un billete de 100 dólares para encontrar una moneda de diez centavos enrollada bajo la cama?

Puede o no ser una moneda

   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 probabilidad de un diez es el doble. Y lo más importante, la pretensión de rentabilidad de get_rand() se la chupa el dedo, así que ¿por qué obtener números aleatorios por la puerta de atrás con una probabilidad desplazada (esperando una distribución uniforme) cuando se puede tener una distribución normal? No estás salvando un billete de 100 dólares, estás salvando fósforos.

 
Vict:

Puede o no ser una moneda

La probabilidad de un diez es el doble. Y lo que es más importante, el reclamo de la rentabilidad de get_rand() se chupa de la mano, así que ¿por qué obtener números aleatorios por la puerta de atrás con una probabilidad desplazada (mientras se espera una distribución uniforme) cuando se puede tener una distribución normal? No estás salvando un billete de 100 dólares, estás salvando fósforos.

Sí, me equivoqué sobre la alta lentitud de su función. He entendido mal el algoritmo. Lo siento.
Pero aun así mi algoritmo es el más rápido de todos los propuestos, a pesar de que es más universal y no está limitado a 32767, como el tuyo.
Código para probarlo.

Este script genera aleatoriamente una matriz de puntos con color y coordenadas aleatorias. El tamaño de la matriz es igual al número de píxeles del gráfico. Se repite 5 veces

  1. con la función regular rand()
  2. con su función get_rand()
  3. usando mi función ulong randUlong()
  4. con mi función uint randUint()
  5. utilizando la función ulong RandomLong( )de@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()

He escogido los números de tal manera que se vea la esencia del problema, cuando aplicamos rand()%20000


como debería ser:


//+------------------------------------------------------------------+
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
  }
//+------------------------------------------------------------------+
Pero el 99,9% de las veces, esta función también funcionará:
uint randUint(uint max) {return(((uint)rand()<<15)|(uint)rand())%max;}
funcionará aún más rápido.
Esta función generará un número aleatorio de 0 a 1073741824. Este número es incluso mayor que el número de ticks de cualquier instrumento en todo el historial. La "injusticia" de tal función sería microscópica para el 99,9% de las tareas.
Archivos adjuntos:
 
Nikolai Semko:

Pero aun así mi algoritmo resulta ser el más rápido de todos los propuestos, a pesar de ser más universal y no estar limitado a 32767 como el tuyo.
El código como prueba.

Gracias por el trabajo, resultados realmente interesantes. Resulta que la función rand() es tan rápida que funciona más rápido que las operaciones aritméticas.

 
Alexey Navoykov:

Gracias por el esfuerzo, resultados realmente interesantes. Resulta que rand() es tan rápido que es más rápido que las operaciones aritméticas.

No, no lo es. Alrededor de un nanosegundo, como extraer la raíz cuadrada de un número doble. Las operaciones +-*/ se realizan en fracciones de nanosegundo.
Pero al igual que la raíz cuadrada, rand() se realiza en los procesadores modernos a nivel de hardware, no mediante programación.

 
Nikolai Semko:

No, no más rápido. Alrededor de un nanosegundo, como extraer la raíz cuadrada de un número doble. Las operaciones +-*/ se realizan en fracciones de nanosegundo.
Pero al igual que la raíz cuadrada, rand() se realiza en los procesadores modernos a nivel de hardware, no mediante programación.

Por qué no, no lo es. Tu versión difiere de la mía en que rand() se llama siempre 5 veces mientras que la mía tiene una media de 1,64 veces en 20 000 de rango y 1 vez en 256. En total rand() se llama 25 veces por cada iteración en tu código mientras que el mío tiene 1,64*2+3 = 5,3 veces. Por supuesto, la situación es extraña, hay que averiguar cuál es la razón exacta. Tienes un montón de operaciones de bit a bit que se realizan allí además...

 
Nikolai Semko:

1. Bueno nos damos cuenta de que en sus funciones el problema no se resuelve sino que sólo se enmascara, no voy a perder peso, sólo me apretaré más el cinturón.

2. En nuestras variantes y en las de Alexey, este es el peor escenario, mientras que en muchas otras situaciones la velocidad estará casi al nivel de rand(), mientras que usted tiene un tiempo constante.

¿Te has preguntado alguna vez por qué rand() genera números en un rango tan estrecho? Se trata de un generador pseudoaleatorio, por lo que es periódico, por lo que al generar un montón de números aleatorios en lugares donde no es necesario, con posterior descarte de los mismos, su calidad se va deteriorando (se repetirá antes).

4. Algunas personas extraen datos aleatorios de forma más complicada. Yo, por ejemplo, arranco de la red, alguien puede incluso comprarlo. ¿Por qué querría malgastar los datos que tanto me ha costado conseguir para luego descartarlos tontamente (generando ulong, escribiendo el algoritmo correcto no es nuestro camino)?

 
Alexey Navoykov:

Por qué no, si sí. Tu versión difiere de la mía en que siempre llama 5 veces a rand() mientras que la mía lo hace una media de 1,64 veces en el rango de 20 000 y 1 vez en el rango de 256. En total tu rand() se llama 25 veces en cada iteración, mientras que la mía lo hace 1,64*2+3 = 5,3 veces. Por supuesto, esta situación es extraña, tenemos que averiguar cuál es la razón exacta. Porque tienes un montón de operaciones bitwise que se realizan allí además...

Los bits son las operaciones más baratas. Casi gratis.

Pero en general estoy de acuerdo. Yo tampoco entiendo por qué... Quizá sean las maravillas de la optimización. Aunque lo que se puede optimizar allí...

 
Nikolai Semko:

Los bits son las operaciones más baratas. Casi gratis.

Pero en general estoy de acuerdo. Yo tampoco entiendo por qué... Quizá sean las maravillas de la optimización. Aunque lo que hay que optimizar...

Parece que no se trata de operaciones aritméticas, porque no las hay, todos los valores se calculan en la fase de compilación. La razón es la presencia de un bucle con un número desconocido de iteraciones (aunque estas iteraciones son en promedio inferiores a dos). Así que su código está de alguna manera optimizado debido al número conocido de llamadas a rand()
 
Vict:

1. Bueno nos damos cuenta de que en sus funciones el problema no se resuelve sino que sólo se enmascara, no voy a perder peso, sólo me apretaré más el cinturón.

2. En nuestra versión y en la de Alexey es el peor de los casos, mientras que en muchas otras situaciones la velocidad estará casi al nivel de rand(), mientras que usted tiene un tiempo constante.

¿Te has preguntado alguna vez por qué rand() genera números en un rango tan estrecho? Se trata de un generador pseudoaleatorio, por lo que es periódico, por lo que al generar un montón de números aleatorios donde no es necesario, con posterior descarte, su calidad se va deteriorando (se repetirá antes).

4. Algunas personas extraen datos aleatorios de forma más complicada. Yo, por ejemplo, arranco de la red, alguien puede incluso comprarlo. ¿Por qué debería malgastar los datos ganados con tanto esfuerzo para luego descartarlos estúpidamente (generando ulong, escribiendo un algoritmo adecuado no es nuestro camino, después de todo)?

Bueno, eso es una tontería.
Para reproducir la situación, cuando este problema se notará al menos en un 0,1%, es necesario operar con rangos superiores a los siguientes valores:

  • (ULONG_MAX)/1000= 18446744073709551 para la función randUlong()
  • (UINT_MAX)/1000= 4294967 para la función randUint()

¿Has utilizado alguna vez estos rangos? ¿Entonces por qué has puesto estas comprobaciones y bucles?
Lo mejor es enemigo de lo bueno.

Personalmente, mis rangos degeneración aleatoria en la práctica se limitan a 2000, 4000 como máximo. rand() funciona bastante bien para este propósito.
Insertar dicha opción en mi código:

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

Así, no se notará la "injusticia" de la función rand() (como ocurría con rand()%20000) y los puntos se situarán visualmente de forma uniforme, por lo que es la función más rápida y eficiente.

No en vano los desarrolladores de procesadores limitaron rand() a 2^15=32768. No son gente estúpida. Eso cubre el 99% de los problemas prácticos.
Y para los amantes de las ideas "extremas" hay opciones más que suficientes:

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

Personalmente, mis rangos degeneración de números aleatorios en la práctica se limitan a 2000, máximo 4000. Para ello, rand() funciona bastante bien.
Insertar dicha variante en mi código:

y no se notará la "falta de equidad" de la función rand() (como ocurría con rand()%20000) y los puntos serán visualmente uniformes, por lo que funciona bastante bien y es la más rápida.

Eres bienvenido a usarlo, no me importa. Más aún cuando el lado derecho de % es un múltiplo de RAND_MAX+1 (256 o 1024).

No en vano los desarrolladores de procesadores limitaron rand() a 2^15=32768. No son gente estúpida. Cubre el 99% de las tareas prácticas.
Y hay variantes más que suficientes para los que les gustan las ideas "fuera de los límites":

¿Qué tienen que ver los desarrolladores de procesadores? El generador está implementado por software. El único requisito es RAND_MAX >= 32767 y un periodo de al menos 2^32. Así que el µl tiene un oscilador muy disperso en los "mínimos".

Y los más previsores se harán un rand() justo (si no hay multiplicidad), esto se recomienda incluso en los libros de referencia.

Razón de la queja: