Caractéristiques du langage mql5, subtilités et techniques - page 136

 
Nikolai Semko:
Vous êtes donc prêt à allumer un billet de 100 dollars pour trouver une pièce de 10 cents roulée sous le lit ?

Peut ou non être une pièce de monnaie

   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é d'un dix est deux fois plus élevée. Et surtout, la prétention à la rentabilité de get_rand() vous est retirée, alors pourquoi obtenir des nombres aléatoires par la petite porte avec une probabilité décalée (tout en s'attendant à une distribution uniforme) quand vous pouvez avoir une distribution normale ? Vous ne sauvez pas un billet de 100 dollars, vous sauvez des allumettes.

 
Vict:

Peut ou non être une pièce de monnaie

La probabilité d'un dix est deux fois plus élevée. Et surtout, la prétention à la rentabilité de get_rand() vous est retirée, alors pourquoi obtenir des nombres aléatoires par la petite porte avec une probabilité décalée (tout en s'attendant à une distribution uniforme) quand vous pouvez avoir une distribution normale ? Vous ne sauvez pas un billet de 100 dollars, vous sauvez des allumettes.

Oui, je me suis trompé sur la grande lenteur de votre fonction. J'ai mal compris l'algorithme. Désolé.
Mais mon algorithme reste le plus rapide de tous ceux proposés, malgré le fait qu'il soit plus universel et non limité à 32767, comme le vôtre.
Le code pour le prouver.

Ce script génère de manière aléatoire un tableau de points avec une couleur et des coordonnées aléatoires. La taille du tableau est égale au nombre de pixels sur le graphique de la carte. Il est répété 5 fois

  1. avec la fonction régulière rand()
  2. avec votre fonction get_rand()
  3. en utilisant ma fonction ulong randUlong()
  4. avec ma fonction uint randUint()
  5. en utilisantla fonction 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()

J'ai choisi les chiffres de manière à montrer l'essence du problème, lorsque nous appliquons rand()%20000.


comme il se doit :


//+------------------------------------------------------------------+
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
  }
//+------------------------------------------------------------------+
Mais dans 99,9 % des cas, cette fonction fonctionnera également :
uint randUint(uint max) {return(((uint)rand()<<15)|(uint)rand())%max;}
il fonctionnera encore plus vite.
Cette fonction génère un nombre aléatoire compris entre 0 et 1073741824, supérieur au nombre de ticks de chaque instrument sur l'ensemble de l'historique. L'"injustice" d'une telle fonction serait microscopique pour 99,9% des tâches.
Dossiers :
 
Nikolai Semko:

Pourtant, mon algorithme s'avère être le plus rapide de tous les algorithmes proposés, malgré le fait qu'il soit plus universel et non limité à 32767 comme le vôtre.
Le code comme preuve.

Merci pour ce travail, les résultats sont vraiment intéressants. Il s'avère que la fonction rand() est si rapide qu'elle fonctionne plus vite que les opérations arithmétiques.

 
Alexey Navoykov:

Merci pour cet effort, les résultats sont vraiment intéressants. Il s'avère que rand() est si rapide qu'il est plus rapide que les opérations arithmétiques.

Non, ça ne l'est pas. Environ une nanoseconde, tout comme l'extraction de la racine carrée d'un nombre double. Les opérations +-*/ sont effectuées en fractions de nanoseconde.
Mais tout comme la racine carrée, la fonction rand() est exécutée dans les processeurs modernes au niveau matériel, et non par programmation.

 
Nikolai Semko:

Non, pas plus vite. Environ une nanoseconde, comme pour extraire la racine carrée d'un nombre double. Les opérations +-*/ sont effectuées en fractions de nanoseconde.
Mais tout comme la racine carrée, la fonction rand() est exécutée dans les processeurs modernes au niveau matériel, et non par programmation.

Votre version diffère de la mienne en ce que rand() est toujours appelé 5 fois alors que la mienne est appelée en moyenne 1,64 fois à 20 000 et 1 fois à 256. Au total, rand() est appelé 25 fois pour chaque itération dans votre code alors que la mienne est appelée 1,64*2+3 = 5,3 fois. Bien sûr, la situation est étrange, nous devons trouver la raison exacte. Vous avez beaucoup d'opérations de type bitwise qui sont exécutées ici en plus...

 
Nikolai Semko:

1. Nous nous rendons bien compte que dans vos fonctions, le problème n'est pas résolu mais seulement masqué, je ne vais pas perdre de poids, je vais juste me serrer la ceinture plus fort.

2. Dans notre variante et celle d'Alexey, c'est le pire scénario, alors que dans de nombreuses autres situations, la vitesse sera presque au niveau de rand(), alors que vous avez un temps constant.

Vous êtes-vous déjà demandé pourquoi rand() génère des nombres dans une plage aussi étroite ? Il s'agit d'un générateur pseudo-aléatoire, il est donc périodique. En générant un tas de nombres aléatoires à des endroits où il n'est pas nécessaire, puis en les rejetant, sa qualité se détériore (il se répétera plus tôt).

4. Certaines personnes exploitent des données aléatoires d'une manière plus compliquée. Si, par exemple, j'arrache des informations sur le net, quelqu'un peut même les acheter. Pourquoi voudrais-je gaspiller des données durement acquises pour ensuite les rejeter sans ménagement (générer des ulong, écrire un algorithme correct n'est pas notre façon de faire, après tout) ?

 
Alexey Navoykov:

Pourquoi pas, si oui. Votre version diffère de la mienne en ce qu'elle appelle toujours 5 rand() alors que la mienne a une moyenne de 1,64 fois à l'échelle 20 000 et 1 fois à l'échelle 256. Au total, votre rand() est appelé 25 fois pour chaque itération, alors que la mienne a 1,64*2+3 = 5,3 fois. Bien sûr, cette situation est étrange, nous devons trouver quelle est la raison exacte. Parce que vous avez beaucoup d'opérations de type bitwise qui sont exécutées ici en plus...

Les bits sont les opérations les moins chères. Presque gratuitement.

Mais dans l'ensemble, je suis d'accord. Je ne comprends pas non plus pourquoi... C'est peut-être les merveilles de l'optimisation. Bien que ce qui peut y être optimisé...

 
Nikolai Semko:

Les bits sont les opérations les moins chères. Presque gratuitement.

Mais dans l'ensemble, je suis d'accord. Je ne comprends pas non plus pourquoi... C'est peut-être les merveilles de l'optimisation. Bien que ce qu'il y a à optimiser...

Il semble qu'il ne s'agisse pas d'opérations arithmétiques, car il n'y en a pas, toutes les valeurs sont calculées à la compilation. La raison est la présence d'une boucle avec un nombre inconnu d'itérations (bien que ces itérations soient en moyenne inférieures à deux). Votre code est donc en quelque sorte optimisé grâce au nombre connu d'appels à rand().
 
Vict:

1. Nous nous rendons compte que dans vos fonctions, le problème n'est pas résolu mais seulement masqué, je ne perdrai pas de poids, je me serrerai plus fort la ceinture.

2. Dans nos variantes et celles d'Alexey, il s'agit du pire scénario, alors que dans de nombreuses autres situations, la vitesse sera presque au niveau de rand(), alors que vous avez un temps constant.

Vous êtes-vous déjà demandé pourquoi rand() génère des nombres dans une plage aussi étroite ? Il s'agit d'un générateur pseudo-aléatoire, il est donc périodique. En générant un tas de nombres aléatoires à des endroits où il n'est pas nécessaire, puis en les rejetant, sa qualité se détériore (il se répétera plus tôt).

4. Certaines personnes exploitent des données aléatoires d'une manière plus compliquée. Si, par exemple, j'arrache des informations sur le net, quelqu'un peut même les acheter. Pourquoi devrais-je gaspiller des données durement acquises pour ensuite les rejeter stupidement (générer des ulong, écrire un algorithme correct n'est pas notre façon de faire, après tout) ?

Eh bien, c'est ringard.
Pour reproduire la situation dans laquelle ce problème sera perceptible au moins à 0,1%, vous devez opérer avec des gammes supérieures aux valeurs suivantes :

  • (ULONG_MAX)/1000= 18446744073709551 pour la fonction randUlong()
  • (UINT_MAX)/1000= 4294967 pour la fonction randUint()

Avez-vous déjà utilisé de telles fourchettes ? Alors, pourquoi avez-vous mis en place ces contrôles et ces boucles ?
Le mieux est l'ennemi du bien.

Personnellement, mes plages degénération aléatoire dans la pratique se limitent à 2000, 4000 au maximum. rand() fonctionne très bien à cet effet.
Insérer une telle option dans mon 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);

Ainsi, vous ne remarquerez pas l'"injustice" de la fonction rand() (comme c'était le cas avec rand()%20000) et les points seront situés visuellement de manière égale, c'est donc la fonction la plus rapide et la plus efficace.

Ce n'est pas pour rien que les développeurs de processeurs ont limité rand() à 2^15=32768. Ce ne sont pas des gens stupides. Cela couvre 99% des problèmes pratiques.
Et pour les amateurs d'idées "extrêmes", il y a plus qu'assez d'options :

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

Personnellement, mes plages degénération de nombres aléatoires en pratique sont limitées à 2000, 4000 maximum. Pour cela, rand() fonctionne très bien.
Insérer une telle variante dans mon code :

et vous ne remarquerez pas "l'injustice" de la fonction rand() (comme c'était le cas avec rand()%20000) et les points seront visuellement uniformes, donc c'est tout à fait fonctionnel et le plus rapide.

Tu es le bienvenu pour l'utiliser, ça ne me dérange pas. D'autant plus lorsque le côté droit de % est un multiple de RAND_MAX+1 (256 ou 1024).

Ce n'est pas pour rien que les développeurs de processeurs ont limité rand() à 2^15=32768. Ce ne sont pas des gens stupides. Il couvre 99% des tâches pratiques.
Et il y a plus qu'assez de variantes pour ceux qui aiment les idées "hors limites" :

Qu'est-ce que les développeurs de processeurs ont à voir là-dedans ? Le générateur est implémenté par logiciel. La seule exigence est RAND_MAX >= 32767 et une période d'au moins 2^32. Le µl a donc un oscillateur très peu dense aux "minimums".

Et les plus prévoyants se feront un rand() équitable (s'il n'y a pas de multiplicité), c'est même recommandé dans les livres de référence.