Особенности языка mql5, тонкости и приёмы работы - страница 136

 
Nikolai Semko:
Т.е. Вы готовы поджечь 100-долларовую купюру, чтобы найти закатившуюся под кровать 10 центовую монетку?

Может быть монеткой, а может и нет

   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%
        

Вероятность десятки в два раза выше. А самое главное - заявление о дороговизне get_rand() высосаны из пальца, так зачем получать случайные числа через задний проход со сдвинутой вероятностью (ожидая при этом равномерное распределение) когда можно нормально? Вы не 100-долларовую купюру экономите, а спички.

 
Vict:

Может быть монеткой, а может и нет

Вероятность десятки в два раза выше. А самое главное - заявление о дороговизне get_rand() высосаны из пальца, так зачем получать случайные числа через задний проход со сдвинутой вероятностью (ожидая при этом равномерное распределение) когда можно нормально? Вы не 100-долларовую купюру экономите, а спички.

Да, был неправ по поводу сильной медлительности Вашей функции. Неправильно понял алгоритм. Сорян.
Но все же мой алгоритм получается самый быстрый из всех предложенных не смотря на то, что он более универсален, а не ограничен числом 32767, как Ваш.
Код в доказательство. 

В данном скрипте рандомно генерируется массив точек со случайным цветом и случайными координатами. Размер массива равен количеству пикселей на чарте графика. Это повторяется 5 раз

  1. c помощью штатной функции rand()
  2. c помощью Вашей функции get_rand()
  3. с помощью моей функции ulong randUlong()
  4. с помощью моей функции uint randUint()
  5. с помощью функции ulong RandomLong() от @Alexey Navoykov
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()

Цифры подобрал таким образом, чтобы продемонстрировать суть проблемы, когда применяем rand()%20000


как должно быть:


//+------------------------------------------------------------------+
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
  }
//+------------------------------------------------------------------+
ЗЫ но для 99.9% случаев прокатит и такая функция:
uint randUint(uint max) {return(((uint)rand()<<15)|(uint)rand())%max;}
она будет работать еще быстрее. 
в этой функции будет генерироваться случайное число от 0 до 1073741824. это число даже больше чем количество тиков по любому инструменту за всю историю. "Нечестность" такой функции будет микроскопической для 99,9% задач.
Файлы:
 
Nikolai Semko:

Но все же мой алгоритм получается самый быстрый из всех предложенных не смотря на то, что он более универсален, а не ограничен числом 32767, как Ваш.
Код в доказательство. 

Спасибо за труды, действительно занятные результаты.  Выходит, что функция rand() настолько быстрая, что работает быстрее арифметических операций.

 
Alexey Navoykov:

Спасибо за труды, действительно занятные результаты.  Выходит, что функция rand() настолько быстрая, что работает быстрее арифметических операций.

нет, не быстрее. Около наносекунды, так же как и извлечение квадратного корня из числа double. Операции +-*/ выполняются за доли наносекунды. 
Но как и квадратный корень, так и rand() выполняется в современных процессорах на уровне "железа", а не программно.

 
Nikolai Semko:

нет, не быстрее. Около наносекунды, так же как и извлечение квадратного корня из числа double. Операции +-*/ выполняются за доли наносекунды. 
Но как и квадратный корень, так и rand() выполняется в современных процессорах на уровне "железа", а не программно.

Почему же нет, если да.  Ваш вариант отличается от моего тем, что у вас всегда вызывается 5 rand(),  а у меня:  в среднем 1.64 раз при диапазоне 20000, и 1 раз при диапазоне 256.  Итого на каждую итерацию у вас rand() вызывается 25 раз, а у меня 1.64*2+3 = 5.3 раза.   Вообще конечно странная ситуация, надо выяснять в чём именно причина.  У вас же там ещё и куча битовых операций выполняется вдобавок...

 
Nikolai Semko:

1. Ну мы ведь понимаем, что в ваших функциях проблема не решается, а лишь маскируется, не буду худеть, а затяну ремень потуже.

2. В наших с Алексеем вариантах это худший вариант работы, во многих других ситуациях скорость будет чуть ли не на уровне rand(), у вас же время постоянно.

3. Не задумывались - почему rand() генерит числа в столь узком диапазоне? Это псевдослучайный генератор, а значит периодический, следовательно, генеря кучу случайных чисел там где это не нужно с последующим их отбросом, ухудшается его качество (пойдут повторения раньше).

4. Некоторые добывают случайные данные более сложным путём. Я, например, дёргал с сети, кто-то может даже покупает. Вот зачем мне тратить полученные с трудом данные для того чтобы потом тупо отбросить (генеря ulong, писать правильный алгоритм - ведь не наш путь)?

 
Alexey Navoykov:

Почему же нет, если да.  Ваш вариант отличается от моего тем, что у вас всегда вызывается 5 rand(),  а у меня:  в среднем 1.64 раз при диапазоне 20000, и 1 раз при диапазоне 256.  Итого на каждую итерацию у вас rand() вызывается 25 раз, а у меня 1.64*2+3 = 5.3 раза.   Вообще конечно странная ситуация, надо выяснять в чём именно причина.  Потому что у вас же там ещё и куча битовых операций выполняется вдобавок...

битовые самые дешевые операции. Почти бесплатные. 

Но в целом согласен. Тоже не понимаю почему... Может чудеса оптимизации. Хотя что там можно оптимизировать...

 
Nikolai Semko:

битовые самые дешевые операции. Почти бесплатные. 

Но в целом согласен. Тоже не понимаю почему... Может чудеса оптимизации. Хотя что там можно оптимизировать...

Похоже дело таки не в арифметических операциях, ибо их там и нет, все значения вычисляются на стадии компиляции.  Причина - в наличии цикла с неизвестным числом итераций (хотя этих итераций в среднем выходит менее двух).  Значит ваш код каким-то образом оптимизируется за счёт известного количества вызовов rand()
 
Vict:

1. Ну мы ведь понимаем, что в ваших функциях проблема не решается, а лишь маскируется, не буду худеть, а затяну ремень потуже.

2. В наших с Алексеем вариантах это худший вариант работы, во многих других ситуациях скорость будет чуть ли не на уровне rand(), у вас же время постоянно.

3. Не задумывались - почему rand() генерит числа в столь узком диапазоне? Это псевдослучайный генератор, а значит периодический, следовательно, генеря кучу случайных чисел там где это не нужно с последующим их отбросом, ухудшается его качество (пойдут повторения раньше).

4. Некоторые добывают случайные данные более сложным путём. Я, например, дёргал с сети, кто-то может даже покупает. Вот зачем мне тратить полученные с трудом данные для того чтобы потом тупо отбросить (генеря ulong, писать правильный алгоритм - ведь не наш путь)?

ну это уже занудство.
Чтобы воспроизвести ситуацию, когда эта проблема будет заметна хоть на 0.1% нужно оперировать диапазонами выше следующих значений:

  •  (ULONG_MAX)/1000= 18446744073709551 для функции randUlong()
  •  (UINT_MAX)/1000= 4294967 для функции randUint()

Вы хоть раз использовали такие диапазоны?  Тогда зачем вставлять эти проверки и циклы?
Лучшее - враг хорошего.

Лично мои диапазоны  генерации случайных чисел на практике ограничены 2000,  максимум 4000. Для этого вполне сносно работает rand()
Вставьте в мой код такой вариант:

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

и Вы уже глазами не заметите "нечестность" функции rand() (как было с вариантом rand()%20000) и точки будут располагаться визуально равномерно, так что она вполне себе рабочая и самая быстрая.

ЗЫ Не даром же разработчики процессоров ограничили rand() значением 2^15=32768. Они же не глупые люди. Это вполне покрывает 99% практических задач. 
А для любителей "запредельных" идей более чем достаточно варианта:

ulong randUlong(ulong max=ULONG_MAX){return(((ulong)rand()<<60)|((ulong)rand()<<45)|((ulong)rand()<<30)|((ulong)rand()<<15)|(ulong)rand())%max;}
Файлы:
 
Nikolai Semko:

Лично мои диапазоны  генерации случайных чисел на практике ограничены 2000,  максимум 4000. Для этого вполне сносно работает rand()
Вставьте в мой код такой вариант:

и Вы уже глазами не заметите "нечестность" функции rand() (как было с вариантом rand()%20000) и точки будут располагаться визуально равномерно, так что она вполне себе рабочая и самая быстрая.

Пользуйтесь на здровье, я не против. Тем более когда справа от % стоит число кратное RAND_MAX+1 (256 или 1024).

ЗЫ Не даром же разработчики процессоров ограничили rand() значением 2^15=32768. Они же не глупые люди. Это вполне покрывает 99% практических задач. 
А для любителей "запредельных" идей более чем достаточно варианта:

При чём тут разработчики процессоров? Генератор - программно реализован. Единственное требование RAND_MAX >= 32767 и период не менее 2^32. Так что в мкл очень скудный генератор на "минималках".

А самые дальновидные будут делать себе честный rand() (если нет кратности), это даже в справочниках рекомендуется.