出于随机模拟的目的,以下算法是否足以产生与大多数计算机语言中的简单rand()命令相同质量的100万个伪随机十进制数?该算法的前提是使用10个质量十进制伪数,并将它们扩展为100万个质量十进制伪数。
请注意以下内容仅为算法而非实际代码。
double rands[10] = {rand()}; /// initialize a vector of 10 quality pseudorands [0,1]
double expandedRands[1000000] = {0}; /// initialize a vector of size 1 million
for(int i = 0; i < 10; i++)
{
for(double j = 0; j < 100000; j++) /// j goes from zero to one hundred thousand
{
expandedRands[(100000 * i) + j] = rands[i] * abs((j - 0.5)/ 1000000);
}
}
编辑:我意识到人类可以清楚地看到从这个算法产生的数字,并知道它们遵循一种模式,但真正的问题是如果用这些数字而不是100万兰特,随机模拟的工作方式是否相同()数字。
答案 0 :(得分:2)
您的算法不会生成均匀分布。
expandedRands[(100000 * i) + j] = rands[i] * (j / 100000);
首先,对于每个初始随机值?,您将在[0,?]范围内生成100,000个值。这显然会使分布偏向较低的值。
此外,最终数据中的每个值都是从最初的10个值中的一个生成的,并且它们都是均匀间隔的。这会向观察者泄漏相当多的信息,这意味着他们能够在最终数组中猜出更多的值,并且很有可能进行正确的猜测。
大概你需要将10次调用rand()
延伸到1,000,000个质量随机数,因为rand()
非常慢(并希望产生非常好的随机数据作为回报)。在这种情况下我会做的是使用rand()
的结果只不过是一个好的,确定性的pRNG的种子。
一些代码,包括用于实现这个想法的C ++工具:
// initialize a vector of 10 quality pseudorands [0,RAND_MAX]
int rands[10];
for(int i = 0; i < 10; ++i) { rands[i] = rand(); }
std::seed_seq seeds(begin(rands), end(rands));
// seed_seq is from C++ and performs a standard RNG 'warm-up' sequence
// In other languages you'll simply implement a warm-up sequence yourself.
std::mt19937 eng(seeds);
// mt19937 is an implementation of a standard RNG.
// the seed_seq ensures a good initial state for producing random bits
// You can use whatever standard pRNG algorithm meets your quality/performance/size needs
// For example, if you need something faster and with a smaller state you could use a linear congruential engine such as minstd_rand0
std::uniform_real_distribution<double> dist(0.0, 1.0);
// a C++ object which takes random bits and produces random values with a good distribution.
// there are many different algorithms for doing this
double expandedRands[1_000_000];
for(int i = 0; i < 1_000_000; ++j) {
expandedRands[i] = dist(eng);
}
expandedRands
现在包含在[0.0,1.0]范围内均匀分布的一百万个值。给定相同的初始10个随机值,您将获得相同的百万输出值,并且输入中的任何差异都应产生完全不同的输出。
如果您因为需要比rand()
的序列化调用更具可并行性的内容而延长rand()
的结果,那么您可以使用10个rand()
调用生成种子序列,然后使用它来种子几个独立的pRNG引擎,这些引擎可以在不同的核心上运行,也可以在GPGPU内核的独立实例中运行(如果你可以在CUDA中实现pRNG和分发等)。
int rands[10];
for (int i = 0; i < 10; ++i) { rands[i] = rand(); }
std::seed_seq seeds(begin(rands), end(rands));
std::mt19937 eng[10];
for (int i = 0; i < 10; ++i) { eng.seed(seeds); }
// now the engines can be used on independent threads.
P.S。我知道你的代码只是伪代码,但是我在C中看到了一个错误,所以以防万一你因为对C的错误概念这样编写你的代码:
double rands[10] = {rand()};
C中的初始化程序不执行该表达式10次,并使用不同的值初始化每个元素。在C中发生的事情是,当初始化器比数组中的元素少时,那里的初始化器被分配给它们的相应元素(第一个初始化器到第一个元素,第二个初始化器到第二个元素等),然后其余元素初始化为零。例如:
int x[10] = {0};
会将整个数组初始化为零,但是:
int x[10] = {1};
将第一个元素初始化为1,然后将其余元素初始化为零。
答案 1 :(得分:1)
这根本不会产生1,000,000个伪随机数。
您正在通过使用加法,乘法和减法将仅10个“实际”伪随机数的数组扩展为100万个。
最后,你仍然只有10个随机数。
考虑一下,如果系统函数rand()
仅生成二进制值,则为1或0.使{0}填充全零的机会为:(0.5)^ 10,或者约0.098%。
现在使用rands[10]
,您将使用零填充整个100万个数字,因为expandedRands[(100000 * i) + j] = rands[i] * (j / 100000);
为rands[i]
,因此0
为rands[i] * (j / 100000)
。< / p>
如果您确实生成了1,000,000个数字,那么将所有数字作为零的可能性是多少?
(0.5)^ 1000000 = 0.你有更好的机会赢得你甚至没有买过的彩票,甚至一次也不会这样。
答案 2 :(得分:0)
随着j变得越来越大,你将以质量随机数i(j / 100000 = 1)结束。
尝试使用Excel中的图形绘制它,您将清楚地看到您收敛到随机数。