Ruby 使用最小存储容量洗牌大范围的数字

Ruby 使用最小存储容量洗牌大范围的数字,ruby,random,set,range,shuffle,Ruby,Random,Set,Range,Shuffle,我有一个非常大的数字范围/集合,(1..1236401668096),我基本上想“洗牌”,即随机遍历,而不重新访问相同的数字。我将运行一个Web服务,每次请求传入时,它都会增加一个计数器,并从范围中提取下一个“无序”数字。该算法必须适应服务器离线,能够使用计数器的持久值重新启动遍历(类似于如何为伪随机数生成器设定种子,并在给定种子和正在进行的迭代的情况下获得相同的伪随机数) 我想知道这样的算法是否存在或可行。我已经看过了,但第一步是“写下从1到N的数字”,这将占用我整个范围的TB存储空间。为每个

我有一个非常大的数字范围/集合,
(1..1236401668096)
,我基本上想“洗牌”,即随机遍历,而不重新访问相同的数字。我将运行一个Web服务,每次请求传入时,它都会增加一个计数器,并从范围中提取下一个“无序”数字。该算法必须适应服务器离线,能够使用计数器的持久值重新启动遍历(类似于如何为伪随机数生成器设定种子,并在给定种子和正在进行的迭代的情况下获得相同的伪随机数)

我想知道这样的算法是否存在或可行。我已经看过了,但第一步是“写下从1到N的数字”,这将占用我整个范围的TB存储空间。为每个请求生成一个伪随机数可能需要一段时间,但随着数据库/树的填满,冲突将变得更加常见,并可能降低性能(根据我的计算,在10亿次点击后,冲突发生的几率已经是0.08%)。对于我的场景,有没有更理想的解决方案,或者这只是一个白日梦

洗牌的原因是,能够正确猜测序列中的下一个数字可能会导致我的应用程序中出现一个较小的DOS漏洞,但也因为数字分布更广,表示层看起来会更好(我不想详细介绍应用程序的具体功能)。在这一点上,我正在考虑只使用PRNG和处理碰撞或洗牌范围片(从
(1..10000000开始)。洗牌
,然后,
(10000001,20000000)。洗牌
,等等,因为每个范围的数字开始用完)


有任何数学学家有更好的想法/建议吗?

我想最好的选择是使用a/。它们是为这种类型的东西而设计的,并且应该不难找到一个现有的实现来满足您的需求

虽然碰撞在理论上是可能的,但可能性极低。引用维基百科:

如果地球上每个人都拥有6亿个UUID,那么复制一个UUID的概率约为50%


分而治之?将其分解为可管理的块并将其洗牌。您可以将数字范围除以它们的模n值。这个列表是有建设性的,根据n的不同,它非常小。一旦一组人精疲力竭,你可以使用下一组人

例如,如果选择n为1000,则创建1000个不同的组。选择一个介于1和1000之间的随机数(我们称之为x),并将其值模1000等于x的数字洗牌。一旦你用完了这个范围,你可以选择一个介于1和1000之间的新随机数(显然没有x)来得到下一个要洗牌的子集。跟踪1..1000范围内的哪些数字已经被使用,这应该不是什么挑战,因此您只需要对子集中的数字使用一个可重复的洗牌算法(例如Fisher-Yates的“索引”)

/dev/random
位连接PRNG或LFSR序列 有几种算法可以生成任意大且周期已知的伪随机数。两个明显的候选算法是LCPRNG(LCG)和LFSR,但还有更多的算法,如Mersenne Twister

这些生成器的周期可以轻松构建以满足您的需求,这样您就不会发生冲突


您可以通过添加10,20,或者从接口(如
/dev/random)获取30位加密散列熵。
因为已知数字的确定部分是唯一的,如果重复它的实际随机部分,则没有区别。

一个选项是使用,虽然它们在加密方面不安全。为什么不使用时间戳和伪随机数或散列的组合来降低冲突的可能性?UUID当然可以防止冲突,但当我必须将其截断回更小的地址空间时,我相信我会再次发生冲突。不幸的是,我无法扩展地址空间以减少冲突的可能性。谢谢你的主意!我喜欢这个声音!我将尝试创建一个m值为1236401668096的LCG。我不太担心它是严格加密安全的,所以我可能跳过/dev/random部分。如果没有其他好的答案出现,我会把这个标记为接受。是的,我已经考虑过了(第二段到最后一段的末尾);因为太长了,读不下去了(我在TL失败了,博士博士)。很高兴这对其他人来说也是一个有效的解决方案@Abe:我有点错过了,但我也建议了一种不太明显的分区方式,而不是按顺序切片。