在 R 中播种用户提供的随机数生成器
Seeding a user supplied random number generator in R
我在 R 中播种用户定义的 RNG 时遇到了一些问题。似乎
set.seed(123, kind='user', normal.kind='user')
实际上并没有将123
传递给用户定义的RNG初始化。
我回到 ?Random.user
上的文档并尝试了那里给出的示例代码,并稍作修改,打印传递给 user_unif_init
函数的种子(下面的完整代码)。
重现步骤:
- 将下面的代码粘贴到
urand.c
- 运行
R CMD SHLIB urand.c
- 打开
R
运行以下命令:
> dyn.load('urand.so')
> set.seed(123, kind='user', normal.kind='user')
Received seed: 720453763
Received seed: 303482705 // any other numbers than 123
这是我在 urand.c
中使用的完整代码:
// ## Marsaglia's congruential PRNG
#include <stdio.h>
#include <R_ext/Random.h>
static Int32 seed;
static double res;
static int nseed = 1;
double * user_unif_rand()
{
seed = 69069 * seed + 1;
res = seed * 2.32830643653869e-10;
return &res;
}
void user_unif_init(Int32 seed_in) {
printf("Received seed: %u\n", seed_in);
seed = seed_in;
}
int * user_unif_nseed() { return &nseed; }
int * user_unif_seedloc() { return (int *) &seed; }
/* ratio-of-uniforms for normal */
#include <math.h>
static double x;
double * user_norm_rand()
{
double u, v, z;
do {
u = unif_rand();
v = 0.857764 * (2. * unif_rand() - 1);
x = v/u; z = 0.25 * x * x;
if (z < 1. - u) break;
if (z > 0.259/u + 0.35) continue;
} while (z > -log(u));
return &x;
}
如有任何帮助,我们将不胜感激!
转发给 RNG 的种子与提供的种子不同,但是,当使用 "normal" 工作流程时,它是可复制的。然后给出可重现的随机数:
dyn.load('urand.so')
RNGkind("user", "user")
#> Received seed: 1844983443
set.seed(123)
#> Received seed: 303482705
runif(10)
#> [1] 0.42061954 0.77097033 0.14981063 0.27065365 0.77665767 0.96882090
#> [7] 0.49077135 0.08621131 0.52903479 0.90398294
set.seed(123)
#> Received seed: 303482705
runif(10)
#> [1] 0.42061954 0.77097033 0.14981063 0.27065365 0.77665767 0.96882090
#> [7] 0.49077135 0.08621131 0.52903479 0.90398294
(请注意,我已将您的 urand.c
稍微更改为使用 R_ext/Print.h
中的 Rprintf
。)
编辑:如果您需要控制种子(为什么?),您可以自己控制:将 user_unif_init
、user_unif_nseed
和 user_unif_seedloc
替换为
void set_seed(int * seed_in) {
Rprintf("Received seed: %u\n", *seed_in);
seed = *seed_in;
}
并显式调用它:
dyn.load('urand.so')
RNGkind("user", "user")
set_seed <- function(seed) {
invisible(.C("set_seed", seed_in = as.integer(seed)))
}
set_seed(123)
#> Received seed: 123
runif(10)
#> [1] 0.00197801 0.61916849 0.34846373 0.04152509 0.09669026 0.29923760
#> [7] 0.04184693 0.32557942 0.44473242 0.22339845
set_seed(123)
#> Received seed: 123
runif(10)
#> [1] 0.00197801 0.61916849 0.34846373 0.04152509 0.09669026 0.29923760
#> [7] 0.04184693 0.32557942 0.44473242 0.22339845
编辑 2:已在 https://svn.r-project.org/R/trunk/src/main/RNG.c:
查看源代码
static void RNG_Init(RNGtype kind, Int32 seed)
{
int j;
BM_norm_keep = 0.0; /* zap Box-Muller history */
/* Initial scrambling */
for(j = 0; j < 50; j++)
seed = (69069 * seed + 1);
[...]
造成差异的是这 50 发 LCG 子弹。我的猜测是 R 的作者假设典型的用户提供的种子很小,因此对于种子来说随机性不够。
R 似乎将 RNG.c
中用户提供的种子打乱如下:
for(j = 0; j < 50; j++)
seed = (69069 * seed + 1)
尝试解读这将是找回原始种子的一种方式。
更新
可以通过69069的multiplicative inverse解扰如下:
Int32 unscramble(Int32 scrambled)
{
int j;
Int32 u = scrambled;
for (j=0; j<50; j++) {
u = ((u - 1) * 2783094533);
}
return u;
}
在我的 user_unif_init()
函数中插入这个解决了问题。
我在 R 中播种用户定义的 RNG 时遇到了一些问题。似乎
set.seed(123, kind='user', normal.kind='user')
实际上并没有将123
传递给用户定义的RNG初始化。
我回到 ?Random.user
上的文档并尝试了那里给出的示例代码,并稍作修改,打印传递给 user_unif_init
函数的种子(下面的完整代码)。
重现步骤:
- 将下面的代码粘贴到
urand.c
- 运行
R CMD SHLIB urand.c
- 打开
R
运行以下命令:
> dyn.load('urand.so') > set.seed(123, kind='user', normal.kind='user') Received seed: 720453763 Received seed: 303482705 // any other numbers than 123
这是我在 urand.c
中使用的完整代码:
// ## Marsaglia's congruential PRNG
#include <stdio.h>
#include <R_ext/Random.h>
static Int32 seed;
static double res;
static int nseed = 1;
double * user_unif_rand()
{
seed = 69069 * seed + 1;
res = seed * 2.32830643653869e-10;
return &res;
}
void user_unif_init(Int32 seed_in) {
printf("Received seed: %u\n", seed_in);
seed = seed_in;
}
int * user_unif_nseed() { return &nseed; }
int * user_unif_seedloc() { return (int *) &seed; }
/* ratio-of-uniforms for normal */
#include <math.h>
static double x;
double * user_norm_rand()
{
double u, v, z;
do {
u = unif_rand();
v = 0.857764 * (2. * unif_rand() - 1);
x = v/u; z = 0.25 * x * x;
if (z < 1. - u) break;
if (z > 0.259/u + 0.35) continue;
} while (z > -log(u));
return &x;
}
如有任何帮助,我们将不胜感激!
转发给 RNG 的种子与提供的种子不同,但是,当使用 "normal" 工作流程时,它是可复制的。然后给出可重现的随机数:
dyn.load('urand.so')
RNGkind("user", "user")
#> Received seed: 1844983443
set.seed(123)
#> Received seed: 303482705
runif(10)
#> [1] 0.42061954 0.77097033 0.14981063 0.27065365 0.77665767 0.96882090
#> [7] 0.49077135 0.08621131 0.52903479 0.90398294
set.seed(123)
#> Received seed: 303482705
runif(10)
#> [1] 0.42061954 0.77097033 0.14981063 0.27065365 0.77665767 0.96882090
#> [7] 0.49077135 0.08621131 0.52903479 0.90398294
(请注意,我已将您的 urand.c
稍微更改为使用 R_ext/Print.h
中的 Rprintf
。)
编辑:如果您需要控制种子(为什么?),您可以自己控制:将 user_unif_init
、user_unif_nseed
和 user_unif_seedloc
替换为
void set_seed(int * seed_in) {
Rprintf("Received seed: %u\n", *seed_in);
seed = *seed_in;
}
并显式调用它:
dyn.load('urand.so')
RNGkind("user", "user")
set_seed <- function(seed) {
invisible(.C("set_seed", seed_in = as.integer(seed)))
}
set_seed(123)
#> Received seed: 123
runif(10)
#> [1] 0.00197801 0.61916849 0.34846373 0.04152509 0.09669026 0.29923760
#> [7] 0.04184693 0.32557942 0.44473242 0.22339845
set_seed(123)
#> Received seed: 123
runif(10)
#> [1] 0.00197801 0.61916849 0.34846373 0.04152509 0.09669026 0.29923760
#> [7] 0.04184693 0.32557942 0.44473242 0.22339845
编辑 2:已在 https://svn.r-project.org/R/trunk/src/main/RNG.c:
查看源代码static void RNG_Init(RNGtype kind, Int32 seed)
{
int j;
BM_norm_keep = 0.0; /* zap Box-Muller history */
/* Initial scrambling */
for(j = 0; j < 50; j++)
seed = (69069 * seed + 1);
[...]
造成差异的是这 50 发 LCG 子弹。我的猜测是 R 的作者假设典型的用户提供的种子很小,因此对于种子来说随机性不够。
R 似乎将 RNG.c
中用户提供的种子打乱如下:
for(j = 0; j < 50; j++)
seed = (69069 * seed + 1)
尝试解读这将是找回原始种子的一种方式。
更新
可以通过69069的multiplicative inverse解扰如下:
Int32 unscramble(Int32 scrambled)
{
int j;
Int32 u = scrambled;
for (j=0; j<50; j++) {
u = ((u - 1) * 2783094533);
}
return u;
}
在我的 user_unif_init()
函数中插入这个解决了问题。