Array#sample使用哪种随机算法?

时间:2018-10-30 18:51:58

标签: ruby cryptography

我试图弄清楚Array#sample使用哪种随机算法,但是在Ruby C代码中迷路了。

1 个答案:

答案 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]