使用每种方法在Ruby中进行排序

时间:2012-12-30 22:22:32

标签: ruby sorting each

我有以下代码:

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条件?

3 个答案:

答案 0 :(得分:3)

代码的工作原理如下:

  • 创建一个空数组,其中将存储最终结果。

  • 获取0和初始数组之间的随机数。

  • 将indexcounter初始化为零。
  • 创建一个无组合的空数组。
  • 循环遍历初始数组
    • 将每个项目推送到未洗脑的阵列,
    • 除了indexcounter ==随机数之外,还有结果数组
    • 同时保留一个索引计数器。
  • 将非抽动数组重命名为初始数组。
  • 从步骤2开始重复,直到初始数组为空。

毋庸置疑,这很笨拙。此外,它以非常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重置为空数组以接收新分发,而洗牌必须继续增长。希望很清楚。