为什么[20,...,13,14] .min(2)=> [13,20]?

时间:2015-08-20 15:00:38

标签: ruby min

[20, 32, 32, 21, 30, 25, 29, 13, 14].min(2)
# => [13, 20]

为什么不是[13, 14]?以及我得到了什么,两个最小的元素(线性时间)?

The doc的句子“如果给出n参数,最少n个元素作为数组返回”对我来说不是很清楚,但我认为它是{{ 1}}应该给我最小的两个元素。我找不到太多关于它的内容,但是this thread,它可能是起源,似乎与我同意,并说它应该返回与min(2)相同,但它不会:

sort.first(n)

对不起,如果对“大”的例子感到愚蠢和抱歉,但已经减少了 - 删除一个号码(13或14除外)确实给了我[20, 32, 32, 21, 30, 25, 29, 13, 14].sort.first(2) # => [13, 14]

2 个答案:

答案 0 :(得分:8)

我刚刚发布了Ruby Issue Tracking System中的错误说明:

  

我想我发现了这个问题。举个例子:

[20, 32, 32, 21, 30, 25, 29, 13, 14].min(2)
     

这将调用函数" nmin_run"在文件" enum.c"中,哪个   套装" bufmax"到我们想要的最小数量(n)的4倍(对于   例如,bufmax是8),然后在行中   1327会致电   功能" nmin_i"对于原始数组的每个元素。

     

在函数" nmin_i"中,当缓冲区已满(" data-> curlen ==   data-> bufmax"),函数" nmin_filter"叫做。在这个例子中,   当curlen为8时会发生这种情况,因此缓冲区为[20,32,32,21,   30,25,29,13]。 " nmin_filter"会做快速的直到n   到目前为止,最小的元素位于缓冲区的最左边部分,并且   将丢弃剩余的元素,这使我们[20,13]   在缓冲区。

     

现在开始出现问题。在" nmin_filter"结束时极限   (显然是为了存储最大的价值   buffer)设置为缓冲区中的最后一个值(在示例中为13),   这不是真的。然后根据该值" nmin_i"将丢弃   所有剩余的元素都大于该值(在示例中,丢弃   14)。然后对缓冲区进行排序并返回:

[13, 20]
     

因此,解决方案是删除所有与限制相关的部分,或者采取   最后一个支点作为限制。

答案 1 :(得分:2)

顺便说一下,回答你的问题......

  

怎么做我得到了我想要的东西,两个最小的元素(线性时间)?

如果此方法不存在或同时它被破坏,您可以使用Quickselect在线性时间中选择两个最小元素,这基本上是Ruby在min中所做的工作。

以下是我对维基百科的直接翻译:

class Array
  def mymin(n)
    return self.sort if self.size <= n

    a = self.dup
    left = 0
    right = a.size - 1
    loop do
      pivot_index = left + (right - left) / 2;
      pivot_value = a[pivot_index]
      a[pivot_index], a[right] = a[right], a[pivot_index]
      store_index = left
      left.upto(right - 1).each do |i|
         if a[i] < pivot_value
           a[store_index], a[i] = a[i], a[store_index]
           store_index += 1
         end
      end
      a[right], a[store_index] = a[store_index], a[right]
      if n - 1 == store_index
        break
      elsif n - 1 < store_index
        right = store_index - 1
      else
        left = store_index + 1
      end
    end
    a.take(n).sort
  end
end

然后我们尝试你的例子:

[20, 32, 32, 21, 30, 25, 29, 13, 14].mymin(2)
# => [13, 14]

耶!我们刚刚修正了min。但请注意,此实现的空间复杂度与原始数组的大小呈线性关系,而Ruby实现与值n呈线性关系。此外,如果您的原始数组有太多重复项,这将有一个糟糕的性能,你应该寻找
3路分区。

如果你只想要n = 2的min并且真的担心性能,那么可以为O(L)保证{(1}}的情况制作优化版本(假设为L是数组的长度。)

class Array
  def min2
    m1 = nil
    m2 = nil
    self.each do |x|
      if m1.nil? || x < m1
        m2 = m1
        m1 = x
      elsif m2.nil? || x < m2
        m2 = x
      end
    end
    [m1, m2].compact
  end
end

以类似的方式使用它:

[20, 32, 32, 21, 30, 25, 29, 13, 14].min2
# => [13, 14]