我试图弄清楚Array#sample
使用哪种随机算法,但是在Ruby C代码中迷路了。
答案 0 :(得分:1)
我发现深入探究实际的源代码很有帮助,尽管您也可以通过转到Ruby Docs并将鼠标悬停在方法名称上以用放大镜显示click to toggle source
来找到它,这也将显示如果您已经完成gem install pry-doc
arr = []
cd arr
show-method sample
(#<Array>):1> show-method sample
From: array.c (C Method):
Owner: Array
Visibility: public
Number of lines: 103
static VALUE
rb_ary_sample(int argc, VALUE *argv, VALUE ary)
{
VALUE nv, result;
VALUE opts, randgen = rb_cRandom;
long n, len, i, j, k, idx[10];
long rnds[numberof(idx)];
if (OPTHASH_GIVEN_P(opts)) {
VALUE rnd;
ID keyword_ids[1];
keyword_ids[0] = id_random;
rb_get_kwargs(opts, keyword_ids, 0, 1, &rnd);
if (rnd != Qundef) {
randgen = rnd;
}
}
len = RARRAY_LEN(ary);
if (argc == 0) {
if (len < 2)
i = 0;
else
i = RAND_UPTO(len);
return rb_ary_elt(ary, i);
}
rb_scan_args(argc, argv, "1", &nv);
n = NUM2LONG(nv);
if (n < 0) rb_raise(rb_eArgError, "negative sample number");
if (n > len) n = len;
if (n <= numberof(idx)) {
for (i = 0; i < n; ++i) {
rnds[i] = RAND_UPTO(len - i);
}
}
k = len;
len = RARRAY_LEN(ary);
if (len < k && n <= numberof(idx)) {
for (i = 0; i < n; ++i) {
if (rnds[i] >= len) return rb_ary_new_capa(0);
}
}
if (n > len) n = len;
switch (n) {
case 0:
return rb_ary_new_capa(0);
case 1:
i = rnds[0];
return rb_ary_new_from_values(1, &RARRAY_AREF(ary, i));
case 2:
i = rnds[0];
j = rnds[1];
if (j >= i) j++;
return rb_ary_new_from_args(2, RARRAY_AREF(ary, i), RARRAY_AREF(ary, j));
case 3:
i = rnds[0];
j = rnds[1];
k = rnds[2];
{
long l = j, g = i;
if (j >= i) l = i, g = ++j;
if (k >= l && (++k >= g)) ++k;
}
return rb_ary_new_from_args(3, RARRAY_AREF(ary, i), RARRAY_AREF(ary, j), RARRAY_AREF(ary, k));
}
if (n <= numberof(idx)) {
long sorted[numberof(idx)];
sorted[0] = idx[0] = rnds[0];
for (i=1; i<n; i++) {
k = rnds[i];
for (j = 0; j < i; ++j) {
if (k < sorted[j]) break;
++k;
}
memmove(&sorted[j+1], &sorted[j], sizeof(sorted[0])*(i-j));
sorted[j] = idx[i] = k;
}
result = rb_ary_new_capa(n);
RARRAY_PTR_USE(result, ptr_result, {
for (i=0; i<n; i++) {
ptr_result[i] = RARRAY_AREF(ary, idx[i]);
}
});
}
else {
result = rb_ary_dup(ary);
RBASIC_CLEAR_CLASS(result);
RB_GC_GUARD(ary);
RARRAY_PTR_USE(result, ptr_result, {
for (i=0; i<n; i++) {
j = RAND_UPTO(len-i) + i;
nv = ptr_result[j];
ptr_result[j] = ptr_result[i];
ptr_result[i] = nv;
}
});
RBASIC_SET_CLASS_RAW(result, rb_cArray);
}
ARY_SET_LEN(result, n);
return result;
}
所以我们可以看到
VALUE opts, randgen = rb_cRandom;
这表示将使用ruby随机类c作为随机性。
仅凭代码并不能告诉我们很多有关算法的信息
因此,对https://ruby-doc.org/core-2.5.0/Random.html的查找告诉我们它使用了
目前,PRNG被作为经过修改的Mersenne Twister实施,期限为2 ** 19937-1。
Mersenne Twister
到底是什么?我不知道,但这听起来很酷,所以https://en.wikipedia.org/wiki/Mersenne_Twister向我们介绍了它,并且它已被广泛使用:
Mersenne Twister是以下软件系统的默认PRNG:Microsoft Excel,[3] GAUSS,[4] GLib,[5] GNU多精度算术库,[6] GNU Octave,[7] GNU科学库,[8] gretl,[9] IDL,[10]朱莉娅,[11] CMU通用Lisp,[12]嵌入式通用Lisp,[13]钢库通用Lisp,[14]枫木,[15] MATLAB,[16 ] Free Pascal,[17] PHP,[18] Python,[19] [20] R,[21] Ruby,[22] SageMath,[23] Scilab,[24] Stata。[25]