随机数生成器,按排序顺序

时间:2017-07-07 06:23:11

标签: java algorithm random sequence distribution

我需要一个生成器,用于许多(最多一万亿,10 ^ 12)个独特的随机64位数字。 生成器需要按排序顺序返回数字(Long.MIN_VALUE为Long.MAX_VALUE)。问题是排序$ 10 ^ {12} $ number的速度很慢。该用例正在复制为BBHash运行的测试(在paper中,4.5索引一万亿个密钥)。

直接的解决方案是在内存中创建一个集合,使用大量的集合 确保不会返回重复项。 但是这会占用太多内存或I / O. 我想最多使用几MB内部状态。

生成器应该在内部使用java.util.Random。 它应该是"公平"尽可能(具有相同的统计分布,如果否则生成)。我还希望有一个128位数字版本(2长)。

到目前为止我所拥有的是在内存中创建一个集合的代码(Java代码):

public static void main(String... args) {
    for(long x : randomSet(10, 0)) {
        System.out.println(x);
    }
}

static Iterable<Long> randomSet(int size, int seed) {
    Random r = new Random(seed);
    TreeSet<Long> set = new TreeSet<Long>();
    while (set.size() < size) {
        set.add(r.nextLong());
    }
    return set;
}

-8292973307042192125
-7423979211207825555
-6688467811848818630
-4962768465676381896
-2228689144322150137
-1083761183081836303
-279624296851435688
4437113781045784766
6146794652083548235
7105486291024734541

最简单(错误)的解决方案是非随机的,是均匀分配结果。 我不认为解决方案会随着时间的推移添加一个随机的差距&#34;将工作, 因为它很慢,10 ^ 12之后这些间隙的总和不会降落到它应该的位置(好吧,也许:记住剩下多少个数,然后重新计算分布......)。我认为以下内容应该有效,但是很复杂,并且不确定要使用哪些公式:对于每个位级别, 递归地,计算可能发生多少0/1 (以某种方式使用二项分布或近似,正态/高斯分布)。 在某些时候停止(例如,100万条或更少的区块), 使用上面的代码,以获得速度。 但也许有一个优雅的解决方案。 也许这与Metropolis-Hastings算法有关,不确定。 我读了&#34;一种有效的顺序随机抽样算法&#34;, 但我认为这只适用于小n,我发现很难从中获得一个简单的算法。

Java代码是最好的,但C很好(无论如何,我可能必须将其转换为C / C ++)。我想不要使用太多的库来简化移植。

5 个答案:

答案 0 :(得分:1)

符合要求

  
      
  1. 从整数区间I = [ - (R + 1),R],R&gt;生成随机数r_i的序列。 0具有统计分布   java.util.Random中
  2.   
  3. 序列r_i必须严格增加(对于i> j,r_i> r_j)
  4.   

我们可以提出一个简单的算法

A1:
 - draw a random number r_i from I via a library call
 - discard it, if it is less or equal the last draw, try another pick

可能的抱怨是这个算法可能没有给出正确数量的生成r_i,有一个关于N = 10 ^ 12总预期数的模糊要求

  
      
  1. “需要一台发电机用于许多(最多一万亿,10 ^ 12)独特的随机64位数字”
  2.   

解决方法是

A2:
 - to generate N numbers and then 
 - sort them

但是还有另一个要求,就是没有足够的可用内存。

  
      
  1. “我想最多使用几MB内部状态。”
  2.   

我的猜想是不可能立刻满足所有这些要求。

作为妥协,我建议

A3:
 R=2^63 = 9 10^18  
 N=1 Trillion = 10^12
 - divide the range I=[-R,R-1] into N intervals of length (2R+1)/N each 
 - visit each of those intervals (visiting one interval after another)
 - draw a random number from that interval

这将按递增顺序给出N个随机数。

<强>更新

略过BBHash papersources几次后,这是我的理解:

给定一些整数集I和子集S,其中N = | S |元素,BBHash过程将计算一个函数f,它将S映射到{1,..,N}的某些排列(什么排列似乎由BBHash过程隐式决定)并将所有其他元素从I映射到特殊值Imax来自I.

可能的测试:

给定S和f可以检查是否正确计算了来自I的某些任意元素的S成员资格。

还可以检查f(S)= {1,..,N}。

我的猜测是,所请求的算法旨在在紧张的内存预算下动态计算N = 10 ^ 12的样本集S,需要随机数序列的唯一性而不是单调性。

引用https://stackoverflow.com/a/35050835/2579220

  

概率数据结构无法给出明确答案,   相反,它们为您提供了合理的答案近似值   以及估算此估算的方法。它们非常有用   对于大数据和流应用程序,因为它们允许   大大减少了所需的内存量(与之相比)   数据结构,为您提供准确的答案)。

     

在大多数情况下,这些数据结构使用哈希函数   随机化项目。因为他们忽略了碰撞,所以他们保持了规模   不变,但这也是他们无法准确给你的原因   值。

在BBHash的情况下,使用一系列不同的散列函数h_i。一个应用不同的h_i直到没有发生碰撞。仅当输入是唯一的时才有效。只有当实现在特定的S中存储了足够多的不同h_i时,它才会起作用。

答案 1 :(得分:1)

我有一个解决方案。

(事实证明,在粗略排序顺序中生成100&#39,000或更多条目比使用大HashSet生成更快。粗略排序意味着用{{替换TreeSet 1}},使用10&000;而不是5的限制。这是因为重复测试要快得多。)

每个固定(子)范围的随机条目数

创建树:对于每个位级别(从最高有效位开始),使用正态分布递归地生成应该将该级别的位设置为0的条目的随机数。其余条目将此级别的位设置为1.在每个递归级别,这将使范围缩小大约一半。例如,当少于100万个条目时停止,然后切换到使用内存中的伪RNG并对这些数字进行排序(或使用位字段)。

这里有一些代码(尚未测试):

HashSet

答案 2 :(得分:0)

让我们调用你的宇宙随机值U.对于初学者来说,这是64位有符号整数的范围,所以有2 ^ 64个可能的值。让我们调用你需要产生N的已排序随机值的总数,你说这大约是10 ^ 12。

预先确定要使用多少合理内存。假设你的机器可以分配和使用1GB而没有任何问题。这是134,217,728 64位值。称之为A(数组大小)。

N / A = 7450.58 ......,所以最多可以调整7451个桶,并调整A到ceil(N / 7451),即134,210,173。计算R(铲斗范围)= U / 7451。

Loop over 7451 buckets (B):
    Generate 134,210,173 random values in the range (0..R),
    inserting them into the array as they are produced. Binary
    insertion should be reasonable (N*log(N), just like generating
    them all then sorting, but you can use the insertion to catch
    duplicates so you don't need extra memory or time for that).

    Output the bucket of values, adding (B*R) to each.

你会有几个人超过N;如果这很关键,那么随机选择所需数量的桶,并从每个桶中删除一个值。

答案 3 :(得分:-1)

10 ^ 12约为2 ^ 40,即连续值之间的平均步长为2 ^ 24。

因此,如果目标是生成不可预测但有序的哈希序列,则不可能,2 ^ 24对于暴力来说太容易了

但如果不是目标,那么为什么不在最高位加入增量2 ^ 40计数器,在较低位中加入2 ^ 24随机值?

答案 4 :(得分:-2)

你想要很多伪随机64位数,都是唯一的。给定独特的输入和相同的密钥,加密是独一无二的 - 它必须是因为它是可逆的。 DES是64位分组密码,因此使用ECB模式加密DES中的数字0,1,2,3,4 ...... 10 ^ 12将为您提供万亿个唯一的64位数字。使用相同的密钥,它们保证唯一,因为输入是唯一的。一个不同的密钥将给出一组不同的唯一数字,但有些可能是第一组中的数字重复。

对于128位数字,使用AES,其具有128位块大小,再次处于ECB模式并使用固定密钥。

唯一需要的内部状态是你正在使用的键和一个数字,表示你在[0..10 ^ 12]范围内的距离。

您需要单独对输出进行排序。鉴于从存储的最后一个数字重新启动进程以生成下一批数字很容易,我怀疑合并排序相对容易实现,每个新批处理在生成它时合并到已经排序的主文件中。批量大小可以保持在内存容量内,主要文件保存在光盘上。

此解决方案不使用java.util.Random。这对你有多重要?除了最复杂的加密分析之外,加密被设计为随机出现,并且可能比标准Java Random PRNG“更随机”。