ruby-changes:25074
From: nobu <ko1@a...>
Date: Tue, 9 Oct 2012 18:01:51 +0900 (JST)
Subject: [ruby-changes:25074] nobu:r37126 (trunk): array.c: use rb_random_ulong_limited
nobu 2012-10-09 18:01:40 +0900 (Tue, 09 Oct 2012) New Revision: 37126 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=37126 Log: array.c: use rb_random_ulong_limited * array.c (rb_ary_sample): use rb_random_ulong_limited, since precision of long may be larger than double. Modified files: trunk/ChangeLog trunk/NEWS trunk/array.c trunk/test/ruby/test_array.rb trunk/test/ruby/test_rand.rb Index: array.c =================================================================== --- array.c (revision 37125) +++ array.c (revision 37126) @@ -3996,7 +3996,7 @@ (argc > 0 && !NIL_P((opts) = rb_check_hash_type(argv[argc-1])) && (--argc, 1)) static VALUE sym_random; -#define RAND_UPTO(max) (long)(rb_random_real(randgen)*(max)) +#define RAND_UPTO(max) (long)rb_random_ulong_limited((randgen), (max)-1) /* * call-seq: @@ -4091,7 +4091,7 @@ VALUE nv, result, *ptr; VALUE opts, randgen = rb_cRandom; long n, len, i, j, k, idx[10]; - double rnds[numberof(idx)]; + long rnds[numberof(idx)]; if (OPTHASH_GIVEN_P(opts)) { randgen = rb_hash_lookup2(opts, sym_random, randgen); @@ -4104,11 +4104,11 @@ i = 0; } else { - double x = rb_random_real(randgen); - if ((len = RARRAY_LEN(ary)) == 0) return Qnil; - i = (long)(x * len); + i = RAND_UPTO(len); + if ((len = RARRAY_LEN(ary)) <= i) return Qnil; + ptr = RARRAY_PTR(ary); } - return RARRAY_PTR(ary)[i]; + return ptr[i]; } rb_scan_args(argc, argv, "1", &nv); n = NUM2LONG(nv); @@ -4116,27 +4116,37 @@ if (n > len) n = len; if (n <= numberof(idx)) { for (i = 0; i < n; ++i) { - rnds[i] = rb_random_real(randgen); + rnds[i] = RAND_UPTO(len - i); } } + k = len; len = RARRAY_LEN(ary); ptr = RARRAY_PTR(ary); + if (len < k) { + if (n <= numberof(idx)) { + for (i = 0; i < n; ++i) { + if (rnds[i] >= len) { + return rb_ary_new2(0); + } + } + } + } if (n > len) n = len; switch (n) { case 0: return rb_ary_new2(0); case 1: - i = (long)(rnds[0] * len); + i = rnds[0]; return rb_ary_new4(1, &ptr[i]); case 2: - i = (long)(rnds[0] * len); - j = (long)(rnds[1] * (len-1)); + i = rnds[0]; + j = rnds[1]; if (j >= i) j++; return rb_ary_new3(2, ptr[i], ptr[j]); case 3: - i = (long)(rnds[0] * len); - j = (long)(rnds[1] * (len-1)); - k = (long)(rnds[2] * (len-2)); + i = rnds[0]; + j = rnds[1]; + k = rnds[2]; { long l = j, g = i; if (j >= i) l = i, g = ++j; @@ -4147,9 +4157,9 @@ if (n <= numberof(idx)) { VALUE *ptr_result; long sorted[numberof(idx)]; - sorted[0] = idx[0] = (long)(rnds[0] * len); + sorted[0] = idx[0] = rnds[0]; for (i=1; i<n; i++) { - k = (long)(rnds[i] * --len); + k = rnds[i]; for (j = 0; j < i; ++j) { if (k < sorted[j]) break; ++k; Index: ChangeLog =================================================================== --- ChangeLog (revision 37125) +++ ChangeLog (revision 37126) @@ -1,5 +1,8 @@ -Tue Oct 9 17:59:00 2012 Nobuyoshi Nakada <nobu@r...> +Tue Oct 9 18:01:37 2012 Nobuyoshi Nakada <nobu@r...> + * array.c (rb_ary_sample): use rb_random_ulong_limited, since + precision of long may be larger than double. + * random.c (rb_random_ulong_limited): new function to return a random value from 0 upto limit as unsigned long, simillary to rb_genrand_ulong_limited but with arbitrary RNG object. Index: NEWS =================================================================== --- NEWS (revision 37125) +++ NEWS (revision 37126) @@ -17,6 +17,11 @@ * builtin classes + * Array + * incompatible changes: + * random parameter of Array#shuffle! and Array#sample now + will be called with one argument, maximum value. + * Enumerable * added method: * added Enumerable#lazy method for lazy enumeration. Index: test/ruby/test_array.rb =================================================================== --- test/ruby/test_array.rb (revision 37125) +++ test/ruby/test_array.rb (revision 37126) @@ -2019,15 +2019,15 @@ def test_sample_random ary = (0...10000).to_a assert_raise(ArgumentError) {ary.sample(1, 2, random: nil)} - gen0 = proc do - 0.5 + gen0 = proc do |max| + (max+1)/2 end class << gen0 alias rand call end - gen1 = proc do + gen1 = proc do |max| ary.replace([]) - 0.5 + (max+1)/2 end class << gen1 alias rand call Index: test/ruby/test_rand.rb =================================================================== --- test/ruby/test_rand.rb (revision 37125) +++ test/ruby/test_rand.rb (revision 37126) @@ -170,8 +170,9 @@ def test_shuffle srand(0) - assert_equal([1,4,2,5,3], [1,2,3,4,5].shuffle) - assert_equal([1,4,2,5,3], [1,2,3,4,5].shuffle(random: Random.new(0))) + result = [*1..5].shuffle + assert_equal([*1..5], result.sort) + assert_equal(result, [*1..5].shuffle(random: Random.new(0))) end def test_big_seed -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/