是否有使用时间来播种随机数生成的替代方案?

时间:2011-10-01 01:39:41

标签: c random random-seed

我正在尝试在计算群集中同时运行一段代码(2000个实例左右)。它的工作方式是我提交作业,集群将在节点每隔一段时间打开时运行它们,每个节点有几个作业。对于使用时间种子的随机数生成中的大量实例,这似乎产生相同的值。

我可以使用一个简单的替代方案吗?重复性和安全性并不重要,快速生成独特的种子。什么是最简单的方法,如果可能的话,跨平台方法会很好。

10 个答案:

答案 0 :(得分:26)

rdtsc指令是非常可靠(随机)的种子。

在Windows中,可以通过__rdtsc()内在函数访问。

在GNU C中,可通过以下方式访问:

unsigned long long rdtsc(){
    unsigned int lo,hi;
    __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
    return ((unsigned long long)hi << 32) | lo;
}

该指令测量自处理器启动以来的总伪周期。鉴于当今机器的频率很高,即使它们同时启动并且以相同的速度运行,两个处理器也不太可能返回相同的值。

答案 1 :(得分:5)

我假设您有一些流程启动其他流程。让它通过种子使用。然后,您可以让主进程只为每个进程传入一个随机数,以用作其种子。那种方式实际上只选择了一个任意种子......你可以用它来做时间。

如果您没有主进程启动其他进程,那么如果每个进程至少具有唯一索引,那么您可以做的是让一个进程在内存中生成一系列随机数(如果共享内存)或在一个文件(如果是共享磁盘)中然后让每个进程将索引的随机数拉出来用作它们的种子。

没有什么能比单一种子的一系列随机数更均匀地分配种子。

答案 2 :(得分:5)

PID和时间的组合应该足以获得唯一的种子。它不是100%跨平台的,但* nix平台上的getpid(3)和Windows上的GetProcessId将获得99.9%的平台。这样的事情应该有效:

srand((time(NULL) & 0xFFFF) | (getpid() << 16));

您还可以在* nix系统上从/dev/urandom读取数据,但这与Windows上没有相同的数据。

答案 3 :(得分:5)

unsigned seed;

read(open("/dev/urandom", O_RDONLY), &seed, sizeof seed);
srand(seed); // IRL, check for errors, close the fd, etc...

我还建议使用更好的随机数生成器。

答案 4 :(得分:3)

如果可以使用C ++ 11,请考虑std::random_device。我建议您观看link以获取全面的指南。

视频链接中提取必要消息:您永远不会使用srand&amp; rand,而是使用std::random_devicestd::mt19937 - 对于大多数情况,以下是您想要的:

#include <iostream>
#include <random>
int main() {
    std::random_device rd;
    std::mt19937 mt(rd());
    std::uniform_int_distribution<int> dist(0,99);
    for (int i = 0; i < 16; i++) {
        std::cout << dist(mt) << " ";
    }
    std::cout << std::endl;
}

答案 5 :(得分:1)

而不是从C std lib time()函数以秒为单位测量的直接时间,您是否可以使用处理器的计数器?大多数处理器都有自由运行的滴答计数,例如在x86 / x64中有Time Stamp Counter

  

自Pentium以来,时间戳计数器是所有x86处理器上的64位寄存器。它计算自重置以来的滴答数。

(该页面还有很多方法可以在不同平台上访问此计数器 - gcc / ms visual c / etc)

请记住,时间戳计数器并非没有缺陷,它可能不会在处理器之间同步(您可能不关心您的应用程序)。节电功能可能会使处理器上升或下降(再次您可能不关心)。

答案 6 :(得分:1)

只是一个想法...生成GUID(16个字节)并将其4字节或8字节块(取决于种子的预期宽度)求和,允许整数环绕。将结果用作种子。

GUID通常封装生成它们的计算机的特征(例如MAC地址),这使得两台不同的机器最终生成相同的随机序列变得不太可能。

这显然不可移植,但为您的系统找到合适的API /库应该不会太难(例如,Win32上为UuidCreate,Linux上为uuid_generate

答案 7 :(得分:1)

提供CryptGenRandom()RtlGenRandom()。它们将为您提供一组随机字节,您可以将其用作种子。

您可以在msdn pages上找到文档。

Linux / Unixes

您可以使用Openssl的RAND_bytes()在linux上获取随机字节数。它默认使用/dev/random

把它放在一起:

#ifdef _WIN32
  #include <NTSecAPI.h>
#else
  #include <openssl/rand.h> 
#endif

uint32_t get_seed(void)
{
  uint32_t seed = 0;

#ifdef _WIN32
  RtlGenRandom(&seed, sizeof(uint32_t) );
#else
  RAND_bytes(&seed, sizeof(uint32_t) ); 
#endif

  return seed;
}

请注意,openssl默认提供加密安全的PRNG,因此您可以直接使用它。更多信息here

答案 8 :(得分:0)

假设您使用的是合理的POSIX-ish系统,您应该拥有clock_gettime。这将以纳秒给出当前时间,这意味着出于所有实际目的,不可能两次获得相同的值。 (从理论上讲,糟糕的实现可能会有更低的分辨率,例如只需要将毫秒乘以100万,但即使像Linux这样的半容系统也会产生真正的纳秒结果。)

答案 9 :(得分:0)

如果唯一性很重要,则需要安排每个节点知道其他人声明了哪些ID。您可以使用一个协议询问“任何人声称ID x?”或者为每个节点预先安排选择尚未分配给其他人的ID。

(GUID使用机器的MAC,因此会属于“提前安排”类别。)

如果没有某种形式的协议,您将冒着两个节点使用相同ID的风险。