如何设计一个带有可选随机种子参数的函数以传递给 mt19937
How to design a function with an optional random seed argument to be passed to mt19937
在 R 中,我可以构建以下 roll_die(seed = NULL)
函数,该函数 returns 一个介于 1 和 6 之间的随机整数,它允许 指定种子的选项 对于 RNG。
roll_die_r <- function(seed = NULL){
# Returns a random number between 1 and 6
# Optionally specify a RNG seed
set.seed(seed)
return(sample(x = 1:6, size = 1L))
}
这很好,因为我可以使用默认 seed = NULL
调用它并返回一个随机值 或 我可以使用指定的种子值调用它,这样我可以获得可重现的结果。
roll_die_r() # random
roll_die_r(seed = 0) # always returns 6
如何使用 mt19937 在 C++ 中实现相同的功能?我能想到的最好的是
#include <Rcpp.h>
#include <random>
using namespace Rcpp;
// [[Rcpp::plugins(cpp11)]]
// [[Rcpp::export]]
int roll_die_cpp(int seed = -1){
// Returns a random integer between 1 and 6
// Optionally specify a RNG seed
std::mt19937 mt;
// Seed the RNG
if(seed == -1) seed = std::random_device{}();
mt.seed(seed);
std::uniform_int_distribution<int> dist(1, 6);
int result = dist(mt);
return result;
}
但这并不理想,因为用户可能不小心 call roll_die_cpp(seed = -1)
并期望得到可重现的结果,但事实并非如此。
roll_die_cpp() # random
roll_die_cpp(seed = 0) # always returns 5
roll_die_cpp(seed = -1) # random
我的问题不是专门针对 roll_die()
方法或随机数生成器 - 它更多的是关于函数设计。在 R 中,我经常使用默认参数设置为 NULL 的函数,但我不知道如何在 C++ 中完成同样的事情。
更新:
这是我正在处理的另一个例子。
R函数
return_0 <- function(msg = NULL){
if(!is.null(msg)) print(msg)
return(0L)
}
return_0() # doesn't print a message
return_0("hello world") # prints hello world
cpp 函数
// [[Rcpp::export]]
int return_0_cpp(std::string msg = "don't print"){
if(msg != "don't print") Rcpp::Rcout << msg;
return(0);
}
return_0_cpp() # doesn't print a message
return_0_cpp(msg = "hello world") # prints hello world
return_0_cpp(msg = "don't print") # doesn't print a message
请注意 return_0_cpp()
有多尴尬。在 cpp 中执行我在 R 中创建的内容的干净方法是什么?
In R I often use functions with default parameters set to NULL but I don't know how
to accomplish the same thing in c++.
std::optional
(C++17 起) 用于可选值:
#include <iostream>
#include <optional>
void fun(std::optional<int> v = std::nullopt) {
if (v) {
std::cout << "value passed = " << v.value();
} else {
std::cout << "no value passed";
}
}
int main(){
fun();
fun(4);
}
作为旁注:根据传递的参数数量,我会小心地让同一个函数做两件不同的事情。有人可能会争辩说
dice.seed(0);
auto x = dice.roll();
比
更明确和可读
auto x = dice.roll(0);
我经常使用 header 唯一的库,它可以像这样工作(大大简化):
namespace ran {
inline std::mt19937& generator()
{
thread_local static std::mt19937 mt{std::random_device{}()};
return mt;
}
template<typename Integral>
void seed(Integral n)
{
generator().seed(std::mt19937::result_type(n));
}
template<typename Integral>
Integral number(Integral min, Integral max)
{
using dist_type = typename std::uniform_int_distribution<Integral>;
thread_local static dist_type dist;
return dist(generator(), typename dist_type::param_type(min, max));
}
} // namespace ran
使用thread_local static
确保线程安全,同时保持性能。它重复使用相同的随机数生成器,在开始时只为它播种一次,或者您可以随时 re-seed 它使用特定值。
int main()
{
for(auto i = 0; i < 10; ++i)
std::cout << ran::number(3, 9) << ' ';
std::cout << '\n';
ran::seed(5);
for(auto i = 0; i < 10; ++i)
std::cout << ran::number(3, 9) << ' ';
std::cout << '\n';
}
在 R 中,我可以构建以下 roll_die(seed = NULL)
函数,该函数 returns 一个介于 1 和 6 之间的随机整数,它允许 指定种子的选项 对于 RNG。
roll_die_r <- function(seed = NULL){
# Returns a random number between 1 and 6
# Optionally specify a RNG seed
set.seed(seed)
return(sample(x = 1:6, size = 1L))
}
这很好,因为我可以使用默认 seed = NULL
调用它并返回一个随机值 或 我可以使用指定的种子值调用它,这样我可以获得可重现的结果。
roll_die_r() # random
roll_die_r(seed = 0) # always returns 6
如何使用 mt19937 在 C++ 中实现相同的功能?我能想到的最好的是
#include <Rcpp.h>
#include <random>
using namespace Rcpp;
// [[Rcpp::plugins(cpp11)]]
// [[Rcpp::export]]
int roll_die_cpp(int seed = -1){
// Returns a random integer between 1 and 6
// Optionally specify a RNG seed
std::mt19937 mt;
// Seed the RNG
if(seed == -1) seed = std::random_device{}();
mt.seed(seed);
std::uniform_int_distribution<int> dist(1, 6);
int result = dist(mt);
return result;
}
但这并不理想,因为用户可能不小心 call roll_die_cpp(seed = -1)
并期望得到可重现的结果,但事实并非如此。
roll_die_cpp() # random
roll_die_cpp(seed = 0) # always returns 5
roll_die_cpp(seed = -1) # random
我的问题不是专门针对 roll_die()
方法或随机数生成器 - 它更多的是关于函数设计。在 R 中,我经常使用默认参数设置为 NULL 的函数,但我不知道如何在 C++ 中完成同样的事情。
更新: 这是我正在处理的另一个例子。
R函数
return_0 <- function(msg = NULL){
if(!is.null(msg)) print(msg)
return(0L)
}
return_0() # doesn't print a message
return_0("hello world") # prints hello world
cpp 函数
// [[Rcpp::export]]
int return_0_cpp(std::string msg = "don't print"){
if(msg != "don't print") Rcpp::Rcout << msg;
return(0);
}
return_0_cpp() # doesn't print a message
return_0_cpp(msg = "hello world") # prints hello world
return_0_cpp(msg = "don't print") # doesn't print a message
请注意 return_0_cpp()
有多尴尬。在 cpp 中执行我在 R 中创建的内容的干净方法是什么?
In R I often use functions with default parameters set to NULL but I don't know how to accomplish the same thing in c++.
std::optional
(C++17 起) 用于可选值:
#include <iostream>
#include <optional>
void fun(std::optional<int> v = std::nullopt) {
if (v) {
std::cout << "value passed = " << v.value();
} else {
std::cout << "no value passed";
}
}
int main(){
fun();
fun(4);
}
作为旁注:根据传递的参数数量,我会小心地让同一个函数做两件不同的事情。有人可能会争辩说
dice.seed(0);
auto x = dice.roll();
比
更明确和可读auto x = dice.roll(0);
我经常使用 header 唯一的库,它可以像这样工作(大大简化):
namespace ran {
inline std::mt19937& generator()
{
thread_local static std::mt19937 mt{std::random_device{}()};
return mt;
}
template<typename Integral>
void seed(Integral n)
{
generator().seed(std::mt19937::result_type(n));
}
template<typename Integral>
Integral number(Integral min, Integral max)
{
using dist_type = typename std::uniform_int_distribution<Integral>;
thread_local static dist_type dist;
return dist(generator(), typename dist_type::param_type(min, max));
}
} // namespace ran
使用thread_local static
确保线程安全,同时保持性能。它重复使用相同的随机数生成器,在开始时只为它播种一次,或者您可以随时 re-seed 它使用特定值。
int main()
{
for(auto i = 0; i < 10; ++i)
std::cout << ran::number(3, 9) << ' ';
std::cout << '\n';
ran::seed(5);
for(auto i = 0; i < 10; ++i)
std::cout << ran::number(3, 9) << ' ';
std::cout << '\n';
}