我有以下代码:
def shuffle arr
shuffled = []
while arr.length > 0
randNum = rand(arr.length)
idx = 0
unshuf = []
arr.each do |item|
if idx == randNum
shuffled.push item
else
unshuf.push item
end
idx = idx + 1
end
arr = unshuf
end
return shuffled
end
puts(shuffle([ 11, 12, 13]))
我试图了解数组unshuf
的状态,因为方法shuffle
被调用。每次调用方法unshuf
之前,数组each
都会重置为空数组。在此类实施下,如何将unshuf
的数字保留为下一轮if
条件?
答案 0 :(得分:3)
代码的工作原理如下:
创建一个空数组,其中将存储最终结果。
获取0和初始数组之间的随机数。
毋庸置疑,这很笨拙。此外,它以非常unRuby的方式编写,保留手动索引并使用randNum
等变量名称。这大致相同:
def shuffle(arr)
dup = arr.dup
arr.size.times.map do # actually arr.map would work
dup.slice!(rand(dup.size))
end
end
但正如其他人所说,[1,2,3].shuffle
是标准的Ruby。
答案 1 :(得分:2)
我不知道你从哪里得到上面的代码。 Ruby Array #shuffle方法调用#shuffle!本机方法rb_ary_shuffle_bang
,如下所示:
static VALUE
rb_ary_shuffle_bang(int argc, VALUE *argv, VALUE ary)
{
VALUE *ptr, opts, *snap_ptr, randgen = rb_cRandom;
long i, snap_len;
if (OPTHASH_GIVEN_P(opts)) {
randgen = rb_hash_lookup2(opts, sym_random, randgen);
}
if (argc > 0) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
}
rb_ary_modify(ary);
i = RARRAY_LEN(ary);
ptr = RARRAY_PTR(ary);
snap_len = i;
snap_ptr = ptr;
while (i) {
long j = RAND_UPTO(i);
VALUE tmp;
if (snap_len != RARRAY_LEN(ary) || snap_ptr != RARRAY_PTR(ary)) {
rb_raise(rb_eRuntimeError, "modified during shuffle");
}
tmp = ptr[--i];
ptr[i] = ptr[j];
ptr[j] = tmp;
}
return ary;
}
在这里你可以看到正确的改组方法。忽略上面代码的上半部分,只注意while
循环,它从上到下遍历数组,并始终从索引i
下面的部分中随机选取一个元素并将其移动到i
的当前位置,继续递减i
。其复杂度为O(n),n =数组长度,因为它应该用于混洗。
然而,你书中的代码工作方式完全不同:它总是从数组中选择一个元素(rand( arr.length )
),然后遍历数组以查看具有该元素的元素遇到索引,给出复杂度O(n ** 2/2)。您的代码也提供了使用#push
方法的一个很好的示例,尽管#<<
已经足够了,因为每次只推送一个元素。您的代码提供了在idx
循环内手动维护索引each
的错误示例。应该使用#each_with_index
方法。当然,出于演示目的,应该完全删除内部循环,并且应该使用类似于本地#shuffle
方法的方法。
答案 2 :(得分:1)
def shuffle arr
shuffled = []
while arr.length > 0
randNum = rand(arr.length)
idx = 0
unshuf = []
print '--- arr='; p arr # <--------
arr.each do |item|
if idx == randNum
shuffled.push item
else
unshuf.push item
end
print 'shuffled='; print shuffled; print ', unshuf='; p unshuf # <--------
idx = idx + 1
end
arr = unshuf
end
return shuffled
end
p shuffle([ 11, 12, 13, 14, 15])
执行:
$ ruby -w t.rb
--- arr=[11, 12, 13, 14, 15]
shuffled=[], unshuf=[11]
shuffled=[], unshuf=[11, 12]
shuffled=[13], unshuf=[11, 12]
shuffled=[13], unshuf=[11, 12, 14]
shuffled=[13], unshuf=[11, 12, 14, 15]
--- arr=[11, 12, 14, 15]
shuffled=[13, 11], unshuf=[]
shuffled=[13, 11], unshuf=[12]
shuffled=[13, 11], unshuf=[12, 14]
shuffled=[13, 11], unshuf=[12, 14, 15]
--- arr=[12, 14, 15]
shuffled=[13, 11, 12], unshuf=[]
shuffled=[13, 11, 12], unshuf=[14]
shuffled=[13, 11, 12], unshuf=[14, 15]
--- arr=[14, 15]
shuffled=[13, 11, 12, 14], unshuf=[]
shuffled=[13, 11, 12, 14], unshuf=[15]
--- arr=[15]
shuffled=[13, 11, 12, 14, 15], unshuf=[]
[13, 11, 12, 14, 15]
说明:arr
的内容在洗牌和非洗牌之间随机分配。然后unshuf替换arr,如果在unshuf中有某些东西,while循环随机重新分配一点点shuffle,一点点unshuf。等等,直到unshuf为空。因此,随机抽取的元素会逐渐填充从最初赋予方法arr
的参数shuffle
中随机获取的元素,但arr
也会通过仅保留arr = unshuf
中的unshuf元素来逐渐清空。当然,在被移入arr之后,必须将unshuf重置为空数组以接收新分发,而洗牌必须继续增长。希望很清楚。