Как MathRand() генерирует значения? - страница 2

 
yu-sha:

Расскажите, плз,  как MathRand() получает значения.

Можно ли ожидать равномерное распределение MathRand() в заявленном диапазоне?

вот один из примеров, конечно существуют и другие, более сложные алгоритмы...

исходник rand.c :

/***
*rand.c - random number generator
*
*       Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
*       defines rand(), srand() - random number generator
*
*******************************************************************************/

#include <cruntime.h>
#include <mtdll.h>
#include <stddef.h>
#include <stdlib.h>

#ifndef _MT
static long holdrand = 1L;
#endif  /* _MT */

/***
*void srand(seed) - seed the random number generator
*
*Purpose:
*       Seeds the random number generator with the int given.  Adapted from the
*       BASIC random number generator.
*
*Entry:
*       unsigned seed - seed to seed rand # generator with
*
*Exit:
*       None.
*
*Exceptions:
*
*******************************************************************************/

void __cdecl srand (
       unsigned int seed
       )
{
#ifdef _MT

       _getptd()->_holdrand = (unsigned long)seed;

#else  /* _MT */
       holdrand = (long)seed;
#endif  /* _MT */
}


/***
*int rand() - returns a random number
*
*Purpose:
*       returns a pseudo-random number 0 through 32767.
*
*Entry:
*       None.
*
*Exit:
*       Returns a pseudo-random number 0 through 32767.
*
*Exceptions:
*
*******************************************************************************/

int __cdecl rand (
       void
       )
{
#ifdef _MT

       _ptiddata ptd = _getptd();

       return( ((ptd->_holdrand = ptd->_holdrand * 214013L
           + 2531011L) >> 16) & 0x7fff );

#else  /* _MT */
       return(((holdrand = holdrand * 214013L + 2531011L) >> 16) & 0x7fff);
#endif  /* _MT */
}
 

Потребовалась функция ГСЧ с гнерацией числа Int от 0 до любого значения.

Так как максимальное значение Int =2 147 483 647, расчет ведем до негою

Получилась такая функция. Думаю распределение получилось равномерным.

int RandomInteger(int max_vl){//случайное Int от 0 до  Max Int
        //srand(0);//если нужна повторяемость.  MathSrand(GetTickCount()) если нужна неповторяемость
        int r=MathRand();// от 0 до 32767.
        double k=32767.0;
        if(max_vl>32767){
                r=r+MathRand()*32767;//0...32767 + 0.. 32767*32767(с шагом 32767)  max=1073709056 > 1 mlrd
                k=k*k;
                if(max_vl>1073709056){// int type max alue =2 147 483 647 > 2 mlrd
                        r=r*2+MathRand()%2;//0...2 147 483 647 > to max int number c шагом 2 + (0 или 1)
                        k=k*2;
                }
        }
        return (int)MathFloor(r/k*max_vl);
}
 
В статье есть генератор до 4294967295
 
Rorschach:
В статье есть генератор до 4294967295

в СБ Алглиб есть генератор повышенной точности

UPD: вот пробовал разобраться с ним https://www.mql5.com/ru/forum/324066#comment_13500222 , вроде работает, но документации по Алглиб в MQL5 нет, на сайте алглиб нужно почитать

 
Rorschach:
В статье есть генератор до 4294967295

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

 
Igor Makanu:

в СБ Алглиб есть генератор повышенной точности

UPD: вот пробовал разобраться с ним https://www.mql5.com/ru/forum/324066#comment_13500222 , вроде работает, но документации по Алглиб в MQL5 нет, на сайте алглиб нужно почитать

Да я его видел, но хочется без алглиба. К тому же та функция содержит массу */%. Сравнивать по скорости не стал, очевидно она медленнее. Генератор из статьи работает с побитовыми смещениями- это быстро.

 

Переписал свою ф-ю для оптимизации скорости:

int RandomInteger(int max_vl){return (int)MathFloor((MathRand()+MathRand()*32767.0)/1073741824.0*max_vl);}//случайное Int от 0 до  Max Int
Распределение получилось равномерным. См. изображения эксперимента в блоге https://www.mql5.com/ru/blogs/post/735953

Переписал ГСЧ из статьи.
Отбросил лишнее, вот что получилось:

//если из define переместить в код RNDUint, то скорость работы увеличится на 30% для 10 млн повторов с 600 мс до 850 мс
#define xor32  xx=xx^(xx<<13);xx=xx^(xx>>17);xx=xx^(xx<<5)
#define xor128 t=(x^(x<<11));x=y;y=z;z=w;w=(w^(w>>19))^(t^(t>>8))
#define inidat x=123456789;y=362436069;z=521288629;w=88675123;xx=2463534242

class RNDUint{
protected:
   uint      x,y,z,w,xx,t;
public:
        RNDUint(void){inidat;};
        ~RNDUint(void){};
   uint      Rand()    {xor128;return(w);};//равномерное распределение на отрезке [0,UINT_MAX=4294967295].
   double    Rand_01() {xor128;return((double)w/UINT_MAX);};//равномерное распределение на отрезке [0,1].
   void      Reset()   {inidat;};//сброс всех исходных значений в первоначальное состояние.
   void      SRand(uint seed)  {//установка новых исходных значений генератора.seed= [0,UINT_MAX=4294967295]. При seed=0 функция меняет начальные значения случайным образом.
      int i;if(seed!=0){xx=seed;}for(i=0;i<16;i++){xor32;}xor32;x=xx;xor32;y=xx;xor32;z=xx;xor32;w=xx;for(i=0;i<16;i++){xor128;}
   };
};

Распределение тожеполучилось равномерным.

Сделал сравнение по скорости обоих функций, оригинальной из статьи и просто MathRand():


Оригинальная из статьи Rnd.Rand_01() - 602 мс
Укороченная по статье rnu.Rand_01() - 596 мс
Моя оптимизированная  версия RandomInteger()  - 840 мс (старая версия 1200 мс, т.е. ускорил на 25%)
Просто по MathRand() - 353 мс (самая быстрая, но распределение будет неравномерным. Если диапазон требуемых чисел больше 32767, то результат будет с пропусками. Если меньше 32767, например i=31111. то к части точек округление будет происходить чаще.)

Пользуйтесь)
Rand 0 ... Max Int с равномерным распределением
Rand 0 ... Max Int с равномерным распределением
  • www.mql5.com
Потребовалась функция ГСЧ с гнерацией числа Int от 0 до любого значения.Получилась такая функция. Думаю распределение получилось равномерным. int RandomInteger(int max_vl){return
 
elibrarius:
Интересно. Нашел еще рандом в стандартной библиотеке, но вряд ли он будет лучше.
 

зачем так всё усложнять?

можно же так (грубо говоря):

if (MathRand()%100>50) => Buy

if (MathRand()%100<50) => Sell

А если поиграться хочется или посмотреть что как показывает, то так:

int x=1;

if (MathRand()%100>50) x=1;

if (MathRand()%100<50) x=2;

Comment(x);

и смотрим как он меняет значения.

 
elibrarius:

Переписал свою ф-ю для оптимизации скорости:

Распределение получилось равномерным. См. изображения эксперимента в блоге https://www.mql5.com/ru/blogs/post/735953

Переписал ГСЧ из статьи.
Отбросил лишнее, вот что получилось:

Распределение тожеполучилось равномерным.

Сравнил с распределением из справки, разницы не вижу

Файлы:
Причина обращения: