保存和重新启动随机链(drand48)从检查点在C



我试图编写一个程序,给出相同的结果,要么完全执行,要么从一些检查点停止和重新启动。要做到这一点,我需要能够在任何情况下重复相同的随机数序列。这里有一段代码,我试着这样做,当然,我没有成功。你能帮我修复这个代码吗?

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来打印随机种子,-Slong设置种子,并且有时在使用随机高斯分布生成器时将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 longstrtoull()等,即使在32位平台上也可以获得64位数字(尽管我必须将其转换为long以满足srand48())。我考虑的另一种选择是接受单独设置三个种子组件的参数-S xxxx:yyyy:zzzz。然后,我必须修改种子打印代码以及解析代码。我使用一个单独的程序randseed/dev/urandom读取数字并格式化结果,以便它可以传递给需要随机种子的程序:

$ randseed -b 8
0xF45820D2895B88CE
$

最新更新