在 Ruby 中生成一个随机数有多昂贵?
How expensive is generating a random number in Ruby?
假设您想生成一个介于 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?
是否有效。
有什么想法吗?
我可以使用基准测试,但仍然对这里的内部工作原理感到好奇。
不,Ruby 不会从该范围创建数组,除非您在 Range
对象上显式调用 .to_a
方法。事实上,rand()
不适用于数组 - .sample
是用于从数组返回随机元素的方法。
Range
class 包括 Enumerable
因此您无需将范围转换为数组即可获得 Enumerable 的迭代方法。 Range 的下限和上限是 (-Float::INFINITY..Float::INFINITY)
,尽管如果将其传递到 rand
.
会导致 Numerical argument out of domain
错误
至于 .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
方法应该相对较快,因为它只是将随机且唯一的索引传递到数组中,然后 returns 该元素。
查看 range
have a look here 的代码。
假设您想生成一个介于 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?
是否有效。
有什么想法吗?
我可以使用基准测试,但仍然对这里的内部工作原理感到好奇。
不,Ruby 不会从该范围创建数组,除非您在 Range
对象上显式调用 .to_a
方法。事实上,rand()
不适用于数组 - .sample
是用于从数组返回随机元素的方法。
Range
class 包括 Enumerable
因此您无需将范围转换为数组即可获得 Enumerable 的迭代方法。 Range 的下限和上限是 (-Float::INFINITY..Float::INFINITY)
,尽管如果将其传递到 rand
.
Numerical argument out of domain
错误
至于 .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
方法应该相对较快,因为它只是将随机且唯一的索引传递到数组中,然后 returns 该元素。
查看 range
have a look here 的代码。