长话短说:
程序同时启动两次时会抽取相同的随机种子。它是怎么发生的?
细节
我正在运行 MCMC 统计分析,因此我执行程序 (phylobayes) 两次以获得2 独立复制。
为此,我的 shell 脚本分离每个复制运行,如下所示(示意性地):
pb -d "inputdata" "replicate1" &
pid1=$!
pb -d "inputdata" "replicate2" &
pid2=$!
wait "$pid1"
wait "$pid2"
(然后,使用 slurm 将这个脚本提交到计算集群(Debian 10)sbatch
)。
但我的许多运行(例如 30%)都是从相同的随机种子开始的!如日志文件所示。
菲洛巴耶斯使用C++Random::initRandom()
命令。 [编辑:实际上,正如所指出的,该函数在包内有一个自定义定义]。
从技术上讲,是否可以采用相同的随机种子?它使用 /dev/random 还是 /dev/urandom ?
如果是,我将sleep
在开始之间插入一个命令;
如果没有,我必须明白我犯了什么愚蠢的错误,但我不知道它会是什么......
答案1
从技术上讲,是否可以采用相同的随机种子?
如果您查看链接的函数,您会发现它使用微秒数作为种子(模 10^6):
void Random::InitRandom(int seed) {
if (seed == -1) {
struct timeval tod;
gettimeofday(&tod, NULL);
seed = tod.tv_usec; // <== this line
}
Seed = seed;
srand(seed);
...
}
所以,是的,如果您的软件的两个实例在同一微秒内运行该函数(或者可能恰好有 1 秒的差异等),您将获得相同的种子。这是合理的,因为您会立即启动一个实例。
gettimeofday() 的粒度通常比 1 μs 更粗,这一事实进一步加剧了这种情况,因此时间上彼此非常接近的运行可能具有相同的 tv_usec 值,即使它们不是发生在完全相同的微秒上。
我认为解决此问题的最佳方法是使用适合您使用软件的方式的不同种子初始化算法来构建您自己的版本。例如,您可以在每次后续运行中将种子数增加 1,或者使用 /dev/random,而不是使用微秒。
或者,如果您有 GNU coreutils,则可以使用该sleep 0.001
命令将第二次执行暂停 1 毫秒,这应该会引入足以让不同实例具有不同种子的延迟。