Создание механизма PRNG для <random>в C ++ 11, который соответствует PRNG, приводит к R - PullRequest
0 голосов
/ 08 ноября 2018

Этот вопрос двоякий. Я перевожу R-скрипт на C ++, который использует комбинированный многорекурсивный генератор L'Ecuyer (CMRG) в качестве своего движка (в частности, MRG32k3a), который затем возвращает случайное число из равномерного распределения за интервал (0, 1). Минимальный пример в R показан ниже:

seednum<-100                              # set seed
set.seed(seednum, kind="L'Ecuyer-CMRG")   # set RNG engine
runif(1)                                  # set distribution

Я хочу иметь возможность проверить мои результаты между сценарием R и кодом C ++ (поскольку сгенерированные случайные числа являются только началом). Я обнаружил, что PRNG с одинаковыми начальными числами на разных языках не обязательно дают один и тот же результат (так как они могут иметь параметры, которые компилятор может свободно указывать), как видно из сообщений SO * здесь и здесь . То есть использование одного и того же начального числа, одного и того же механизма и одного и того же распределения может привести к различным случайным числам в зависимости от конкретной реализации PRNG. Соответствующий пример между R и C ++ 11 приведен ниже. Используя вездесущий PRNG Мерсенна-Твистера в R:

seednum<-100
set.seed(seednum, kind="Mersenne-Twister")
runif(1)

В результате получается случайное число 0.3077661. Делаем то же самое в C ++ 11:

#include <iostream>
#include <random>

int main()
{
  unsigned seed = 100;

  std::mt19937 generator (seed);

  std::uniform_real_distribution<double> distribution (0.0, 1.0);

  std::cout << distribution(generator) << std::endl;

  return 0;
}

В результате получается случайное число 0.671156. Первоначально я был смущен этим результатом, но предыдущие вопросы SO прояснили это для меня (как указано выше). Казалось бы, есть параметры, передаваемые в MRG32k3a в R, которые мне нужно реплицировать в C ++, чтобы генерировать те же случайные числа. Таким образом, первый вопрос: где я могу найти документацию по реализации MRG32k3a в R, в которой указаны эти параметры?

Второй вопрос касается реализации этого генератора в C ++ 11. Этот генератор не отображается в списке предварительно сконфигурированных типов движков в библиотеке <random> C ++ 11, указанной здесь здесь . Пример реализации MRG32k3a в C можно найти здесь и показан ниже:

/*
   32-bits Random number generator U(0,1): MRG32k3a
   Author: Pierre L'Ecuyer,
   Source: Good Parameter Sets for Combined Multiple Recursive Random
           Number Generators,
           Shorter version in Operations Research,
           47, 1 (1999), 159--164.
   ---------------------------------------------------------
*/
#include <stdio.h>

#define norm 2.328306549295728e-10
#define m1   4294967087.0
#define m2   4294944443.0
#define a12     1403580.0
#define a13n     810728.0
#define a21      527612.0
#define a23n    1370589.0

/***
The seeds for s10, s11, s12 must be integers in [0, m1 - 1] and not all 0. 
The seeds for s20, s21, s22 must be integers in [0, m2 - 1] and not all 0. 
***/

#define SEED 100

static double s10 = SEED, s11 = SEED, s12 = SEED,
              s20 = SEED, s21 = SEED, s22 = SEED;


double MRG32k3a (void)
{
   long k;
   double p1, p2;
   /* Component 1 */
   p1 = a12 * s11 - a13n * s10;
   k = p1 / m1;
   p1 -= k * m1;
   if (p1 < 0.0)
      p1 += m1;
   s10 = s11;
   s11 = s12;
   s12 = p1;

   /* Component 2 */
   p2 = a21 * s22 - a23n * s20;
   k = p2 / m2;
   p2 -= k * m2;
   if (p2 < 0.0)
      p2 += m2;
   s20 = s21;
   s21 = s22;
   s22 = p2;

   /* Combination */
   if (p1 <= p2)
      return ((p1 - p2 + m1) * norm);
   else
      return ((p1 - p2) * norm);
}

int main()
{
   double result = MRG32k3a();

   printf("Result with seed 100 is: %f\n", result);

   return (0);
}

Как уже отмечалось, мне нужно использовать этот генератор для создания двигателя, который можно подавать в единый реальный дистрибутив. Проблема в том, что я понятия не имею, как это делается, и я нигде не могу найти никакой информации (кроме знания того, что движки - это классы). Существуют ли какие-либо ресурсы C ++ 11, которые могут помочь мне в такой задаче? Я не прошу решения проблемы, а скорее указатели, которые помогут мне реализовать это самостоятельно.

Ответы [ 2 ]

0 голосов
/ 08 ноября 2018

Я обнаружил, что PRNG с одинаковыми начальными числами на разных языках не обязательно дают один и тот же результат (поскольку они могут иметь параметры, которые компилятор может указывать свободно), как видно из сообщений SO * здесь и здесь . То есть использование одного и того же начального числа, одного и того же механизма и одного и того же распределения может привести к различным случайным числам в зависимости от конкретной реализации PRNG.

Первый ответ объясняет лишь то, что не существует последовательности случайных чисел, которая бы универсально соответствовала данному семени PRNG; он может быть задокументирован и реализован по-разному в разных API (не только в компиляторе и не только на уровне языка). Второй ответ специфичен для rand и srand в языке C и имеет место, потому что rand и srand используют неуказанный алгоритм .

Хотя ни один из ответов не затрагивает распределения случайных чисел, они также важны, если желательна воспроизводимая «случайность». В этом смысле, хотя C ++ гарантирует поведение механизмов, которые он предоставляет, он делает поведение своих распределений (включая uniform_real_distribution) специфичным для реализации .

В общем, проблем, связанных с заполнением PRNG для повторяемой «случайности», можно было бы избежать, если бы API RNG использовали стабильный (неизменный) и документированный алгоритм не только для засеянного PRNG, но и для любых методов с произвольным числом которые используют этот PRNG (который, в случае R, включает runif и rnorm) & mdash; в последнем случае, потому что воспроизводимость "случайных" последовательностей зависит от того, как эти методы (не только сам PRNG) задокументированы.

В зависимости от того, написали ли вы код R, о котором идет речь, может быть вариант записать код C ++ и R в , используя пользовательский PRNG (как вы, кажется, сделали это самостоятельно) и использовать реализованные на заказ алгоритмы для каждого метода случайных чисел, который использует исходный код R (например, runif и rnorm). Этот вариант может быть жизнеспособным, особенно потому, что статистические тесты, как правило, нечувствительны к деталям конкретного используемого ГСЧ.

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

0 голосов
/ 08 ноября 2018

Первый вопрос, таким образом, где я могу найти документацию по реализации MRG32k3a в R, которая определяет эти параметры?

Я бы использовал источник: https://github.com/wch/r-source/blob/5a156a0865362bb8381dcd69ac335f5174a4f60c/src/main/RNG.c#L143

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

Требования к RandomNumberEngine можно найти здесь: https://en.cppreference.com/w/cpp/named_req/RandomNumberEngine Хотя для выполнения uniform_real_distribution:

достаточно выполнить UniformRandomBitGenerator
Expression      Return type     Requirements
G::result_type  T               T is an unsigned integer type
G::min()        T               Returns the smallest value that G's operator()
                                may return. The value is strictly less than
                                G::max().
G::max()        T               Returns the largest value that G's operator() may
                                return. The value is strictly greater than
                                G::min()
g()             T               Returns a value in the closed interval [G::min(),
                                G::max()]. Has amortized constant complexity.  

Основная проблема заключается в том, что MRG32k3a должен возвращать число с плавающей запятой в (0,1), а C ++ UniformRandomBitGenerator возвращает целочисленный тип. Почему вы хотите интегрировать с заголовком <random>?

Дополнительные трудности, которые вы должны принять во внимание:

Альтернативы могут включать непосредственное использование исходного кода R без интеграции с заголовком <random> или ссылкой на libR.

...