在R中为用户提供的随机数生成器播种

时间:2017-12-15 01:13:45

标签: r random r-extension

我在用R播种用户定义的RNG时遇到了一些麻烦。似乎

set.seed(123, kind='user', normal.kind='user')

实际上并未将123传递给用户定义的RNG初始化。

我回到了?Random.user提供的文档,并尝试了那里给出的示例代码,稍作修改,我打印传递给user_unif_init函数的种子(下面的完整代码)。

重现的步骤:

  1. urand.c
  2. 中粘贴以下代码
  3. 运行R CMD SHLIB urand.c
  4. 打开R
  5. 运行以下命令:

    > dyn.load('urand.so')
    > set.seed(123, kind='user', normal.kind='user')
    Received seed: 720453763
    Received seed: 303482705 // any other numbers than 123
    
  6. 这是我在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;
    }
    

    非常感谢任何帮助!

2 个答案:

答案 0 :(得分:2)

似乎R在for(j = 0; j < 50; j++) seed = (69069 * seed + 1) 中对用户提供的种子进行加密,如下所示:

Int32 unscramble(Int32 scrambled)
{
        int j;
        Int32 u = scrambled;
        for (j=0; j<50; j++) {
                u = ((u - 1) * 2783094533);
        }
        return u;
}

link to source

尝试解读这将是一种获取原始种子的方法。

<强>更新

解密可以通过69069的multiplicative inverse完成,如下所示:

user_unif_init()

在我的{{1}}函数中插入此函数可以解决问题。

答案 1 :(得分:1)

转发给RNG的种子与提供的种子不同,但是,当正常的种子时,它是可重现的。使用工作流程。然后,这给出了可重现的随机数:

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略微更改为使用Rprintf中的R_ext/Print.h。)

编辑:如果你需要控制种子(为什么?),你可以自己做:用{/ 1>替换user_unif_inituser_unif_nseeduser_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的作者假设典型的用户提供的种子很小,因此对种子来说不够随机。