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

时间:2017-12-30 04:35:02

标签: ruby performance ruby-2.4

假设您要生成1到10亿之间的随机数:

rand(1..1_000_000_000)

每次调用这行代码时,Ruby都会从该范围创建一个数组吗?

Rubocop建议这种方法超过rand(1_000_000_000)+1,但似乎有可能出现疼痛。

Ruby的博士说:

# When +max+ is a Range, +rand+ returns a random number where
# range.member?(number) == true.

+max+是传递给rand的参数,但它没有说明它是如何获得number参数的。我也不确定在一个范围内调用.member?是否具有高效性。

有什么想法吗?

我可以使用基准但仍然对这里的内部运作感到好奇。

1 个答案:

答案 0 :(得分:2)

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

Range类包含Enumerable,因此您可以获得Enumerable的迭代方法,而无需将范围转换为数组。范围的下限和上限为(-Float::INFINITY..Float::INFINITY),但如果将其传递给Numerical argument out of domain,则会导致rand错误。

对于.member?,该方法只调用一个名为range_cover的C函数调用另一个名为r_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)

正如您在第一个示例中所看到的,将range作为参数传递给rand非常快。

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

查看range have a look here的代码。