diff --git a/mrbgems/mruby-random/README.md b/mrbgems/mruby-random/README.md new file mode 100644 index 000000000..b57316b84 --- /dev/null +++ b/mrbgems/mruby-random/README.md @@ -0,0 +1,263 @@ +# 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 primarily utilizes the **xoshiro128++ 1.0** algorithm for pseudo-random number generation. + +In certain 32-bit configurations (specifically when `MRB_INT32` is defined and `MRB_64BIT` is not), a different algorithm, **XORSHIFT96**, might be employed. + +## License + +The `mruby-random` mrbgem is released under the MIT License. See the [LICENSE](LICENSE) file for details.