類別 Random
Random
提供介面給 Ruby 的偽亂數產生器,或稱 PRNG。PRNG 產生一組確定性的位元序列,近似於真正的亂數。序列可以用整數、浮點數或二進位字串表示。
產生器可以透過使用 Random.srand
初始化,使用系統產生的或使用者提供的種子值。
類別方法 Random.rand
提供 Kernel.rand
的基本功能,以及更好的浮點數值處理。這些都是 Ruby 系統 PRNG 的介面。
Random.new
將建立一個新的 PRNG,其狀態獨立於 Ruby 系統 PRNG,允許多個具有不同種子值或序列位置的產生器同時存在。 Random
物件可以被序列化,允許序列儲存和繼續。
PRNG 目前實現為修改過的梅森旋轉演算法,週期為 2**19937-1。由於此演算法不適用於加密用途,因此您必須使用 SecureRandom
來確保安全性,而不是此 PRNG。
另請參閱 Random::Formatter
模組,它新增了便利的方法來產生各種形式的亂數資料。
公開類別方法
傳回一個亂數二進位字串。參數 size
指定傳回字串的長度。
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))); }
使用 seed
建立一個新的 PRNG 來設定初始狀態。如果省略 seed
,產生器會使用 Random.new_seed
初始化。
有關種子值的用途,請參閱 Random.srand
。
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; }
傳回一個任意的種子值。當沒有指定種子值作為參數時,Random.new
會使用此值。
Random.new_seed #=> 115032730400174366788466674494640623225
static VALUE random_seed(VALUE _) { VALUE v; with_random_seed(DEFAULT_SEED_CNT, 1) { v = make_seed_value(seedbuf, DEFAULT_SEED_CNT); } return v; }
使用 Ruby 系統 PRNG 傳回一個亂數。
另請參閱 Random#rand
。
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; }
傳回用於初始化 Ruby 系統 PRNG 的種子值。這可以用於稍後使用相同狀態初始化另一個產生器,使其產生相同的數字序列。
Random.seed #=> 1234 prng1 = Random.new(Random.seed) prng1.seed #=> 1234 prng1.rand(100) #=> 47 Random.seed #=> 1234 Random.rand(100) #=> 47
static VALUE random_s_seed(VALUE obj) { rb_random_mt_t *rnd = rand_mt_start(default_rand()); return rnd->base.seed; }
使用 數字
為系統偽亂數產生器設定種子。將傳回前一個種子值。
如果省略 數字
,則使用作業系統提供的熵來源 (如果可用,Unix 系統上的 /dev/urandom 或 Windows 上的 RSA 加密提供者) 為產生器設定種子,然後再與時間、程序 ID 和序列號合併。
srand 可用於確保在程式不同執行之間,偽亂數的序列可重複。透過將種子設定為已知值,可以在測試期間讓程式具有確定性。
srand 1234 # => 268519324636777531569100071560086917274 [ rand, rand ] # => [0.1915194503788923, 0.6221087710398319] [ rand(10), rand(1000) ] # => [4, 664] srand 1234 # => 1234 [ rand, rand ] # => [0.1915194503788923, 0.6221087710398319]
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; }
使用平台提供的功能傳回字串。傳回值預期是二進位形式的密碼安全偽亂數。如果平台提供的功能無法準備結果,此方法會引發 RuntimeError
。
在 2017 年,Linux 手冊頁 random(7) 寫道:「目前沒有任何密碼原語可以保證超過 256 位元安全性」。因此,將大小 > 32 傳遞給此方法可能會受到質疑。
Random.urandom(8) #=> "\x78\x41\xBA\xAF\x7D\xEA\xD8\xEA"
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; }
公開實例方法
如果兩個產生器具有相同的內部狀態,則傳回 true,否則傳回 false。等效產生器將傳回相同的偽亂數序列。兩個產生器通常只有在使用相同的種子初始化,
Random.new == Random.new # => false Random.new(1234) == Random.new(1234) # => true
且具有相同的呼叫歷程時,才會具有相同的狀態。
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
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); }
傳回包含 大小
位元的亂數二進位字串。
random_string = Random.new.bytes(10) # => "\xD7:R\xAB?\x83\xCE\xFAkO" random_string.size # => 10
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))); }
當 max
是 Integer
時,rand
會傳回一個大於或等於零且小於 max
的亂數整數。與 Kernel.rand
不同,當 max
是負整數或零時,rand
會引發 ArgumentError
。
prng = Random.new prng.rand(100) # => 42
當 max
是 Float
時,rand
會傳回 0.0 到 max
之間的亂數浮點數,包括 0.0 但不包括 max
。
prng.rand(1.5) # => 1.4600282860034115
當 range
是 Range
時,rand
會傳回一個亂數,其中 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
範圍的起始值和結束值都必須回應減法 (-
) 和加法 (+
) 方法,否則 rand 將會引發 ArgumentError
。
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; }
傳回用於初始化產生器的種子值。這可以用於在稍後時間使用相同的狀態初始化另一個產生器,導致它產生相同的數字序列。
prng1 = Random.new(1234) prng1.seed #=> 1234 prng1.rand(100) #=> 47 prng2 = Random.new(prng1.seed) prng2.rand(100) #=> 47
static VALUE random_get_seed(VALUE obj) { return get_rnd(obj)->seed; }