Как получить случайное число в N-ном деапазоне ? - страница 4

 
Alexey Navoykov:

На основе чего вы решили что распределение будет честным?  Математические обоснования есть?  У меня лично большие сомнения в этом, чисто интуитивно.  Когда что-то исключаешь, то результат никак не может быть честным, ибо ты вносишь некую ЗАКОНОМЕРНОСТЬ в процесс генерации.   Другое дело, если процесс исключения носил случайный характер, тогда его действие было бы нейтрально.

Теперь мы должны  поинтересоваться, что такое процесс исключения. Ведь, так? 

 
Alexey Navoykov:

На основе чего вы решили что распределение будет честным?  Математические обоснования есть?  У меня лично большие сомнения в этом, чисто интуитивно.  Когда что-то исключаешь, то результат никак не может быть честным, ибо ты вносишь некую ЗАКОНОМЕРНОСТЬ в процесс генерации.   Другое дело, если процесс исключения носил случайный характер, тогда его действие было бы нейтрально.

Тут математическое обоснование проще не бывает. Например, генератор выдает числа 0, 1, 2, 3, 4. Нужно получить число 0 или 1 (как остаток от деления на 2). Всего вариантов 5, вероятность выпадания каждого числа 0.2, но четных три, а нечетных 2, поэтому вероятность 0 будет 0.6, а вероятность 1 - 0.4.  Делаем так: получаем случайное число, если это 4 - пропускаем и запрашиваем новое число. В итого вероятность получения четного и нечетного чисел выравнивается и соответственно выравнивается вероятность получения 0 и 1 в виде остатка.

 
Неправда Ваша. Вот, есть бинарные опционы. Предлагаю все наоборот. Если вверх дернулась, то плюс, а если вниз - минус. ГПСЧ. 
 
Алексей Тарабанов:
Неправда Ваша. Вот, есть бинарные опционы. Предлагаю все наоборот. Если вверх дернулась, то плюс, а если вниз - минус. ГПСЧ. 

Минус единицу! Умножай на минус единицу!

 
Ползай задом наперед, делай все наоборот. Можем и так. 
 
Dmitry Fedoseev:

Тут математическое обоснование проще не бывает. Например, генератор выдает числа 0, 1, 2, 3, 4. Нужно получить число 0 или 1 (как остаток от деления на 2). Всего вариантов 5, вероятность выпадания каждого числа 0.2, но четных три, а нечетных 2, поэтому вероятность 0 будет 0.6, а вероятность 1 - 0.4.  Делаем так: получаем случайное число, если это 4 - пропускаем и запрашиваем новое число. В итого вероятность получения четного и нечетного чисел выравнивается и соответственно выравнивается вероятность получения 0 и 1 в виде остатка.

Да, всё верно. Чё-то я сразу не сообразил...  И только сейчас понял, что мои формулы с целочисленными расчётами ошибочны. Странно, что никто не обратил внимание, там же полная хрень получалась  )

Более того, даже расчёты с плавающей точкой дают ощутимую погрешность.  Ваш упрощённый пример наглядно показывает, что единственно правильный способ - это исключать хвост, как и предлагал SemenTalonov.

 

Приведу наглядный пример.

Диапазон: 0 - 999

Кол-во бросков: 10 000 000

Верхняя линия - распределение кол-ва всех отданных генератором чисел (с хвостом)

Нижняя линия - распределение кол-ва с исключением из выдачи чисел в последнем некратном диапазоне (без хвоста)

По графикам очевидно, без коррекции, числа больше 760 выпадают реже.

Файлы:
TestRND.mq5  8 kb
 

Вот такой код у меня родился.  Сорри за растянутость, но так удобнее читается.

int _MaxValidRnd(int range)  { const int rnd_range= 32768;  return rnd_range - rnd_range%range - 1; }


ulong RandomLong(ulong range)
{
  ulong rnd;
  if (range <= 1<<15) { int max=_MaxValidRnd(range);  while((rnd=rand()) > max);  return rnd%range; }
  if (range <= 1<<30) { int hrange=int(range>>15)+1;  int max=_MaxValidRnd(hrange);  while((rnd=rand()) > max || (rnd= ((rnd%hrange)<<15 | rand())) > range);  return rnd; }
  if (range <= 1<<45) { int hrange=int(range>>30)+1;  int max=_MaxValidRnd(hrange);  while((rnd=rand()) > max || (rnd= ((rnd%hrange)<<30 | rand()<<15 | rand())) > range);  return rnd; }
  if (range <= 1<<60) { int hrange=int(range>>45)+1;  int max=_MaxValidRnd(hrange);  while((rnd=rand()) > max || (rnd= ((rnd%hrange)<<45 | (long)rand()<<30 | rand()<<15 | rand())) > range);  return rnd; }
                else  { int hrange=int(range>>60)+1;  int max=_MaxValidRnd(hrange);  while((rnd=rand()) > max || (rnd= ((rnd%hrange)<<60 | (long)rand()<<45 | (long)rand()<<30 | rand()<<15 | rand())) > range);  return rnd; }
}
 
А может проще попросить разрабов добавить функцию где можно выбирать диапозон? :) я когда искал в сети интернет эту функцию, то очень много таких тем, значит пользуется популярность rand()
 
Если тестер с облаками не нужен, то, на мой взгляд, проще написать на стороне, чем мастерить все эти велосипеды. Взять плюсовую https://en.cppreference.com/w/cpp/numeric/random (сомневаюсь, что в мкл появится что-то такого уровня), всё вылизано, без багов. А если хочется, то можно подсунуть свой источник случайности (чего-нибудь из сети дёрнуть, а у некоторых на борту есть ГСЧ), который будет работать с желаемым адаптером обеспечивающим нужное распределение.
Pseudo-random number generation - cppreference.com
  • en.cppreference.com
The random number library provides classes that generate random and pseudo-random numbers. These classes include: Uniform random bit generators (URBGs), which include both random number engines, which are pseudo-random number generators that generate integer sequences with a uniform distribution, and true random number generators if available...
Причина обращения: