Características da linguagem mql5, subtilezas e técnicas - página 136

 
Nikolai Semko:
Então está disposto a acender uma nota de 100 dólares para encontrar um centavo enrolado debaixo da cama?

Pode ou não ser uma moeda

   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%
        

A probabilidade de um dez é duas vezes mais elevada. E o mais importante, o get_rand() costliness claim é sugado da sua mão, então porquê obter números aleatórios através da porta traseira com uma probabilidade deslocada (enquanto espera uma distribuição uniforme) quando pode ter uma distribuição normal? Não está a guardar uma nota de 100 dólares, está a guardar fósforos.

 
Vict:

Pode ou não ser uma moeda

A probabilidade de um dez é duas vezes mais elevada. E o mais importante, o get_rand() costliness claim é sugado da sua mão, então porquê obter números aleatórios através da porta traseira com uma probabilidade deslocada (enquanto espera uma distribuição uniforme) quando pode ter uma distribuição normal? Não está a guardar uma nota de 100 dólares, está a guardar fósforos.

Sim, eu estava enganado quanto à grande lentidão da sua função. Interpretei mal o algoritmo. Desculpe.
Mas ainda assim o meu algoritmo é o mais rápido de todos os propostos, apesar de ser mais universal e não limitado a 32767, como o seu.
Código para o provar.

Este guião gera aleatoriamente um conjunto de pontos com cores e coordenadas aleatórias. O tamanho da matriz é igual ao número de pixels no gráfico. É repetido 5 vezes

  1. com a função rand() regular
  2. com a sua função get_rand()
  3. usando a minha função ulong randUlong()
  4. com a minha função uint randUint()
  5. usando a função @AlexeyNavoykov's ulong RandomLong()
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()

Apanhei números de tal forma para mostrar a essência do problema, quando aplicamos rand()%20000


como deve 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
  }
//+------------------------------------------------------------------+
Mas 99,9% do tempo, esta função também funcionará:
uint randUint(uint max) {return(((uint)rand()<<15)|(uint)rand())%max;}
funcionará ainda mais rapidamente.
Esta função irá gerar um número aleatório de 0 a 1073741824. Este número é ainda maior do que o número de carraças para qualquer instrumento ao longo de toda a história. A "injustiça" de tal função seria microscópica para 99,9% das tarefas.
Arquivos anexados:
 
Nikolai Semko:

Mas mesmo assim o meu algoritmo revela-se o mais rápido de todos os algoritmos propostos, apesar de ser mais universal e não limitado a 32767 como o seu.
O código como prova.

Obrigado pelo trabalho, resultados realmente interessantes. Acontece que a função rand() é tão rápida que funciona mais rapidamente do que as operações aritméticas.

 
Alexey Navoykov:

Obrigado pelo esforço, resultados realmente interessantes. Acontece que o rand() é tão rápido que é mais rápido do que as operações aritméticas.

Não, não é. Cerca de um nanossegundo, tal como extrair a raiz quadrada a partir de um número duplo. As operações +-*/ são realizadas em fracções de um nanossegundo.
Mas tal como a raiz quadrada, o rand() é executado em processadores modernos a nível de hardware, não programático.

 
Nikolai Semko:

não, não mais rápido. Cerca de um nanossegundo, tal como extrair a raiz quadrada a partir de um número duplo. As operações +-*/ são realizadas em fracções de um nanossegundo.
Mas tal como a raiz quadrada, o rand() é executado em processadores modernos a nível de hardware, não programático.

Porque não, não é. A sua versão difere da minha porque rand() é sempre chamada 5 vezes enquanto a minha tem uma média de 1,64 vezes a 20 000 e 1 vez a 256. Ao todo rand() é chamada 25 vezes para cada iteração no seu código enquanto a minha tem 1,64*2+3 = 5,3 vezes. É claro que a situação é estranha, temos de descobrir qual é exactamente a razão. Além disso, há lá muitas operações bitwise a serem realizadas...

 
Nikolai Semko:

1. Bem, compreendemos que nas suas funções o problema não está resolvido, mas apenas mascarado, não vou perder peso, vou apenas apertar mais o meu cinto.

2. Nas nossas versões e nas de Alexey é o pior cenário, enquanto em muitas outras situações a velocidade estará quase ao nível do rand(), enquanto se tem tempo constante.

Alguma vez se perguntou porque é que o rand() gera números num intervalo tão estreito? Este é um gerador pseudo-aleatória, por isso é periódico, gerando assim um monte de números aleatórios em locais onde não é necessário, com posterior descarte dos mesmos, a sua qualidade está a deteriorar-se (irá repetir-se mais cedo).

4. Algumas pessoas extraem dados aleatórios de uma forma mais complicada. Eu, por exemplo, puxar da rede, alguém pode até comprá-la. Porque haveria de querer desperdiçar dados duramente conquistados para depois descartá-los (gerar ulong, escrever o algoritmo certo não é o nosso caminho)?

 
Alexey Navoykov:

Porque não, se sim. A sua versão difere da minha porque chama sempre 5 rand() enquanto que a minha tem uma média de 1,64 vezes a 20 000 intervalos e 1 vez a 256 intervalos. Ao todo, o seu rand() é chamado 25 vezes para cada iteração, enquanto que a minha tem 1,64*2+3 = 5,3 vezes. É claro que esta situação é estranha, temos de descobrir qual é exactamente a razão. Porque, para além disso, tem lá muitas operações bitwise a serem realizadas...

são as operações mais baratas. Quase sem custos.

Mas de um modo geral, concordo. Também não compreendo porquê... Talvez sejam as maravilhas da optimização. Embora o que aí pode ser optimizado...

 
Nikolai Semko:

são as operações mais baratas. Quase sem custos.

Mas de um modo geral, concordo. Também não compreendo porquê... Talvez sejam as maravilhas da optimização. Embora o que há para optimizar...

Parece que não se trata de operações aritméticas, porque não existem, todos os valores são calculados na fase de compilação. A razão é a presença de um laço com um número desconhecido de iterações (embora estas iterações sejam em média inferiores a duas). Assim, o seu código é de alguma forma optimizado devido ao número conhecido de chamadas rand()
 
Vict:

1. Bem, compreendemos que nas suas funções o problema não está resolvido, mas apenas mascarado, não vou perder peso, vou apenas apertar mais o meu cinto.

2. Nas nossas e nas variantes de Alexey é o pior cenário, enquanto em muitas outras situações a velocidade será quase ao nível do rand(), enquanto se tem tempo constante.

Alguma vez se perguntou porque é que o rand() gera números num intervalo tão estreito? Este é um gerador pseudo-aleatória, portanto é periódico, gerando assim um monte de números aleatórios onde não é necessário, com posterior descarte, a sua qualidade está a deteriorar-se (irá repetir-se mais cedo).

4. Algumas pessoas extraem dados aleatórios de uma forma mais complicada. Eu, por exemplo, puxar da rede, alguém pode até comprá-la. É por isso que eu deveria desperdiçar dados difíceis de obter para depois estupidamente os descartar (gerar ulong, escrever um algoritmo adequado não é o nosso caminho, afinal de contas)?

bem, isso é nerd.
Para reproduzir a situação, quando este problema for perceptível pelo menos em 0,1%, é necessário operar com intervalos acima dos seguintes valores:

  • (ULONG_MAX)/1000= 18446744073709551 para a função randUlong()
  • (UINT_MAX)/1000= 4294967 para a função randUint()

Alguma vez utilizou tais intervalos? Então porque é que colocou estes controlos e laços?
O melhor é o inimigo do bom.

Pessoalmente, os meus intervalos degeração aleatórios na prática são limitados a 2000, 4000 no máximo. rand() funciona bastante bem para este fim.
Inserir tal opção no meu 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);

Assim, não notará "injustiça" da função rand() (como foi com rand()%20000) e os pontos serão situados visualmente de forma uniforme, pelo que é a função mais rápida e mais eficiente.

Não é por nada que os programadores do processador limitaram rand() a 2^15=32768. Eles não são pessoas estúpidas. Isto cobre 99% dos problemas práticos.
E para os amantes de ideias "extremas" existe uma opção mais do que suficiente:

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

Pessoalmente, os meus intervalos degeração de números aleatórios na prática são limitados a 2000, máximo 4000. Para isto, rand() funciona bastante bem.
Inserir tal variante no meu código:

e não notará "injustiça" da função rand() (como era com rand()%20000) e os pontos serão visualmente uniformes, por isso está a funcionar bastante e é o mais rápido.

É bem-vinda a sua utilização, não me importo. Tanto mais que o lado direito de % é um múltiplo de RAND_MAX+1 (256 ou 1024).

Não é por nada que os programadores do processador limitaram rand() a 2^15=32768. Eles não são pessoas estúpidas. Cobre 99% das tarefas práticas.
E há variantes mais do que suficientes para aqueles que gostam de ideias "fora dos limites":

O que é que os desenvolvedores de processadores têm a ver com isto? O gerador é um software implementado. O único requisito é RAND_MAX >= 32767 e um período de pelo menos 2^32. Assim, o µl tem um oscilador muito esparso em "mínimos".

E os mais clarividentes farão eles próprios um rand() justo (se não houver multiplicidade), isto até é recomendado em livros de referência.