基于ANU量子随机数服务器的随机数

时间:2015-02-26 17:23:41

标签: ruby random

我被要求使用ANU Quantum Random Numbers Service创建随机数,并仅使用Random.rand作为后备。

 module QRandom

   def next
     RestClient.get('http://qrng.anu.edu.au/API/jsonI.php?type=uint16&length=1'){ |response, request, result, &block|
       case response.code
         when 200
           _json=JSON.parse(response)
           if _json["success"]==true && _json["data"] 
             _json["data"].first || Random.rand(65535)
           else
             Random.rand(65535) #fallback
           end
         else
           puts response #log problem
           Random.rand(65535)   #fallback
       end
     }
   end

 end

他们的API服务给了我一个介于0-65535之间的数字。为了为更大的集合创建随机数,比如0-99999之间的随机数,我必须执行以下操作:

(QRandom.next.to_f*(99999.to_f/65535)).round 

这让我觉得这是错误的做法,因为如果我要使用一个服务(量子或非量子)从0-3创建数字并将它们转换到0-9999的空间我可以选择4个数字我总是得到。如何使用产生0-65535之间数字的服务为更大数字集创建随机数?

3 个答案:

答案 0 :(得分:3)

由于65535是二进制的1111111111111111,您可以将随机数服务器视为随机位的来源。事实上它以16块为单位给你的位并不重要,因为你可以发出多个请求,你也可以忽略响应中的某些位。

因此,在执行抽象之后,我们现在拥有的服务可以随时为您提供随机位(0或1)。

计算出你需要多少比特的随机性。因为你想要一个介于0到99999之间的数字,你只需要找到一个全1且大于或等于99999的二进制数。十进制99999等于二进制11000011010011111,这是17位长,所以你需要17随机性。

现在从服务中获得17位随机性并将它们组装成二进制数。该数字将介于0和2 ** 17-1(131071)之间,并且将均匀分布。如果随机数恰好大于99999,则丢弃你所拥有的位并再试一次。 (需要重试的概率应小于50%。)

最终你会得到一个0到99999之间的数字,这个算法应该给你一个完全均匀的分布。

答案 1 :(得分:0)

将您获得的个别数字视为16位随机性。要制作更大的随机数,您只需要更多位。棘手的一点是弄清楚多少位就足够了。例如,如果你想从0到65000的绝对公平分布生成数字,那么16位是不够的应该是非常明显的;即使你有覆盖范围,一些数字的选择概率也是其他数字的两倍。

这个问题有两种解决方法。使用Ruby的Bignum(技术上发生在幕后,它在Ruby中很好用,因为你不会溢出你的Integer类型)可以使用一个简单收集更多位的方法,直到除法的结果永远不会是模糊的 - 即,当你正在进行的除法中添加更多有效位时的差异永远不会改变结果。

这可能是这样的,使用QRandom.next方法获取16个批次中的位:

def QRandom.rand max
  max = max.to_i # This approach requires integers
  power = 1
  sum = 0

  loop do
    sum = 2**16 * sum + QRandom.next
    power *= 2**16
    lower_bound = sum * max / power
    break lower_bound if lower_bound == ( (sum + 1) * max ) / power
  end
end

因为从您选择的源中获取随机位需要花费相当多的成本,所以您可以从最有效的形式中获益,这在原理上类似于Arithmetic Coding并挤出最大可能的熵来自您的来源,同时在0...max生成无偏数。您需要实现一个方法QRandom.next_bits( num ),该方法返回一个由来自您的16位数字的比特流缓冲区构造的整数:

def QRandom.rand max
  max = max.to_i # This approach requires integers
  # I prefer this:  start_bits = Math.log2( max ).floor
  # But this also works (and avoids suggestions the algo uses FP):
  start_bits = max.to_s(2).length
  sum = QRandom.next_bits( start_bits )
  power = 2 ** start_bits

  # No need for fractional bits if max is power of 2
  return sum if power == max

  # Draw 1 bit at a time to resolve fractional powers of 2
  loop do
    lower_bound = (sum * max) / power
    break lower_bound if lower_bound == ((sum + 1) * max)/ power
    sum = 2 * sum + QRandom.next_bits(1) # 0 or 1
    power *= 2
  end
end

这可以最有效地利用源中的位。它总是与重试方案一样高效或更好。每次调用QRandom.rand( max )时使用的预期位数为1 + Math.log2( max ) - 即平均而言,这允许您仅绘制代表范围所需的小数位数。

答案 2 :(得分:0)

要求更多号码怎么样?使用该API的长度参数,您可以只需要额外的数字并将它们相加,这样您就可以获得更大的数字。

http://qrng.anu.edu.au/API/jsonI.php?type=uint16&length=2

你可以使用注入求和和模运算来确保数字不大于你想要的数量。

json["data"].inject(:+) % MAX_NUMBER

我对您的代码进行了一些其他更改,例如使用SecureRandom而不是常规的Random。你可以在这里找到代码:

https://gist.github.com/matugm/bee45bfe637f0abf8f29#file-qrandom-rb