我试图编写一个程序,给出相同的结果,要么完全执行,要么从一些检查点停止和重新启动。要做到这一点,我需要能够在任何情况下重复相同的随机数序列。这里有一段代码,我试着这样做,当然,我没有成功。你能帮我修复这个代码吗?
int main(){
int i;
long int seed;
// Initial seed
srand48(3);
// Print 5 random numbers
for(i=0;i<5;i++) printf("%d %fn",i,drand48());
// CHECKPOINT: HOW TO PROPERLY SET seed?
seed=mrand48(); // <--- FIXME
// 5 numbers more
for(i=5;i<10;i++) printf("%d %fn",i,drand48());
// Restart from the CHECKPOINT.
srand48(seed);
// Last 5 numbers again
for(i=5;i<10;i++) printf("%d %fn",i,drand48());
}
如果您需要能够恢复随机数序列,那么您不能让drand48()
包对您隐藏种子值,因此您需要使用包中的不同函数。具体来说,您应该调用:
double erand48(unsigned short xsubi[3]);
代替:
double drand48(void);
,你将保持一个3个unsigned short
值的数组,在每个检查点,你将记录它们的值作为状态的一部分。如果您需要从停止的地方恢复,您将从保存状态恢复值到您的数组中,然后继续您的快乐之路。
这也是你编写库代码的方式,既不干扰使用随机数生成器的其他代码,也不被使用随机数生成器的其他代码干扰。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
unsigned short seed[3] = { 0, 0, 3 };
// Print 5 random numbers
for (int i = 0; i < 5; i++)
printf("%d %fn", i, erand48(seed));
// CHECKPOINT
unsigned short saved[3];
memmove(saved, seed, sizeof(seed));
// 5 numbers more
for (int i = 5; i < 10; i++)
printf("%d %fn", i, erand48(seed));
// Restart from the CHECKPOINT.
memmove(seed, saved, sizeof(seed));
// Last 5 numbers again
for (int i = 5; i < 10; i++)
printf("%d %fn", i, erand48(seed));
return 0;
}
示例运行:
0 0.700302
1 0.122979
2 0.346792
3 0.290702
4 0.617395
5 0.059760
6 0.783933
7 0.352009
8 0.734377
9 0.124767
5 0.059760
6 0.783933
7 0.352009
8 0.734377
9 0.124767
显然,最初如何设置种子数组完全取决于您。您可以很容易地允许用户指定种子值,并报告您正在使用的种子,以便他们可以这样做。例如,您可以使用PID中的一些元素或一天中的时间以及子秒组件作为默认种子。或者你可以访问一个随机数设备,如/dev/urandom
,并从中获得6字节的随机值作为种子使用。
如何允许用户仅使用
long int
指定种子值?在这种方法中,似乎用户需要定义3个数字,但我只想在输入文件中询问1个数字(如安全素数)。
你可以用任何你选择的方法把一个数字分开。我有一个程序,它采用选项-s
来打印随机种子,-S
从long
设置种子,并且有时在使用随机高斯分布生成器时将long
拆分为3个unsigned short
值。我主要在64位系统上工作,所以我简单地将long
分成三个16位组件;代码在32位系统下也可以安全编译,但是种子中的第三个数字为0。这样的:
case 'q':
qflag = true;
break;
case 'r':
check_range(optarg, &min, &max);
perturber = ptb_uniform;
break;
case 's':
sflag = true;
break;
case 't':
delim = optarg;
break;
case 'S':
seed = strtol(optarg, 0, 0);
break;
case 'V':
err_version("PERTURB", &"@(#)$Revision: 1.6 $ ($Date: 2015/08/06 05:05:21 $)"[4]);
/*NOTREACHED*/
default:
err_usage(usestr);
/*NOTREACHED*/
}
}
if (sflag)
printf("Seed: %ldn", seed);
if (gflag)
{
unsigned short g_seed[3] = { 0, 0, 0 };
g_seed[0] = (unsigned short)(seed & 0xFFFF);
g_seed[2] = (unsigned short)((seed >> 16) & 0xFFFF);
if (sizeof(seed) > 4)
{
/* Avoid 32-bit right shift on 32-bit platform */
g_seed[1] = (unsigned short)(((seed >> 31) >> 1) & 0xFFFF);
}
gaussian_init(&g_control, g_seed);
}
else
srand48(seed);
filter_anon(argc, argv, optind, perturb);
return 0;
}
就我的目的而言,对于32位具有更严格的播种值是可以的(不是理想的,但是OK)。是的,我可以使用unsigned long long
和strtoull()
等,即使在32位平台上也可以获得64位数字(尽管我必须将其转换为long
以满足srand48()
)。我考虑的另一种选择是接受单独设置三个种子组件的参数-S xxxx:yyyy:zzzz
。然后,我必须修改种子打印代码以及解析代码。我使用一个单独的程序randseed
从/dev/urandom
读取数字并格式化结果,以便它可以传递给需要随机种子的程序:
$ randseed -b 8
0xF45820D2895B88CE
$