从C中的检查点保存并重新启动随机链(drand48)

时间:2015-08-05 23:25:35

标签: c random

我正在尝试编写一个程序,如果完全执行或者从某个检查点停止并重新启动,则会产生相同的结果。为此,我需要能够在任何场景中重复完全相同的随机数序列。所以,这里是我试图做的一段代码,但当然,我没有成功。你能帮我修一下这段代码吗?

int main(){
 int i;
 long int seed;

 // Initial seed
 srand48(3);

 // Print 5 random numbers
 for(i=0;i<5;i++)  printf("%d %f\n",i,drand48());

 // CHECKPOINT: HOW TO PROPERLY SET seed?
 seed=mrand48(); // <--- FIXME

 // 5 numbers more
 for(i=5;i<10;i++) printf("%d %f\n",i,drand48());

 // Restart from the CHECKPOINT.
 srand48(seed);

 // Last 5 numbers again
 for(i=5;i<10;i++) printf("%d %f\n",i,drand48());

}

1 个答案:

答案 0 :(得分:3)

如果您需要能够恢复随机数序列,则不能让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 %f\n", 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 %f\n", 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 %f\n", 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 {{1}使用随机高斯分布生成器时的值。我主要在64位系统上工作,所以我只是将unsigned short分成三个16位组件;代码也在32位系统下安全编译,但将种子中的第三个数字保留为0.像这样:

long

就我的目的而言,32位的播种值更受限制是可以的(不理想,但可以)。是的,我可以使用 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: %ld\n", 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; } unsigned long long等来获取64位数字甚至在32位平台上(尽管我必须将其转换为strtoull()无论如何要满足long。我考虑的另一种方法是接受一个参数srand48(),其中三个种子组件分别设置。然后我必须修改种子打印代码以及解析我使用一个单独的程序-S xxxx:yyyy:zzzz来读取randseed中的数字并格式化结果,以便将其传递给需要随机种子的程序:

/dev/urandom