可以接受依赖于唯一的随机整数吗?


42

我一直在执行网络协议,并且我要求数据包具有唯一的标识符。到目前为止,我只是生成随机的32位整数,并假设从天文学角度来看,在程序/连接的生命周期内不会发生冲突是不太可能的。这是生产代码中通常被认为可以接受的做法,还是应该设计一种更复杂的系统来防止冲突?


47
为什么使用顺序整数不能削减它?
whatsisname '16

20
您为什么不只使用增量int? GUID的,其目的是为了拥有你所描述的独特性能,在大小为128位,而不是32
罗伯特·哈维

21
或者,为每台连接的计算机分配一个通道号,并使用递增的序列ID。这两个数字组合在一起(通道号占据了高位)成为您新的唯一ID。
罗伯特·哈维

27
如果您的“随机数生成器” 保证在生成所有其他数字之前不会重复特定的数字,则它是非常差的随机数生成器!按照同样的逻辑,抛硬币的唯一可能的“随机”顺序就是HTHTHTHTHT
。...– alephzero

17
“我要求数据包具有唯一的标识符”违反此要求的后果是什么?如果你需要的唯一标识符,在这个词的最严格的阅读,你必须有一个集中的系统dolling出标识符(如苹果电脑如何分配给各个网络卡公司)。最有可能您对“要求”有一个较软的定义。了解该级别的柔软度将极大地改变您收到的答案。
Cort Ammon

Answers:


142

当心生日悖论

假设您正在从一组大小为N(在您的情况下为N = 2 ^ 32)中生成一个随机值序列(均匀地,独立地)。

然后,生日悖论的经验法则规定,一旦生成了大约sqrt(N)值,则至少有50%的机会发生了冲突,即,在碰撞中至少有两个相同的值。生成的序列。

对于N = 2 ^ 32,sqrt(N)= 2 ^ 16 =65536。因此,在生成大约65k标识符之后,它们中的两个很可能发生碰撞!如果您每秒生成一个标识符,则将在不到一天的时间内发生;不用说,许多网络协议的运行速度都比后者快。


11
+1。在我的上一份工作中,我们的一位合作伙伴实际上使用这种方法来生成随机标识符(不是用于网络数据包,而是用于最终由最终客户创建的共享业务对象)。当我查询数据时,发现平均每天有两到三对重复项。(幸运的是,如果重复副本是在彼此之间四个小时内创建的,这只会使事情破裂,这种情况发生的频率要少一些。但是仍然如此。)
ruakh

6
(单击此处可进行数学运算)就其价值而言,$ \ sqrt {N} $近似值在达到恒定因子之前都是准确的;对于$ N = 2 ^ {32} $,实际阈值为77164,因为这是$ n $的最小值,因此$ \ prod_ {k = 1} ^ {n-1}(1- k / N) <1 / 2. $
wchargin '16

4
@wchargin:达到0.5的概率真的没有什么神奇的;值得注意的是,概率随着N的增加而相对快速地增加。如果32位标识符将有轻微但不平凡的随机冲突机会,那么40位标识符将几乎没有。
超级猫

3
@supercat:没错。我只是想知道,如果提供这样一个常数,那么不妨给出一个准确的值:-)
wchargin '16

2
@wchargin:我更喜欢从需要开始担心重复的地方来思考。如果一个碰撞远低于sqrt(N),则碰撞的概率会迅速下降,以至于可以肯定地说,除非随机生成器存在严重缺陷,否则碰撞不会发生。
超级猫

12

如果随机数具有足够的比特,则依靠随机数是唯一的被广泛认为是可以接受的。在某些加密协议中,重复随机数会破坏整个安全性。只要所使用的随机数生成器中没有严重的漏洞,就没有问题。

用于生成UUID的一种算法将有效地生成由122个随机位组成的ID,并假定它是唯一的。其他两种算法依赖于被截断为122位的唯一的哈希值,这具有大致相同的冲突风险。

因此,有一些标准依赖于122位足以使随机ID唯一,但是32位绝对不够。如果使用32位ID,则在碰撞风险达到50%之前只需要大约21个ID,因为使用21个ID时,将有近23对对,每对可能是碰撞。

甚至122位也比我在任何新设计中建议的要少。如果遵循一些标准化对您很重要,请使用UUID。否则,请使用大于122位的内容。

具有160位输出的SHA1哈希函数不再被认为是安全的,部分原因是160位不足以保证输出的唯一性。现代哈希函数的输出范围是224位到512位。随机生成的ID应以相同的大小为目标,以确保唯一性和良好的安全裕度。


12
SHA-1被认为是不安全的,因为针对算法本身的特定攻击(即非随机)可以比蛮力更快地找到冲突,而不是因为随机冲突的可能性很高。粗略估计说,如果使用122位和每秒10亿个(10 ^ 9)ID的生成速率,则要花50年的时间才能达到50%的碰撞机会。
8bittree '16

sqrt(2^122)= 2.3万亿五次方的UUID
noɥʇʎԀʎzɐɹƆ

2
@ 8bittree比特币网络每10分钟计算2个SHA2哈希。如果是SHA1哈希,则只需一周时间即可产生碰撞。如果以与比特币计算散列的速度相同的速度产生UUID,则产生冲突的时间将少于2秒。
kasperd '16

比特币是所有试图发现冲突的工具,它非常受欢迎,并且具有专门设计用于发现哈希的专用硬件。现在,可以肯定的是,如果OP计划创建一种非常流行的加密货币或类似的货币,则每个ID可能需要数百或数千个比特。但是,如果标准的UUID库足够用了,那么立即假设这些要求可能会鼓励远远超出必要的工作。
8bittree '16

@ 8bittree如果使用标准库有任何好处,则一定要使用UUID。但是,从中取出一些随机字节urandom并不会比使用UUID库更多。我只是在Python中实现了两者进行比较,每种方法恰好是源代码的25个字符。
kasperd '16

3

我称这种不好的做法。随机数生成只是不创建唯一数,而只是创建随机数。随机分布可能包含一些重复项。通过添加时间元素,可以使这种情况的发生不太可能。如果从系统时钟获取当前时间(以毫秒为单位)。像这样:

parseToInt(toString(System.currentTimeMillis()) + toString(Random.makeInt()))

将会走很长一段路。显然,要真正保证唯一性,您需要使用UUID / GUID。但是生成它们可能会很昂贵,以上可能就足够了,因为重叠的唯一可能性是,如果随机生成在同一毫秒内具有重复项。


9
在某些系统中1ms可能很长。
–quant_dev

7
这实际上并没有减少碰撞的机会。N个数字后发生碰撞的概率与OP原始解决方案的概率完全相等。在顺序分配键时,通常使用将当前时间用作种子的技巧。
Cort Ammon

2
@Fresheyeball我相信它不会有任何效果,除非Random.makeInt()实际上并未生成从整数的最小值到整数的最大值的均匀分布。对于此函数生成的每个过去的值,makeInt都有一个随机值,在这个精确的时间步长处,该值会生成该值,从而产生冲突。由于makeInt中的所有值都是等概率的,因此发生碰撞的概率等于没有增加时间的发生碰撞的概率。
Cort Ammon

2
@CortAmmon这不是将当前时间用作种子,只要没有在同一毫秒内生成所有N个数字,它肯定会有所不同,因为两个带有不同时间戳部分的数字永远不会冲突。如果您想象另一个答案的示例,即每秒一个数据包在不到一天的时间内发生冲突的可能性为50%,那么这个问题在每秒一个数据包中发生冲突的可能性为0%,至少直到发生问题为止currentTimeMillis
hobbs

3
@hobbs您会忘记整数溢出。现在,如果OP使用的密钥是一个包含2个整数的结构,一个包含System.currentTimeMillis,一个包含Random.makeInt(),则发生碰撞的可能性将大大降低。但是,这不是本示例中的代码执行的操作。给定任何先前时间和随机值以及任何当前时间,发生碰撞的概率与两个随机数首先发生碰撞的概率相同。
Cort Ammon

3

它取决于故障的可能性和故障的后果。

我记得在软件和硬件人员之间的一次辩论中,硬件人员认为一种错误结果可能性很小(例如100年内发生1次失败)的算法是可以接受的,而软件人员则认为这是一场恶性循环。事实证明,硬件人员通常会计算预期的故障率,并且非常习惯这样的想法,即每件事都会偶尔给出错误的答案,例如,由于宇宙射线引起的干扰。他们发现软件人员期望100%的可靠性很奇怪。


1

当然,您会发现两个随机的32位整数是连续的,但是概率不是很高,但这并不是完全不可能的。适当的工程决策是基于冲突的后果,对所生成数字数量的估计,要求唯一性的生命周期以及恶意用户开始尝试导致冲突时发生的情况。


0

可以假设随机数是唯一的,但是您必须小心。

假设您的随机数是均匀分布的,发生碰撞的可能性大约为(n 2/2)/ k,其中n是您生成的随机数的数量,k是“随机”数可以取的可能值的数量。

您不必将数字放在天文数字上不太可能,因此可以将其设为2 30中的 1 (大约10亿)。进一步说,您生成了2个30个数据包(如果每个数据包代表大约一千字节的数据,那么这意味着大约一兆字节的总数据,虽然很大,但并非难以想象)。我们发现我们需要一个至少2 89个可能值的随机数。

首先,您的随机数必须足够大。一个32位随机数最多可以有2 32个可能的值。对于繁忙的服务器而言,它远远不够高。

其次,您的随机数生成器需要具有足够大的内部状态。如果您的随机数生成器仅具有32位内部状态,那么无论您生成的值有多大,您最多仍只能获得2 32个可能的值。

第三,如果您需要随机数在连接中唯一,而不是仅在连接内唯一,则您的随机数生成器必须是种子良好的。如果您的程序经常重新启动,则尤其如此。

通常,编程语言中的“常规”随机数生成器不适合此类用途。通常由密码库提供的随机数生成器。


0

上面的某些答案中所包含的假设是,随机数生成器确实是“平坦的”-任何两个数字成为下一个生成的数字的概率相同。

对于大多数随机数生成器而言,可能并非如此。其中大多数使用重复应用于种子的一些高阶多项式。

就是说,有很多系统依赖于此方案,通常使用UUID。例如,《第二人生》中的每个对象和资产都有一个128位的UUID,它是随机生成的,并且很少发生碰撞。


0

很多人已经给出了高质量的答案,但是我想补充一些要点:首先,@ nomadictype关于生日悖论的观点非常出色

另一点:随机性并不像人们通常认为的那样直接生成和定义。(实际上,实际上有针对随机性的统计检验)。

话虽如此,重要的是要意识到“ 赌徒的谬论”,这是一种统计谬论,人们认为独立事件在某种程度上会相互影响。随机事件通常在统计上彼此独立-即,如果您随机生成一个“ 10”,则它至少不会改变您将来生成更多“ 10”的可能性。(也许有人可以提出该规则的例外,但我希望几乎所有随机数生成器都是这种情况)。

因此,我的回答是,如果您可以假设足够长的随机数序列是唯一的,那么它们就不是真正的随机数,因为这将是一个清晰的统计模式。此外,这也意味着每个新数字都不是独立事件,因为例如生成10,则表示将来生成10的概率为0%(不可能发生),再加上这意味着您增加获得10以外的数字的几率(即,生成的数字越多,剩余数字中每个数字出现的可能性就越高)。

我还要考虑的另一件事是:据我所知,单场比赛赢得强力球的机会约为1.75亿分之一。但是,某人获胜的几率要高得多。您对某人 “获胜”(即成为重复)的几率比对任何特定数字“获胜” /成为重复的几率更感兴趣。


如果一个人以这样一种方式生成4096位标识符,即每个位都可能独立于在同一标识符或任何其他标识符中生成的任何其他位,则每个比特都是0或1,那么任何两个标识符将匹配的可能性将是即使对于可观察到的宇宙中每个大约4.0E81原子随机生成不同的标识符,它也将变得很小。这样的标识符几乎肯定会是唯一不会以任何方式让他们“非随机”的事实
supercat

@supercat是的-给定足够大的数量,重复的可能性很小,但这不是不可能的。实际上,取决于非唯一性的后果是OP描述的是否是一个好主意。
EJoshuaS-恢复莫妮卡

如果随机偶然碰撞的可能性小于流星罢工使依赖唯一id的设备消失的可能性,那么从工程学的角度来看,就不必担心前者了。迫切需要担心任何可能导致随机数不独立的事情,但是随机冲突将不成问题。
超级猫

@supercat我认为您在读错这个,在生日悖论上看到了另一个答案,我认为发生碰撞的可能性比您计算的要大得多-OP仅使用32位数字,因此我不确定您在哪里从4096开始,并且由于游牧类型表明与该长度的数字发生最终碰撞的概率实际上高得惊人。
EJoshuaS-恢复莫妮卡

没错,如果冲突是完全不能接受的,那么即使对于一小部分人,32位数字也太短了。如果使用的数字足够大,则可以将随机碰撞的可能性降低到可以安全地假设它们不会发生的程度,在许多情况下,使用较大的数字可能比尝试使用其他方式更好。确保唯一性,因为后者通常需要访问无法撤消或回滚的状态转换,即使重置系统时钟或从备份重新加载系统也是如此。
超级猫

0

不管使用多少位-您都不能保证两个“随机”数将不同。相反,我建议您使用诸如计算机的IP地址或其他网络地址之类的东西和一个序列号,最好是HONKIN'BIG序列号-128位(显然是无符号的)听起来像是一个好的开始,但是256更好。


-1

不,当然不。除非您使用的样品未经更换,否则重复的机会(尽管很小)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.