123456789_123456789_123456789_123456789_123456789_

Class: Random

Relationships & Source Files
Namespace Children
Modules:
Defined in: random.c

Overview

Random provides an interface to Ruby’s pseudo-random number generator, or PRNG. The PRNG produces a deterministic sequence of bits which approximate true randomness. The sequence may be represented by integers, floats, or binary strings.

The generator may be initialized with either a system-generated or user-supplied seed value by using .srand.

The class method .rand provides the base functionality of Kernel.rand along with better handling of floating point values. These are both interfaces to the Ruby system PRNG.

.new will create a new PRNG with a state independent of the Ruby system PRNG, allowing multiple generators with different seed values or sequence positions to exist simultaneously. Random objects can be marshaled, allowing sequences to be saved and resumed.

PRNGs are currently implemented as a modified Mersenne Twister with a period of 2**19937-1. As this algorithm is not for cryptographical use, you must use SecureRandom for security purpose, instead of this PRNG.

See also Formatter module that adds convenience methods to generate various forms of random data.

Class Method Summary

Instance Method Summary

Constructor Details

.new(seed = Random.new_seed) ⇒ prng

Creates a new PRNG using .seed to set the initial state. If .seed is omitted, the generator is initialized with .new_seed.

See .srand for more information on the use of seed values.

[ GitHub ]

  
# File 'random.c', line 403

static VALUE
random_init(int argc, VALUE *argv, VALUE obj)
{
    rb_random_t *rnd = try_get_rnd(obj);
    const rb_random_interface_t *rng = rb_rand_if(obj);

    if (!rng) {
        rb_raise(rb_eTypeError, "undefined random interface: %s",
                 RTYPEDDATA_TYPE(obj)->wrap_struct_name);
    }

    unsigned int major = rng->version.major;
    unsigned int minor = rng->version.minor;
    if (major != RUBY_RANDOM_INTERFACE_VERSION_MAJOR) {
        rb_raise(rb_eTypeError, "Random interface version "
                 STRINGIZE(RUBY_RANDOM_INTERFACE_VERSION_MAJOR) "."
                 STRINGIZE(RUBY_RANDOM_INTERFACE_VERSION_MINOR) " "
                 "expected: %d.%d", major, minor);
    }
    argc = rb_check_arity(argc, 0, 1);
    rb_check_frozen(obj);
    if (argc == 0) {
        rnd->seed = rand_init_default(rng, rnd);
    }
    else {
        rnd->seed = rand_init(rng, rnd, rb_to_int(argv[0]));
    }
    return obj;
}

Class Method Details

.bytes(size) ⇒ String

Returns a random binary string. The argument size specifies the length of the returned string.

[ GitHub ]

  
# File 'random.c', line 1321

static VALUE
random_s_bytes(VALUE obj, VALUE len)
{
    rb_random_t *rnd = rand_start(default_rand());
    return rand_bytes(&random_mt_if, rnd, NUM2LONG(rb_to_int(len)));
}

.new_seedInteger

Returns an arbitrary seed value. This is used by .new when no seed value is specified as an argument.

Random.new_seed  #=> 115032730400174366788466674494640623225
[ GitHub ]

  
# File 'random.c', line 745

static VALUE
random_seed(VALUE _)
{
    VALUE v;
    with_random_seed(DEFAULT_SEED_CNT, 1, true) {
        v = make_seed_value(seedbuf, DEFAULT_SEED_CNT);
    }
    return v;
}

.randFloat .rand(max) ⇒ Numeric .rand(range) ⇒ Numeric

Returns a random number using the Ruby system PRNG.

See also #rand.

[ GitHub ]

  
# File 'random.c', line 1700

static VALUE
random_s_rand(int argc, VALUE *argv, VALUE obj)
{
    VALUE v = rand_random(argc, argv, Qnil, rand_start(default_rand()));
    check_random_number(v, argv);
    return v;
}

.seedInteger

Returns the seed value used to initialize the Ruby system PRNG. This may be used to initialize another generator with the same state at a later time, causing it to produce the same sequence of numbers.

Random.seed      #=> 1234
prng1 = Random.new(Random.seed)
prng1.seed       #=> 1234
prng1.rand(100)  #=> 47
Random.seed      #=> 1234
Random.rand(100) #=> 47
[ GitHub ]

  
# File 'random.c', line 1343

static VALUE
random_s_seed(VALUE obj)
{
    rb_random_mt_t *rnd = rand_mt_start(default_rand());
    return rnd->base.seed;
}

.srand(number = Random.new_seed) ⇒ old_seed

Alias for Kernel.srand.

.urandom(size) ⇒ String

Returns a string, using platform providing features. Returned value is expected to be a cryptographically secure pseudo-random number in binary form. This method raises a ::RuntimeError if the feature provided by platform failed to prepare the result.

In 2017, Linux manpage random(7) writes that “no cryptographic primitive available today can hope to promise more than 256 bits of security”. So it might be questionable to pass size > 32 to this method.

Random.urandom(8)  #=> "\x78\x41\xBA\xAF\x7D\xEA\xD8\xEA"
[ GitHub ]

  
# File 'random.c', line 771

static VALUE
random_raw_seed(VALUE self, VALUE size)
{
    long n = NUM2ULONG(size);
    VALUE buf = rb_str_new(0, n);
    if (n == 0) return buf;
    if (fill_random_bytes(RSTRING_PTR(buf), n, TRUE))
        rb_raise(rb_eRuntimeError, "failed to get urandom");
    return buf;
}

Instance Method Details

#==(prng2) ⇒ Boolean

Returns true if the two generators have the same internal state, otherwise false. Equivalent generators will return the same sequence of pseudo-random numbers. Two generators will generally have the same state only if they were initialized with the same seed

Random.new == Random.new             # => false
Random.new(1234) == Random.new(1234) # => true

and have the same invocation history.

prng1 = Random.new(1234)
prng2 = Random.new(1234)
prng1 == prng2 # => true

prng1.rand     # => 0.1915194503788923
prng1 == prng2 # => false

prng2.rand     # => 0.1915194503788923
prng1 == prng2 # => true
[ GitHub ]

  
# File 'random.c', line 1628

static VALUE
rand_mt_equal(VALUE self, VALUE other)
{
    rb_random_mt_t *r1, *r2;
    if (rb_obj_class(self) != rb_obj_class(other)) return Qfalse;
    r1 = get_rnd_mt(self);
    r2 = get_rnd_mt(other);
    if (memcmp(r1->mt.state, r2->mt.state, sizeof(r1->mt.state))) return Qfalse;
    if ((r1->mt.next - r1->mt.state) != (r2->mt.next - r2->mt.state)) return Qfalse;
    if (r1->mt.left != r2->mt.left) return Qfalse;
    return rb_equal(r1->base.seed, r2->base.seed);
}

#bytes(size) ⇒ String

Returns a random binary string containing size bytes.

random_string = Random.new.bytes(10) # => "\xD7:R\xAB?\x83\xCE\xFAkO"
random_string.size                   # => 10
[ GitHub ]

  
# File 'random.c', line 1275

static VALUE
random_bytes(VALUE obj, VALUE len)
{
    rb_random_t *rnd = try_get_rnd(obj);
    return rand_bytes(rb_rand_if(obj), rnd, NUM2LONG(rb_to_int(len)));
}

#initialize_copy(orig)

This method is for internal use only.
[ GitHub ]

  
# File 'random.c', line 803

static VALUE
rand_mt_copy(VALUE obj, VALUE orig)
{
    rb_random_mt_t *rnd1, *rnd2;
    struct MT *mt;

    if (!OBJ_INIT_COPY(obj, orig)) return obj;

    rnd1 = get_rnd_mt(obj);
    rnd2 = get_rnd_mt(orig);
    mt = &rnd1->mt;

    *rnd1 = *rnd2;
    mt->next = mt->state + numberof(mt->state) - mt->left + 1;
    return obj;
}

#left (private)

This method is for internal use only.
[ GitHub ]

  
# File 'random.c', line 844

static VALUE
rand_mt_left(VALUE obj)
{
    rb_random_mt_t *rnd = get_rnd_mt(obj);
    return INT2FIX(rnd->mt.left);
}

#marshal_dump (private)

This method is for internal use only.
[ GitHub ]

  
# File 'random.c', line 859

static VALUE
rand_mt_dump(VALUE obj)
{
    rb_random_mt_t *rnd = rb_check_typeddata(obj, &random_mt_type);
    VALUE dump = rb_ary_new2(3);

    rb_ary_push(dump, mt_state(&rnd->mt));
    rb_ary_push(dump, INT2FIX(rnd->mt.left));
    rb_ary_push(dump, rnd->base.seed);

    return dump;
}

#marshal_load(dump) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'random.c', line 873

static VALUE
rand_mt_load(VALUE obj, VALUE dump)
{
    rb_random_mt_t *rnd = rb_check_typeddata(obj, &random_mt_type);
    struct MT *mt = &rnd->mt;
    VALUE state, left = INT2FIX(1), seed = INT2FIX(0);
    unsigned long x;

    rb_check_copyable(obj, dump);
    Check_Type(dump, T_ARRAY);
    switch (RARRAY_LEN(dump)) {
      case 3:
        seed = RARRAY_AREF(dump, 2);
      case 2:
        left = RARRAY_AREF(dump, 1);
      case 1:
        state = RARRAY_AREF(dump, 0);
        break;
      default:
        rb_raise(rb_eArgError, "wrong dump data");
    }
    rb_integer_pack(state, mt->state, numberof(mt->state),
        sizeof(*mt->state), 0,
        INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER);
    x = NUM2ULONG(left);
    if (x > numberof(mt->state) || x == 0) {
        rb_raise(rb_eArgError, "wrong value");
    }
    mt->left = (unsigned int)x;
    mt->next = mt->state + numberof(mt->state) - x + 1;
    rnd->base.seed = rb_to_int(seed);

    return obj;
}

#randFloat #rand(max) ⇒ Numeric #rand(range) ⇒ Numeric

When max is an ::Integer, rand returns a random integer greater than or equal to zero and less than max. Unlike Kernel.rand, when max is a negative integer or zero, rand raises an ::ArgumentError.

prng = Random.new
prng.rand(100)       # => 42

When max is a ::Float, rand returns a random floating point number between 0.0 and max, including 0.0 and excluding max.

prng.rand(1.5)       # => 1.4600282860034115

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

prng.rand(5..9)      # => one of [5, 6, 7, 8, 9]
prng.rand(5...9)     # => one of [5, 6, 7, 8]
prng.rand(5.0..9.0)  # => between 5.0 and 9.0, including 9.0
prng.rand(5.0...9.0) # => between 5.0 and 9.0, excluding 9.0

Both the beginning and ending values of the range must respond to subtract (-) and add (+)methods, or rand will raise an ::ArgumentError.

[ GitHub ]

  
# File 'random.c', line 1545

static VALUE
random_rand(int argc, VALUE *argv, VALUE obj)
{
    VALUE v = rand_random(argc, argv, obj, try_get_rnd(obj));
    check_random_number(v, argv);
    return v;
}

#seedInteger

Returns the seed value used to initialize the generator. This may be used to initialize another generator with the same state at a later time, causing it to produce the same sequence of numbers.

prng1 = Random.new(1234)
prng1.seed       #=> 1234
prng1.rand(100)  #=> 47

prng2 = Random.new(prng1.seed)
prng2.rand(100)  #=> 47
[ GitHub ]

  
# File 'random.c', line 796

static VALUE
random_get_seed(VALUE obj)
{
    return get_rnd(obj)->seed;
}

#state (private)

This method is for internal use only.
[ GitHub ]

  
# File 'random.c', line 829

static VALUE
rand_mt_state(VALUE obj)
{
    rb_random_mt_t *rnd = get_rnd_mt(obj);
    return mt_state(&rnd->mt);
}