我正在开发一个类项目,并且正在使用我的教科书中的代码在我的代码中生成一个随机数。但是,每次运行代码时,无论如何都会得到相同的输出,这让我感到困惑,所以我猜我实际上并不了解随机数生成器的工作原理。代码如下 - 它附带说明,但我不确定每次运行代码时我应该做什么来生成随机结果?
/* Prime modulus multiplicative linear congruential generator
Z[i] = (630360016 * Z[i-1]) (mod(pow(2,31) - 1)), based on Marse and Roberts'
portable FORTRAN random-number generator UNIRAN. Multiple (100) streams are
supported, with seeds spaced 100,000 apart. Throughout, input argument
"stream" must be an int giving the desired stream number. The header file
lcgrand.h must be included in the calling program (#include "lcgrand.h")
before using these functions.
Usage: (Three functions)
1. To obtain the next U(0,1) random number from stream "stream," execute
u = lcgrand(stream);
where lcgrand is a float function. The float variable u will contain the
next random number.
2. To set the seed for stream "stream" to a desired value zset, execute
lcgrandst(zset, stream);
where lcgrandst is a void function and zset must be a long set to the
desired seed, a number between 1 and 2147483646 (inclusive). Default
seeds for all 100 streams are given in the code.
3. To get the current (most recently used) integer in the sequence being
generated for stream "stream" into the long variable zget, execute
zget = lcgrandgt(stream);
where lcgrandgt is a long function. */
/* Define the constants. */
#define MODLUS 2147483647
#define MULT1 24112
#define MULT2 26143
/* Set the default seeds for all 100 streams. */
static long zrng[] =
{ 1,
1973272912, 281629770, 20006270,1280689831,2096730329,1933576050,
913566091, 246780520,1363774876, 604901985,1511192140,1259851944,
824064364, 150493284, 242708531, 75253171,1964472944,1202299975,
233217322,1911216000, 726370533, 403498145, 993232223,1103205531,
762430696,1922803170,1385516923, 76271663, 413682397, 726466604,
336157058,1432650381,1120463904, 595778810, 877722890,1046574445,
68911991,2088367019, 748545416, 622401386,2122378830, 640690903,
1774806513,2132545692,2079249579, 78130110, 852776735,1187867272,
1351423507,1645973084,1997049139, 922510944,2045512870, 898585771,
243649545,1004818771, 773686062, 403188473, 372279877,1901633463,
498067494,2087759558, 493157915, 597104727,1530940798,1814496276,
536444882,1663153658, 855503735, 67784357,1432404475, 619691088,
119025595, 880802310, 176192644,1116780070, 277854671,1366580350,
1142483975,2026948561,1053920743, 786262391,1792203830,1494667770,
1923011392,1433700034,1244184613,1147297105, 539712780,1545929719,
190641742,1645390429, 264907697, 620389253,1502074852, 927711160,
364849192,2049576050, 638580085, 547070247 };
/* Generate the next random number. */
float lcgrand(int stream)
{
long zi, lowprd, hi31;
zi = zrng[stream];
lowprd = (zi & 65535) * MULT1;
hi31 = (zi >> 16) * MULT1 + (lowprd >> 16);
zi = ((lowprd & 65535) - MODLUS) +
((hi31 & 32767) << 16) + (hi31 >> 15);
if (zi < 0) zi += MODLUS;
lowprd = (zi & 65535) * MULT2;
hi31 = (zi >> 16) * MULT2 + (lowprd >> 16);
zi = ((lowprd & 65535) - MODLUS) +
((hi31 & 32767) << 16) + (hi31 >> 15);
if (zi < 0) zi += MODLUS;
zrng[stream] = zi;
return (zi >> 7 | 1) / 16777216.0;
}
void lcgrandst (long zset, int stream) /* Set the current zrng for stream
"stream" to zset. */
{
zrng[stream] = zset;
}
long lcgrandgt (int stream) /* Return the current zrng for stream "stream". */
{
return zrng[stream];
}
编辑:我忘了实际包含运行这些功能的代码,它是这样的:
float Expon(float mean) /* Exponential variate generation function. */
{
/* Return an exponential random variate with mean "mean". */
return -mean * log(lcgrand(1));
}
基于我有限的理解,我认为我的问题来自于lcgrand(1)
,但我并不完全确定。
答案 0 :(得分:3)
计算机通常无法生成真正的随机数,因为它们被设计为确定性地运行,这基本上与随机数相反。由于程序控制所有输入,并且程序不会改变,因此输出不能是真正随机的。这就是为什么许多计算机生成的随机数被称为伪随机而不是随机的。
然而,尽管生成的数字不能真正随机,但是每次运行程序时,通过引入一些每次运行程序时都会发生变化的变量,可以使它们更难以预测并获得不同的序列。该变量称为“种子”。许多程序将使用计算机时钟的值种子他们的随机数生成器,或者来自其他不可预测的东西,如用户提供的鼠标移动的鼠标坐标序列,以及鼠标移动之间的时间间隔
查看您提供的代码,请注意种子都被列为“默认种子”,这意味着如果您不希望每个流都提供相同的结果,则应该覆盖特定流的种子你运行程序的时间。
您可能认为这是一个提供可以直接从鼠标或时钟直接检索的值的代码。所有这些代码的要点是尽可能让某人难以预测序列中的下一个数字将被过去的结果抽样(毕竟任何拥有相同随机数生成器代码的人都可以尝试匹配部分他们的序列与你的序列一起预测接下来会发生什么,这就是随机数发生器通常试图阻止的事情。所以代码必须是非平凡的,以减少其他人用它来预测下一个数字的可能性。
在运行时使用通过时钟或鼠标生成的种子是阻止值可预测的一种方法。但通常不断提供随机种子是不切实际的。并且因为随机种子有时可能基于非随机源(如时钟),您可能最终只能在这些值中获得一些可预测性。因此,由于种子数据的大多数来源并非真正随机,因此您必须花费一些时间使其尽可能随机且不可预测。通过组合不同的种子值和代码,根据大量难以复制的输入生成数字,您可以快速获得真正随机数的近似值。
由于这是针对某个课程,我将留给您阅读有关如何设置种子的使用点编号2的注释。
答案 1 :(得分:0)
我通过以下方式运行您的代码:
#include <stdio.h> // printf()
#include <math.h> // logf()
#define MODLUS 2147483647
#define MULT1 24112
#define MULT2 26143
/* Set the default seeds for all 100 streams. */
static long zrng[] =
{ 1,
1973272912, 281629770, 20006270,1280689831,2096730329,1933576050,
913566091, 246780520,1363774876, 604901985,1511192140,1259851944,
824064364, 150493284, 242708531, 75253171,1964472944,1202299975,
233217322,1911216000, 726370533, 403498145, 993232223,1103205531,
762430696,1922803170,1385516923, 76271663, 413682397, 726466604,
336157058,1432650381,1120463904, 595778810, 877722890,1046574445,
68911991,2088367019, 748545416, 622401386,2122378830, 640690903,
1774806513,2132545692,2079249579, 78130110, 852776735,1187867272,
1351423507,1645973084,1997049139, 922510944,2045512870, 898585771,
243649545,1004818771, 773686062, 403188473, 372279877,1901633463,
498067494,2087759558, 493157915, 597104727,1530940798,1814496276,
536444882,1663153658, 855503735, 67784357,1432404475, 619691088,
119025595, 880802310, 176192644,1116780070, 277854671,1366580350,
1142483975,2026948561,1053920743, 786262391,1792203830,1494667770,
1923011392,1433700034,1244184613,1147297105, 539712780,1545929719,
190641742,1645390429, 264907697, 620389253,1502074852, 927711160,
364849192,2049576050, 638580085, 547070247 };
/* Generate the next random number. */
float lcgrand(int stream)
{
long zi, lowprd, hi31;
zi = zrng[stream];
lowprd = (zi & 65535) * MULT1;
hi31 = (zi >> 16) * MULT1 + (lowprd >> 16);
zi = ((lowprd & 65535) - MODLUS) +
((hi31 & 32767) << 16) + (hi31 >> 15);
if (zi < 0) zi += MODLUS;
lowprd = (zi & 65535) * MULT2;
hi31 = (zi >> 16) * MULT2 + (lowprd >> 16);
zi = ((lowprd & 65535) - MODLUS) +
((hi31 & 32767) << 16) + (hi31 >> 15);
if (zi < 0) zi += MODLUS;
zrng[stream] = zi;
return (float)(zi >> 7 | 1) / 16777216.0f; // note cast to `float` and changing `6777216.0` from double to float
}
void lcgrandst (long zset, int stream) /* Set the current zrng for stream
"stream" to zset. */
{
zrng[stream] = zset;
}
long lcgrandgt (int stream) /* Return the current zrng for stream "stream". */
{
return zrng[stream];
}
int main( void )
{
float result = 0.0f;
for( size_t i=0; i<20; i++ )
{
result = lcgrand(1);
result = logf( result ); // note change from `log()` to `logf()`
printf( "%f\n", result );
}
}
结果是:
-0.835640
-0.959694
-0.680949
-0.343732
-1.686887
-0.181990
-0.436622
-0.979359
-1.054951
-0.269755
-0.807537
-0.094184
-0.745967
-0.745367
-0.159959
-0.217918
-0.324280
-0.357220
所以发布的代码需要一两个小调整才能正常工作
因此,您的应用程序问题不在发布的代码中