272 lines
8.4 KiB
Markdown
Vendored
272 lines
8.4 KiB
Markdown
Vendored
# mruby-random
|
|
|
|
mruby-random is an mrbgem that provides pseudo-random number generation facilities for mruby.
|
|
|
|
## Features
|
|
|
|
- Provides `Kernel#rand` method for generating pseudo-random numbers.
|
|
- Supports generating random numbers within a specific range.
|
|
- Allows setting a seed for reproducible random number sequences using `Kernel#srand`.
|
|
|
|
## Global Random Number Generation
|
|
|
|
### Generating a random number
|
|
|
|
To generate a pseudo-random floating-point number between 0.0 (inclusive) and 1.0 (exclusive):
|
|
|
|
```ruby
|
|
r = rand
|
|
p r # => 0.31415926535
|
|
```
|
|
|
|
To generate a pseudo-random integer number between 0 (inclusive) and a given maximum integer (exclusive):
|
|
|
|
```ruby
|
|
r = rand(100)
|
|
p r # => 42
|
|
```
|
|
|
|
To generate a pseudo-random integer within a given `Range`:
|
|
|
|
```ruby
|
|
r = rand(10..20) # or rand(10...20)
|
|
p r # => 15 (e.g., between 10 and 20, or 10 and 19)
|
|
```
|
|
|
|
### Seeding the random number generator
|
|
|
|
To initialize the pseudo-random number generator with a specific seed:
|
|
|
|
```ruby
|
|
srand(12345)
|
|
p rand(100) # => 81
|
|
p rand(100) # => 81 (if you re-seed with srand(12345) again)
|
|
|
|
# Using the same seed will produce the same sequence of random numbers
|
|
srand(12345)
|
|
p rand(100) # => 81
|
|
srand(12345)
|
|
p rand(100) # => 81
|
|
```
|
|
|
|
## The `Random` Class
|
|
|
|
Besides the global `Kernel#rand` and `Kernel#srand` methods, `mruby-random` also provides a `Random` class for managing separate random number generators.
|
|
|
|
### Creating an Instance
|
|
|
|
You can create a new instance of the `Random` class with a system-generated seed:
|
|
|
|
```ruby
|
|
rng = Random.new
|
|
p rng.rand(100)
|
|
```
|
|
|
|
Or you can provide a specific seed:
|
|
|
|
```ruby
|
|
rng = Random.new(12345)
|
|
p rng.rand(100) # => 81
|
|
```
|
|
|
|
### Instance Methods
|
|
|
|
#### `rand`
|
|
|
|
The `rand` instance method behaves similarly to `Kernel#rand`, but operates on the specific `Random` instance.
|
|
|
|
- Called with no arguments, it returns a pseudo-random floating-point number between 0.0 (inclusive) and 1.0 (exclusive).
|
|
|
|
```ruby
|
|
rng = Random.new
|
|
p rng.rand # => 0.123456789
|
|
```
|
|
|
|
- Called with an integer `max` argument, it returns a pseudo-random integer between 0 (inclusive) and `max` (exclusive).
|
|
|
|
```ruby
|
|
rng = Random.new
|
|
p rng.rand(50) # => 23
|
|
```
|
|
|
|
- Called with a `Range` argument (`min..max` or `min...max`), it returns a pseudo-random integer within that range (inclusive of `min`, and inclusive or exclusive of `max` depending on the range type).
|
|
|
|
```ruby
|
|
rng = Random.new
|
|
p rng.rand(10..20) # => 15 (between 10 and 20, inclusive)
|
|
p rng.rand(10...20) # => 12 (between 10 and 19, inclusive)
|
|
```
|
|
|
|
#### `srand`
|
|
|
|
The `srand` instance method is used to seed the specific `Random` instance. It allows you to re-initialize the random number generator for that instance with a specific seed, making its sequence of generated numbers predictable.
|
|
|
|
```ruby
|
|
rng = Random.new(111)
|
|
p rng.rand(1000) # => 100
|
|
p rng.rand(1000) # => 283
|
|
|
|
rng.srand(111) # Re-seed the same instance
|
|
p rng.rand(1000) # => 100 (sequence repeats for this instance)
|
|
|
|
rng2 = Random.new(111) # A different instance with the same seed
|
|
p rng2.rand(1000) # => 100
|
|
```
|
|
|
|
It is important to note that `Random#srand` is an alias for `Random#initialize`. Re-seeding an existing `Random` object will reset its internal state.
|
|
|
|
#### `bytes(n)`
|
|
|
|
The `bytes` method returns a string containing `n` pseudo-random bytes.
|
|
|
|
```ruby
|
|
rng = Random.new
|
|
p rng.bytes(5) # => "\xAB\xCD\xEF\x12\x34" (example output)
|
|
```
|
|
|
|
### Class Methods (Using the Default Generator)
|
|
|
|
The `Random` class also provides class methods that operate on a global, default random number generator. This is the same generator used by `Kernel#rand` and `Kernel#srand`.
|
|
|
|
#### `Random.rand`
|
|
|
|
This method is equivalent to `Kernel.rand` (or simply `rand`).
|
|
|
|
- With no arguments, returns a float between 0.0 and 1.0 (exclusive of 1.0):
|
|
|
|
```ruby
|
|
p Random.rand # => 0.7654321
|
|
```
|
|
|
|
- With an integer `max` argument, returns an integer between 0 and `max` (exclusive of `max`):
|
|
|
|
```ruby
|
|
p Random.rand(10) # => 7
|
|
```
|
|
|
|
- With a `Range` argument, returns an integer within the range:
|
|
|
|
```ruby
|
|
p Random.rand(50..60) # => 53
|
|
```
|
|
|
|
#### `Random.srand(seed)`
|
|
|
|
This method is equivalent to `Kernel.srand(seed)` (or simply `srand(seed)`). It seeds the global default random number generator.
|
|
|
|
```ruby
|
|
Random.srand(123)
|
|
p Random.rand(100) # => 13
|
|
p rand(100) # => 80 (uses the same seeded generator)
|
|
|
|
Random.srand(123)
|
|
p Random.rand(100) # => 13 (sequence repeats)
|
|
```
|
|
|
|
#### `Random.bytes(n)`
|
|
|
|
This method returns a string containing `n` pseudo-random bytes, generated by the global default random number generator.
|
|
|
|
```ruby
|
|
p Random.bytes(3) # => "\xDE\xAD\xBE" (example output)
|
|
```
|
|
|
|
## Array Methods
|
|
|
|
`mruby-random` extends the `Array` class with methods for shuffling elements and sampling random elements. These methods can optionally accept a `Random` instance to use a specific random number generator.
|
|
|
|
### `shuffle`
|
|
|
|
The `shuffle` method returns a _new_ array with the elements of the original array in a random order.
|
|
|
|
```ruby
|
|
a = [1, 2, 3, 4, 5]
|
|
p a.shuffle # => [3, 1, 5, 2, 4] (example output)
|
|
p a # => [1, 2, 3, 4, 5] (original array is unchanged)
|
|
```
|
|
|
|
You can provide a specific `Random` instance using the `random:` keyword argument. This is useful for reproducible shuffling.
|
|
|
|
```ruby
|
|
rng = Random.new(123)
|
|
a = [1, 2, 3, 4, 5]
|
|
p a.shuffle(random: rng) # => [1, 5, 3, 2, 4] (example output, will be consistent with seed 123)
|
|
|
|
rng2 = Random.new(123) # Same seed
|
|
p a.shuffle(random: rng2) # => [1, 5, 3, 2, 4] (same shuffled order)
|
|
```
|
|
|
|
### `shuffle!`
|
|
|
|
The `shuffle!` method shuffles the elements of the array in-place. It modifies the original array.
|
|
|
|
```ruby
|
|
a = [1, 2, 3, 4, 5]
|
|
p a.shuffle! # => [4, 2, 1, 5, 3] (example output)
|
|
p a # => [4, 2, 1, 5, 3] (original array is modified)
|
|
```
|
|
|
|
Similarly to `shuffle`, you can provide a specific `Random` instance using the `random:` keyword argument.
|
|
|
|
```ruby
|
|
rng = Random.new(456)
|
|
a = [:a, :b, :c, :d, :e]
|
|
p a.shuffle!(random: rng) # => [:c, :a, :e, :d, :b] (example output, consistent with seed 456)
|
|
p a # => [:c, :a, :e, :d, :b] (original array is modified)
|
|
```
|
|
|
|
### `sample`
|
|
|
|
The `sample` method chooses one or more random elements from the array.
|
|
|
|
- When called with no arguments, it returns a single random element from the array. If the array is empty, it returns `nil`.
|
|
|
|
```ruby
|
|
a = ["apple", "banana", "cherry", "date"]
|
|
p a.sample # => "cherry" (example output)
|
|
|
|
empty_array = []
|
|
p empty_array.sample # => nil
|
|
```
|
|
|
|
- When called with an integer `n` as an argument, it returns a new array containing `n` unique random elements from the original array. If the array does not have enough unique elements, it returns all elements in a shuffled order. If the array is empty, it returns an empty array.
|
|
|
|
```ruby
|
|
a = ["apple", "banana", "cherry", "date", "elderberry"]
|
|
p a.sample(3) # => ["date", "apple", "banana"] (example output)
|
|
p a.sample(10) # => ["banana", "elderberry", "apple", "date", "cherry"] (all elements, shuffled)
|
|
|
|
|
|
empty_array = []
|
|
p empty_array.sample(3) # => []
|
|
```
|
|
|
|
- You can provide a specific `Random` instance using the `random:` keyword argument for both forms of `sample`.
|
|
|
|
```ruby
|
|
rng = Random.new(789)
|
|
a = [10, 20, 30, 40, 50]
|
|
p a.sample(random: rng) # => 30 (example output, consistent with seed 789)
|
|
|
|
rng_b = Random.new(789) # Re-initialize with the same seed for predictable multi-sampling
|
|
p a.sample(2, random: rng_b) # => [30, 50] (example output)
|
|
```
|
|
|
|
## Algorithm
|
|
|
|
The `mruby-random` mrbgem uses the **PCG-XSH-RR** (Permuted Congruential Generator - XorShift High, Random Rotate) algorithm for pseudo-random number generation.
|
|
|
|
### Key Features
|
|
|
|
- **Compact State**: Uses only 64 bits of state (compared to 128 bits in the previous xoshiro128++ implementation), reducing memory footprint by 50%
|
|
- **Excellent Statistical Quality**: Passes rigorous statistical test suites (TestU01, PractRand)
|
|
- **Platform-Optimized**: Automatically adapts to platform characteristics for optimal performance
|
|
- On **32-bit platforms** (`MRB_32BIT`): Uses an optimized 32-bit multiplier that requires only 2 multiply operations instead of 3
|
|
- On **64-bit platforms**: Uses the standard 64-bit multiplier for maximum statistical quality
|
|
- **Fast Performance**: Competitive speed with modern PRNG algorithms while maintaining smaller memory footprint
|
|
|
|
The PCG family of algorithms was developed by Melissa O'Neill and is widely used in production systems. For more details, see <https://www.pcg-random.org/>.
|
|
|
|
## License
|
|
|
|
The `mruby-random` mrbgem is released under the MIT License. See the LICENSE file for details.
|