【问题标题】:Why do these RNG's in C++ and R not produce similar results?为什么 C++ 和 R 中的这些 RNG 不会产生相似的结果?
【发布时间】:2013-07-16 16:03:07
【问题描述】:

请原谅这篇文章令人作呕的愚蠢性质,但我有一个问题要问那些在个人计算机上使用 C++ 和 R 编程的人。

问题:为什么下面两个程序产生的这些随机数不相等,我该如何解决这个问题?

  1. 首先,我怀疑我在R程序中误用了local函数和<<-运算符。
  2. 其次,我怀疑这可能是浮动精度问题。对我来说,这两个程序有什么不同并不是很明显,所以我不知道如何解决这个问题。

我尝试将我在 C++ 中的所有计算转换为 double/float(甚至是 long double),并使用 fmod 而不是模运算符 %:再次输出不同,但仍然不类似于R 中的输出。我不知道它是否重要,但我想补充一点,我正在使用 G++ 编译器编译 C++ 代码。

算法:以下算法可用于任何标准个人计算机。建议并行使用三个词生成器,

  • mk = 171 mk-1 (mod 30269)
  • m'k = 172 m'k-1 (mod 30307)
  • m''k = 172 m''k-1 (mod 30323)

并将小数部分用作伪随机数

  • gk = {mk / 30269 + m'k / 30307 + m''k / 30323}

我使用了初始值m0 = 5,m'0 = 11,m''0 = 17.

程序:我有以下 C++ 程序:

//: MC:Uniform.cpp
// Generate pseudo random numbers uniformly between 0 and 1
#include <iostream>
#include <math.h> // For using "fmod()"
using namespace std;

float uniform(){
    // A sequence of initial values
    static int x = 5;
    static int y = 11;
    static int z = 17;

    // Some integer arithmetic required
    x = 171 * (x % 177) - 2 * (x / 177);
    y = 172 * (x % 176) - 35 * (y / 176);
    z = 170 * (x % 178) - 63 * (z / 178);

    /* If both operands are nonnegative then the
    remainder is nonnegative; if not, the sign of
    the remainder is implementation-defined. */
    if(x < 0)
        x = x + 30269;
    if(y < 0)
        y = y + 30307;
    if(z < 0)
        z = z + 30323;

    return fmod(x / 30269. + y / 30307. + z / 30323., 1.);
}

int main(){
    // Print 5 random numbers
    for(int i = 0; i < 5; i++){
        cout << uniform() << ", ";
    }               
}///:~

程序以代码退出并输出以下内容:

0.686912, 0.329174, 0.689649, 0.753722, 0.209394,

我在 R 中也有一个程序,如下所示:

## Generate pseudo random numbers uniformly between 0 and 1
uniform <- local({
    # A sequence of initial values
    x = 5
    y = 11
    z = 17

    # Use the <<- operator to make x, y and z local static 
    # variables in R.
    f <- function(){
        x <<- 171 * (x %% 177) - 2 * (x / 177)
        y <<- 172 * (y %% 176) - 35 * (y / 176)
        z <<- 170 * (z %% 178) - 63 * (z / 178)

        return((x / 30269. + y / 30307. + z / 30323.)%%1.)
    }
})

# Print 5 random numbers
for(i in 1:5){
    print(uniform())
}

这个程序也以代码退出并产生输出

[1] 0.1857093
[1] 0.7222047
[1] 0.05103441
[1] 0.7375034
[1] 0.2065817

任何建议都非常感谢,在此先感谢。

【问题讨论】:

  • 作为一种完全不同的部署路径而不是编程练习,请注意,您也可以轻松地使用来自 C/C++ 的 R RNG。 R 提供了用于从外部程序链接的“libRMath”库; “编写 R 扩展”告诉你如何在 C 代码中完成它,而 Rcpp 使它在 C++ 中变得微不足道。这样,您就保证获得相同的 RNG 流。
  • 太棒了。谢谢。
  • +1 是清楚的例子,对于愚蠢的复制粘贴错误没有减分。我们都做了太多这样的事情:-)
  • 值得一提的是,您的 RNG 被称为 Wichmann-Hill 算法。 here 提供了多种编程语言的文档和资源,原始论文可通过 JSTOR 上的免费帐户 here 获得。

标签: c++ r random


【解决方案1】:

您的 R 代码中还需要一些 %/%(整数除法)。请记住,R 中的数值变量默认是浮点数,而不是整数;所以/ 将用非整数商进行普通除法。您还省略了处理负面x/y/z 的部分。

f <- function(){
    x <<- 171 * (x %% 177) - 2 * (x %/% 177)
    y <<- 172 * (y %% 176) - 35 * (y %/% 176)
    z <<- 170 * (z %% 178) - 63 * (z %/% 178)

    if(x < 0)
        x <<- x + 30269;
    if(y < 0)
        y <<- y + 30307;
    if(z < 0)
        z <<- z + 30323;

    return((x / 30269. + y / 30307. + z / 30323.)%%1)
}

进行这些更改后,结果似乎没有任何严重错误。 100000 次随机抽取的快速直方图看起来非常均匀,而且我找不到任何自相关。仍然与您的 C++ 结果不匹配....

【讨论】:

  • 感谢您指出 R 中整数除法的部分。
【解决方案2】:

您的 C++ 代码中有一个简单的复制/粘贴错误。这个

x = 171 * (x % 177) -  2 * (x / 177);
y = 172 * (x % 176) - 35 * (y / 176);
z = 170 * (x % 178) - 63 * (z / 178);

应该是这个。

x = 171 * (x % 177) -  2 * (x / 177);
y = 172 * (y % 176) - 35 * (y / 176);
z = 170 * (z % 178) - 63 * (z / 178);

【讨论】:

  • 很棒的地方,但这给了我 0.185982、0.769997、0.20492、0.528218、0.813943,
  • 你做了@HongOoi 的修复吗?我没有运行 C++ 代码,但这就是我用 R 得到的。
  • 啊不......只是C++......我会收回我的智慧
  • 这是问题的主要原因,感谢您发现它。再加上 Hong Ooi 关于整数除法的观点,现在的代码就像一个魅力。附带说明:C++ 代码中的简单复制/粘贴错误实际上是在 R 代码之前编写的...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-11
  • 2022-11-21
  • 2016-11-13
  • 2011-05-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多