Случайные числа - страница 2

 

Да вряд ли, No_Name. Эта функция линейно зависит от функции генератора, так что если есть проблемы, то их надо искать именно в MathRand(). А аргументы твои неубедительны. Нарушения равномерности не должно быть. Проверь на скрипте ниже, подставив свою функцию вместо MathRand() и изменив размерность массива freq[].

Mak, ты меня напугал. Чтобы разобраться и с этим, нацарапал простенький скрипт для генерации миллиарда чисел, и для каждого от 0 до 32767 вычисляется частота. Выход скрипта - две частоты, минимальная и максимальная. Прогони его пару десятков раз. Я не стал строго проверять нулевую гипотезу о равномерности, но, по-моему, результаты должны тебя убедить.

//+------------------------------------------------------------------+
//|                                            rand_uniform_test.mq4 |
//|                                       Copyright © 2007, Mathemat |
//+------------------------------------------------------------------+
 
#property copyright "Copyright © 2007, Mathemat"
#property show_inputs
 
extern int iterations = 1000000000;
 
int start()
{
   int freq[ 32768 ];
   ArrayInitialize( freq, 0 );
    
   MathSrand( GetTickCount() );
   for ( int i = 0; i < iterations; i ++ )  
   {
      freq[ MathRand() ] ++;
      if ( i % 1000000 == 0 )   Comment( ( i + 1 ) / 1000000, " млн. итераций" ); 
   }
   
   Alert( "Минимальная частота = ",     freq[ ArrayMinimum( freq ) ], 
          " ; максимальная частота = ", freq[ ArrayMaximum( freq ) ] );
   return(0);
}
 
В общем случае:

x (x_min <= x <= x_max) --> y (y_min <= y <= y_max): y = a + b * x;
a = (y_min * x_max - y_max * x_min) / (x_max - x_min);
b = (y_max - y_min) / (x_max - x_min);

Линейное преобразование не меняет тип функции распределения, а лишь подвергает ее сдвигу и сжатию либо растяжению. При этом белый шум остается белым шумом. Хорошо известный пример изменения типа функции распределения -- генерация СЧ с гауссовым распределением популярным методом "логарифмической пары", т.е. применением нелинейного преобразования к изначально равномерно-распределенной последовательности СЧ.

<Подробный комментарий сожрал форум.>

Лично я пользуюсь способом Notepad + ^C + ^V, чего и всем желаю.
 
alexjou, ты случаем не учился в 18-м интернате?
 
Нет. У мну 3 класса ЦПШ. :))))
 
Ага, понятно. Батюшка, видно, вместо Закона Божьего тебю рихметике учил...
 
"^C + ^V, чего и всем желаю" - пользуюсь, но иногда забываю.
Есть ощущение, что при использовании некоторых слов в сообщении на форуме срабатывает какой-то фильтр, и имитирует сбой :))

Да, я имел в виду то, про что потом писал No_Name

Проблема связана с низкой дискретностью исходного ряда (функции MathRand())
Пробуйте, два варианта - Max = 32767 и Max = 16383.
int iterations = 100000001;
int Min = 0;
 
// Выбрать один из вариантов Max
//int Max = 32767;
int Max = 16383;
 
int testCount = 0;
int NumTests  = 5;
double test[11][5];
 
int start()
{
   for(int k = 0; k < NumTests; k++) Test(k);
}
 
void Test(int k)
{
   int freq[0];
   int min = Min;
   int max = Max + k - NumTests/2;
   int Range = max - min + 1;
 
   test[k][1] = min;
   test[k][2] = max;
   
   ArrayResize(freq, Range);
   ArrayInitialize(freq, 0);
    
   MathSrand(GetTickCount());
   for (int i = 1; i <= iterations; i ++)  
   {
      int R = MathRand()/32768.0*(max - min + 1) + min;
      freq[R] ++;
      if (i % 1000000 == 0)   
      {
         string s = "";
         test[k][0] = (i + 1)/1000000;
         test[k][3] = 1.0*Range*freq[ArrayMinimum(freq,Range,min)]/i;
         test[k][4] = 1.0*Range*freq[ArrayMaximum(freq,Range,min)]/i;
         
         for(int j = 0; j < NumTests; j++)
         {
            s = s + "\n" + DoubleToStr(test[j][0],0)
                  + " млн - min: " + DoubleToStr(test[j][1],0)
                  + " - max: " + DoubleToStr(test[j][2],0)
                  + " - fmin: " + DoubleToStr(test[j][3],4)
                  + " - fmax: " + DoubleToStr(test[j][4],4);
         }
         Comment(s); 
      }
   }
}

Если вас результаты устраивают, флаг в руки.

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

По этой причине использовать MathRand проблематично.
Скомбинировать из нее чтото более приличное тоже невозможно.
Выходов только два:
1 - убедить разработчиков добавить еще одну, хорошую функцию,
2 - открыть Кнута, и написать ее самому на MQL.
 
Насколько я понимаю, математические функции MQL являются просто обертками к соответствующим функциям С. Поэтому претензии к разработчикам МТ по поводу ГСЧ, вообще говоря, несправедливы. Не входя в излишние подробности, можно сказать, что вообще создание "хорошего" ГСЧ -- давнишняя и больная проблема. Одной из лучших программ, реально использовавшихся в монте-карловских расчетах в теории поля на решетке, была 64-разрядная программа для векторного компьютера CRAY-2, написанная на CRAY FORTRAN, реализующая сдвиговый регистр со случайной обратной связью. Однако "написать ее самому на MQL" вряд ли удастся, т.к. она явным образом эксплуатировала архитектуру процессора.
Как паллиатив для улучшения качества штатного ГСЧ можно предложить методику случайной перетасовки специфически инициализированной генерируемой последовательности СЧ. В аттаче пример такого подхода для VB6 (взято из кн.: Д.К. Крейг, Д. Уэбб. Microsoft Visual Basic 6.0. Мастерская разработчика. Изд. 5-е. М., "Русская редакция", 2001, с. 499).
Файлы:
random.zip  1 kb
 
Претензий нет, я писал, что пользоваться этой функцией нужно с большой осторожностью.
В большинстве случаев будет получаться совсем не то, что ожидается.
 

Интересно, насколько часто возникает небходимость генерировать СЧ с точностью 4+ знака? Если достаточно меньшей точности, или, что эквивалентно, нужно генерировать целые случайные числа в диапазоне, меньшем 32768, проблему можно обойти (с некоторой пренебрежимой потерей к.п.д). Допустим нужно генерировать целые СЧ в диапазоне от 1 до N.

Для начала найдём k = 32768/N .

После этого просто запишем условие if (MathRand()>= k*N) "нуегонафигэточисло";

else "авотсэтимпоработаем";