我想创建一个类的许多实例,并希望它们使用boost随机数生成器来生成具有用户定义平均值的正态分布式跳转。我从一些消息来源读到他们说你不想像here那样重新编号。理想情况下,我想要一个全局生成器,并且每个类的每个实例都能够更改均值并生成一个对所有实例都不相同的随机数。我正在努力实现这个我有一个全局普通类,但种子对于每个类的实例是相同的。
import csv
entries = {}
with open('namesFile.txt') as f:
for x, name, y in csv.reader(f):
name = name.strip()
entries[name] = [int(x), name, float(y)]
这是您编译上述代码时得到的输出。
// C/C++ standard library
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/variate_generator.hpp>
#include <boost/random/lognormal_distribution.hpp>
/**
* The mt11213b generator is fast and has a reasonable cycle length
* See http://www.boost.org/doc/libs/1_60_0/doc/html/boost_random/reference.html#boost_random.reference.generators
*/
struct random_generator : boost::mt11213b {
random_generator(void){
seed(static_cast<unsigned int>(std::time(0)));
}
} random_generator;
template<
class Type
> struct Distribution {
Type distribution;
boost::variate_generator<decltype(random_generator),Type> variate_generator;
template<class... Args>
Distribution(Args... args):
variate_generator(random_generator,Type(args...)) {}
double random(void) {
return variate_generator();
}
};
typedef Distribution< boost::normal_distribution<> > Normal;
using namespace std;
// global normal random number generator
Normal normal_random_generator;
// Class Individual
class Individual {
public:
Individual() { } // constructor initialise value
virtual~Individual() = default;
// an accessor to pass information back
void move_bias_random_walk(double mu) {
normal_random_generator = {mu, sigma_};
distance_ += normal_random_generator.random();
}
// An accessor for the distance object
double get_distance() {
return distance_;
}
private:
//containers
double distance_ = 0.4;
double sigma_ = 0.4;
};
int main() {
cout << "!!!Begin!!!" << endl;
// Initialise two individuals in this case but there could be thousands
Individual individual_a;
Individual individual_b;
cout << "starting values: individual a = " << individual_a.get_distance() << " individual b = " << individual_b.get_distance() << endl;
// Do 10 jumps with the same mean for each individual and see where they end up each time
cout << "A\tB" << endl;
for (auto i = 1; i <= 10; ++i) {
double mean = rand();
individual_a.move_bias_random_walk(mean);
individual_b.move_bias_random_walk(mean);
cout << individual_a.get_distance() << "\t" << individual_b.get_distance() << endl;
}
cout << "finished" << endl;
system("PAUSE");
return 0;
}
答案 0 :(得分:3)
我试图想出一个能够实现我想要实现的目标的例子。我希望你不介意我摆脱了提升,因为我没有理由在这里使用它。
希望这会有所帮助:
// C/C++ standard library
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <random>
// Class Individual
class Individual
{
public:
// an accessor to pass information back
void move_bias_random_walk( double mu, double sigma = 0.4 )
{
distance_ += std::normal_distribution<double>{mu, sigma}( myEngine );
}
// An accessor for the distance object
double get_distance() { return distance_; }
private:
// containers
double distance_ = 0.4;
static std::mt19937 myEngine;
};
// initialize static random engine to be shared by all instances of the Inidvidual class
auto Individual::myEngine = std::mt19937( std::time( 0 ) );
int main()
{
std::cout << "!!!Begin!!!" << std::endl;
// Initialise two individuals in this case but there could be thousands
Individual individual_a{};
Individual individual_b{};
std::cout << "starting values: individual a = " << individual_a.get_distance()
<< " individual b = " << individual_b.get_distance() << std::endl;
// Do 10 jumps with the same mean for each individual and see where they end up each time
std::cout << "A\tB" << std::endl;
// let's not use rand()
std::default_random_engine eng{1337};
std::uniform_real_distribution<double> uniformMean{-10,10};
for ( auto i = 1; i <= 10; ++i ) {
double mean = uniformMean(eng);
individual_a.move_bias_random_walk( mean );
individual_b.move_bias_random_walk( mean );
std::cout << individual_a.get_distance() << " " << individual_b.get_distance() << std::endl;
}
std::cout << "finished" << std::endl;
return 0;
}
打印:
!!!Begin!!!
starting values: individual a = 0.4 individual b = 0.4
A B
8.01456 7.68829
2.53383 1.19675
7.06496 5.74414
9.60985 9.04333
12.4008 13.4647
11.2468 13.4128
6.02199 8.24547
0.361428 2.85905
-3.28938 -1.59109
-5.99163 -4.37436
finished
答案 1 :(得分:2)
就像Christoph评论的那样,如果你复制发电机引擎的状态,你就会有两台具有相同状态的引擎。
所以在副本之后播种引擎:
template<class... Args>
Distribution(Args... args):
variate_generator(random_generator,Type(args...)) {
boost::random::random_device dev;
variate_generator.engine().seed(dev);
}
请注意random_device
播种是多么可取。这可以确保种子本身是随机的,也可以确保引擎的整个状态都是种子。
如果您不想链接到Boost Random,可以再次使用单个种子值:
template<class... Args>
Distribution(Args... args):
variate_generator(random_generator,Type(args...)) {
std::random_device dev;
variate_generator.engine().seed(dev());
}
当你这样做时
normal_random_generator = {mu, sigma_};
您正在替换全局Distribution
实例,并将mu
设置为您从main获得的值。由于你(ab)在那里使用rand()
,mu
只会是一些完全不随机和大的价值。在我的系统上,它总是
1804289383
846930886
1681692777
1714636915
1957747793
424238335
719885386
1649760492
596516649
1189641421
相比之下,您的发行版的sigma非常小,因此您生成的值将接近原始值,并且数字的科学格式将隐藏任何差异:
!!!Begin!!!
starting values: individual a = 0.4 individual b = 0.4
A B
1.80429e+09 1.80429e+09
2.65122e+09 2.65122e+09
4.33291e+09 4.33291e+09
6.04755e+09 6.04755e+09
8.0053e+09 8.0053e+09
8.42954e+09 8.42954e+09
9.14942e+09 9.14942e+09
1.07992e+10 1.07992e+10
1.13957e+10 1.13957e+10
1.25853e+10 1.25853e+10
finished
看起来两列的值都相同。但是,它们基本上只是rand()
的输出,但变化很小。添加
std::cout << std::fixed;
显示存在差异:
!!!Begin!!!
starting values: individual a = 0.4 individual b = 0.4
A B
1804289383.532134 1804289383.306165
2651220269.054946 2651220269.827112
4332913046.416999 4332913046.791281
6047549960.973747 6047549961.979666
8005297753.938927 8005297755.381466
8429536088.122741 8429536090.737263
9149421474.458202 9149421477.268963
10799181966.514246 10799181969.109875
11395698614.754076 11395698617.892900
12585340035.563337 12585340038.882833
finished
总而言之,我建议
rand()
和/或为mean
此外,我建议使用全局变量从不。事实上,每次在这里创建一个Normal
的新实例:
normal_random_generator = {mu, sigma_};
我没有看到用该实例覆盖全局变量可能有什么价值。它只会降低效率。所以,这是完全相同和更有效的:
void move_bias_random_walk(double mu) {
Normal nrg {mu, sigma_};
distance_ += nrg.random();
}
了解您的发行版的Sigma,因此您可以预测所需数字的差异。
<强> Live On Coliru 强>
// C/C++ standard library
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/variate_generator.hpp>
#include <boost/random/lognormal_distribution.hpp>
#include <boost/random/random_device.hpp>
/**
* The mt11213b generator is fast and has a reasonable cycle length
* See http://www.boost.org/doc/libs/1_60_0/doc/html/boost_random/reference.html#boost_random.reference.generators
*/
typedef boost::mt11213b Engine;
boost::random::random_device random_device;
template<
class Type
> struct Distribution {
boost::variate_generator<Engine, Type> variate_generator;
template<class... Args>
Distribution(Args... args):
variate_generator(Engine(random_device()), Type(args...)) {
//variate_generator.engine().seed(random_device);
//std::cout << "ctor test: " << variate_generator.engine()() << "\n";
}
double random(void) {
double v = variate_generator();
//std::cout << "debug: " << v << "\n";
return v;
}
};
typedef Distribution< boost::normal_distribution<> > Normal;
// Class Individual
class Individual {
public:
Individual() { } // constructor initialise value
virtual ~Individual() = default;
// an accessor to pass information back
void move_bias_random_walk(double mu) {
Normal nrg {mu, sigma_};
distance_ += nrg.random();
}
// An accessor for the distance object
double get_distance() {
return distance_;
}
private:
//containers
double distance_ = 0.4;
double sigma_ = 0.4;
};
int main() {
std::cout << std::fixed;
std::cout << "!!!Begin!!!" << std::endl;
// Initialise two individuals in this case but there could be thousands
Individual individual_a;
Individual individual_b;
std::cout << "starting values: individual a = " << individual_a.get_distance() << " individual b = " << individual_b.get_distance() << std::endl;
// Do 10 jumps with the same mean for each individual and see where they end up each time
std::cout << "A\tB" << std::endl;
for (auto i = 1; i <= 10; ++i) {
double mean = rand()%10;
//std::cout << "mean: " << mean << "\n";
individual_a.move_bias_random_walk(mean);
individual_b.move_bias_random_walk(mean);
std::cout << individual_a.get_distance() << "\t" << individual_b.get_distance() << std::endl;
}
std::cout << "finished" << std::endl;
}
打印
!!!Begin!!!
starting values: individual a = 0.400000 individual b = 0.400000
A B
3.186589 3.754065
9.341219 8.984621
17.078740 16.054461
21.787808 21.412336
24.896861 24.272279
29.801920 29.090233
36.134987 35.568845
38.228595 37.365732
46.833353 46.410176
47.573564 47.194575
finished
以下内容完全相同,但效率更高:
<强> Live On Coliru 强>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/random_device.hpp>
#include <iostream>
/**
* The mt11213b generator is fast and has a reasonable cycle length
* See http://www.boost.org/doc/libs/1_60_0/doc/html/boost_random/reference.html#boost_random.reference.generators
*/
typedef boost::mt11213b Engine;
template <typename Distribution>
class Individual {
public:
Individual(Engine& engine) : engine_(engine) { }
// an accessor to pass information back
void move_bias_random_walk(double mu) {
Distribution dist { mu, sigma_ };
distance_ += dist(engine_);
}
// An accessor for the distance object
double get_distance() {
return distance_;
}
private:
Engine& engine_;
//containers
double distance_ = 0.4;
double sigma_ = 0.4;
};
int main() {
boost::random::random_device device;
Engine engine(device);
std::cout << std::fixed;
std::cout << "!!!Begin!!!" << std::endl;
// Initialise two individuals in this case but there could be thousands
Individual<boost::normal_distribution<> > individual_a(engine);
Individual<boost::normal_distribution<> > individual_b(engine);
std::cout << "starting values: individual a = " << individual_a.get_distance() << " individual b = " << individual_b.get_distance() << std::endl;
// Do 10 jumps with the same mean for each individual and see where they end up each time
std::cout << "A\tB" << std::endl;
for (auto i = 1; i <= 10; ++i) {
double mean = rand()%10;
individual_a.move_bias_random_walk(mean);
individual_b.move_bias_random_walk(mean);
std::cout << individual_a.get_distance() << "\t" << individual_b.get_distance() << std::endl;
}
std::cout << "finished" << std::endl;
}
请注意
Engine
个实例