种子比时间更好(0)?

时间:2009-09-09 00:22:32

标签: c++ boost random

我知道时间(0)通常用于播种随机数生成器,并且当程序每秒运行多次时它才成为问题。我想知道在生成随机数时要考虑哪些更好的种子。我在Windows上阅读了有关GetTickCount,timeGetTime和QueryPerformanceCounter的内容。几乎所有的操作都能满足这些要求,还是有更好的播种选择?

以下是使用boost库的快速代码示例:

#include <iostream>
#include <boost/random.hpp>
using namespace std;
using namespace boost;

int main()
{
    mt19937 randGen(42);
    uniform_int<> range(0,100);
    variate_generator<mt19937&, uniform_int<> > GetRand(randGen, range);

    for (int i = 0; i < 30; ++i)
        cout << GetRand() << endl;
}

11 个答案:

答案 0 :(得分:12)

Netscape安全的一些早期黑客主要围绕知道何时发送加密数据包并缩小具有该知识的种子的可能范围。因此,获得蜱计数或其他甚至远程确定性的东西并不是你最好的选择。

即使使用种子,“随机”数字的序列也是基于该种子的确定性的。内华达州博彩委员会的一名调查员意识到这一点,他应该检查某些位置并利用这些知识在被抓住之前赚取相当多的钱。

如果您需要世界级的随机性,您可以在系统中添加硬件,以提供高度随机化的数字。这就是众所周知的扑克网站的做法(至少,这就是他们所说的)。

除此之外,结合您系统中的一些因素,这些因素都会以尽可能小的可预测性独立快速地变化,从而创造出非常不错的种子。对SO的相关帖子的回答建议使用Guid.NewGuid()。GetHashCode()。由于Guid是基于许多确定性因素,包括时间,因此不能为种子提供良好的基础:

  

WinAPI GUID的密码分析   发电机显示,自从   V4 GUID序列是伪随机的,   鉴于初始状态可以   预测接下来的250 000个GUID   由函数返回   UuidCreate [2]。这就是GUID的原因   不应该用于密码学,   例如,作为随机密钥。

来源:Wikipedia Globally Unique Identifier

答案 1 :(得分:6)

关于在线扑克早期关于32位种子的评论但有趣的故事太长了

  

中使用的混洗算法   ASF软件始终以一个开头   订购的卡片组,然后   生成一系列随机数   用来重新排序甲板。真实的   卡片组,有52个! (〜2 ^ 226)   可能独特的洗牌。回想起那个   32位随机数的种子   生成器必须是32位数,   意思是刚刚超过4个   十亿可能的种子。自甲板   重新初始化和发电机   每次洗牌之前重新播种,只有4次   可能导致十亿次洗牌   从这个算法。 4B可能   shuffles惊人地低于52!

     

RST开发的工具可以利用它   漏洞需要五张牌   要知道的甲板。基于   五个已知卡,程序搜索   通过几十万   可能的改组和演绎   一个是完美的匹配。如果是   Texas Hold'em Poker,这意味着   程序将两张卡作为输入   作弊的玩家被处理,   再加上前三张社区卡   面朝上(翻牌)。   这五张牌之后就知道了   四轮下注中的第一轮,和   足以确定(实时,   在比赛期间)确切的洗牌。

http://www.ibm.com/developerworks/library/s-playing/

答案 2 :(得分:5)

在unix系统上,您可以从/ dev / random中取几个字节作为RNG的种子。 / dev / random应该是非常好的随机,使用PC上可用的不同熵源。当然,这完全取决于实现。

这可能对加密应用程序有用,因为时间(0)相对容易猜测。

答案 3 :(得分:4)

您将需要替代/辅助熵源。根据您要使用的熵数,您可以计算以下任何输入的哈希值,并将其用作最终生成器的种子:

  • 在堆栈上声明一个未初始化的随机大小字符数组
  • 分配随机字节的内存
  • 要求用户移动鼠标
  • 要求用户将随机CD放入CD驱动器并从第一首曲目随机位置读取随机字节
  • 打开用户的麦克风或摄像头,收集随机输入秒数,计算哈希和种子
  • Windows :使用CryptGenRandom获取加密随机字节的缓冲区
  • Unix :正如其他人提到的那样,请从/dev/random
  • 中读取

答案 4 :(得分:4)

在unix上尝试从/ dev / random读取。从这个设备读取是很慢的,所以不要经常这样做 - 例如只设置初始种子。随机设备从硬件生成的熵(来自设备的环境噪声)获取数据,并且在给定时间段内没有无限量的可用数据。如果用完熵,SSL库可能会失败。一段时间后熵重新填充(实际上它是一个熵池)。还有随机的afaik,它更经济,但更不随机,不会在低熵条件下阻挡。

答案 5 :(得分:2)

有一项网络服务提供由大气噪音产生的免费和付费“真实”随机位:http://www.random.org/

连线播放了一篇关于两个人的文章,他们基本上使用网络摄像头CCD芯片产生的噪音来生成随机数:http://www.wired.com/wired/archive/11.08/random.html

答案 6 :(得分:1)

使用tickCout()或任何高频率的东西都是个坏主意。 这是因为couter循环很快就会回到零,因此可以获得具有相同种子的可能性。

time(NULL):   Repeats every 64 years.  
tickCouter()  Repeats every X days.

你可以尝试从自然中获得一些随机价值 Lightining在最后一秒击中全球(显然是在线)? (你可能需要做研究,看看它是否可变)。

答案 7 :(得分:1)

您可以在程序退出时存储随机种子并在开始时加载它,因此您只需在第一次程序启动时使用时间(0)初始化RNG。

答案 8 :(得分:1)

由于您已经在使用boost,因此您可能需要boost::random_device

(至少在Linux上。我不记得在Windows上是否有明显的CryptGenRandom实现。)

答案 9 :(得分:0)

使用随机数生成器的方法只播种一次,因此您的在线游戏示例不是问题,因为可能会在程序首次启动时播种的每个值使用相同的rng (也许几年前)。

同样在你自己的代码中尝试为rng播种一次,然后在需要的地方使用相同的实例,而不是在整个地方创建一个带有新种子的新rng。

答案 10 :(得分:0)

使用(仅)时间作为PRNG种子基本上有两个问题:

  1. 可预测(这使得它不适合加密)
  2. 连续种子具有非常线性的依赖性
  3. 对于第一个问题,通常必须采取尽可能多的熵来源。

    关于第二个问题,Makoto Matsumoto的论文Common defects in initialization of pseudorandom number generators可能会给出一些见解。