代码之家  ›  专栏  ›  技术社区  ›  John Bachir

Array#sample使用什么随机算法?

  •  2
  • John Bachir  · 技术社区  · 7 年前

    我想找出哪种随机算法 Array#sample 使用,但在Ruby C代码中我有点迷路了。

    1 回复  |  直到 7 年前
        1
  •  1
  •   John Bachir    7 年前

    我发现深入研究pry来检查实际的源代码很有帮助,尽管您也可以通过 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 Random类c用于随机性。

    仅仅代码并不能告诉我们很多关于算法的信息

    https://ruby-doc.org/core-2.5.0/Random.html 告诉我们它使用

    PRNGs目前是一种改进的Mersenne捻线机,周期为2**19937-1。

    那到底是什么 Mersenne Twister ? 我不知道,但听起来很酷 https://en.wikipedia.org/wiki/Mersenne_Twister

    推荐文章