Class: Random

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 Random.srand.

The class method Random.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.

Random.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.

Defined Under Namespace

Modules: Formatter

Constant Summary collapse

DEFAULT =
rb_cRandom

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#new(seed = Random.new_seed) ⇒ Object

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

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


400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
# File 'random.c', line 400

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);
    }
    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.

Returns:


1223
1224
1225
1226
1227
1228
# File 'random.c', line 1223

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 Random.new when no seed value is specified as an argument.

Random.new_seed  #=> 115032730400174366788466674494640623225

Returns:


649
650
651
652
653
654
655
656
657
# File 'random.c', line 649

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

.randFloat .rand(max) ⇒ Numeric

Overloads:


1580
1581
1582
1583
1584
1585
1586
# File 'random.c', line 1580

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;
}

.seedObject


1230
1231
1232
1233
1234
1235
# File 'random.c', line 1230

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) ⇒ Object

Seeds the system pseudo-random number generator, with number. The previous seed value is returned.

If number is omitted, seeds the generator using a source of entropy provided by the operating system, if available (/dev/urandom on Unix systems or the RSA cryptographic provider on Windows), which is then combined with the time, the process id, and a sequence number.

srand may be used to ensure repeatable sequences of pseudo-random numbers between different runs of the program. By setting the seed to a known value, programs can be made deterministic during testing.

srand 1234               # => 268519324636777531569100071560086917274
[ rand, rand ]           # => [0.1915194503788923, 0.6221087710398319]
[ rand(10), rand(1000) ] # => [4, 664]
srand 1234               # => 1234
[ rand, rand ]           # => [0.1915194503788923, 0.6221087710398319]

860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
# File 'random.c', line 860

static VALUE
rb_f_srand(int argc, VALUE *argv, VALUE obj)
{
    VALUE seed, old;
    rb_random_mt_t *r = rand_mt_start(default_rand());

    if (rb_check_arity(argc, 0, 1) == 0) {
        seed = random_seed(obj);
    }
    else {
	seed = rb_to_int(argv[0]);
    }
    old = r->base.seed;
    rand_init(&random_mt_if, &r->base, seed);
    r->base.seed = seed;

    return old;
}

.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"

Returns:


675
676
677
678
679
680
681
682
683
684
# File 'random.c', line 675

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

Returns:

  • (Boolean)

1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
# File 'random.c', line 1512

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

Returns:


1177
1178
1179
1180
1181
1182
# File 'random.c', line 1177

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) ⇒ Object

:nodoc:


707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
# File 'random.c', line 707

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;
}

#leftObject (private)

:nodoc:


748
749
750
751
752
753
# File 'random.c', line 748

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

#marshal_dumpObject (private)

:nodoc:


763
764
765
766
767
768
769
770
771
772
773
774
# File 'random.c', line 763

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) ⇒ Object (private)

:nodoc:


777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
# File 'random.c', line 777

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)) {
	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

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 max 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.

Overloads:


1431
1432
1433
1434
1435
1436
1437
# File 'random.c', line 1431

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

Returns:


700
701
702
703
704
# File 'random.c', line 700

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

#stateObject (private)

:nodoc:


733
734
735
736
737
738
# File 'random.c', line 733

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