在Ruby中生成一个随机数有多贵?

在Ruby中生成一个随机数有多贵?,ruby,performance,ruby-2.4,Ruby,Performance,Ruby 2.4,假设您要生成一个介于10亿到10亿之间的随机数: rand(1..1_000_000_000) Ruby会在每次调用这行代码时从该范围创建一个数组吗 Rubocop建议这种方法优于rand1_000_000_000+1,但似乎存在潜在的疼痛 Ruby的文档说: # When +max+ is a Range, +rand+ returns a random number where # range.member?(number) == true. 其中+max+是传递给rand的参数,但它没

假设您要生成一个介于10亿到10亿之间的随机数:

rand(1..1_000_000_000)
Ruby会在每次调用这行代码时从该范围创建一个数组吗

Rubocop建议这种方法优于rand1_000_000_000+1,但似乎存在潜在的疼痛

Ruby的文档说:

# When +max+ is a Range, +rand+ returns a random number where
# range.member?(number) == true.
其中+max+是传递给rand的参数,但它没有说明如何获取number参数。我也不确定是否打电话。成员?在一个靶场上,它的性能很好

有什么想法吗


我可以使用基准测试,但仍然对这里的内部工作方式感到好奇

不,Ruby不会从该范围创建数组,除非您显式调用.to\u范围对象上的方法。事实上,rand不适用于数组-。sample是用于从数组返回随机元素的方法

Range类包括Enumerable,因此您可以获得Enumerable的迭代方法,而无需将范围转换为数组。范围的下限和上限是-Float::INFINITY..Float::INFINITY,但如果将其传递给rand,则会导致数值参数域外错误

至于.member?,该方法只调用一个名为range\u cover的C函数,该函数调用另一个名为r\u cover\p的函数,该函数检查一个值是否在两个数字或字符串之间

要测试将范围传递给rand和调用阵列上的sample之间的速度差异,可以执行以下测试:

require 'benchmark'

puts Benchmark.measure { rand(0..10_000_000) }
=> 0.000000   0.000000   0.000000 (  0.000009)

puts Benchmark.measure { (0..10_000_000).to_a.sample }
=> 0.300000   0.030000   0.330000 (  0.347752)
正如您在第一个示例中所看到的,将一个范围作为参数传递给rand非常迅速

相反,在一个范围内调用.to_.sample相当慢。这是由于阵列创建过程需要将适当的数据分配到内存中。.sample方法应该相对较快,因为它只是将一个随机且唯一的索引传递到数组中并返回该元素


查看范围的代码。

如果文档特别提到范围,您可以非常肯定它是有效的。您也可以尝试使用比现有内存更多的内存:rand1..2**64您可以扩展吗?我知道它们在枚举时是惰性加载的,但为什么它们是高效的呢?比如,为什么会出现这种情况?您询问是否创建了数组。正如另一位Ryan所说,您可以通过请求一个随机数来轻松测试,该随机数的范围非常大,以至于为其分配一个数组会消耗比可用内存更多的内存。如果代码正常工作,则不会创建任何数组;如果代码开始对交换文件进行抖动,或引发内存不足异常,则会创建中间数组。调用成员?在一个范围是常数的情况下,它是左的,因为您似乎满足于可以计算rand1_000_000_000+1而不创建数组:randa..b可以计算randb-a+1+a。