【问题标题】:Determine median element of a nested array in Ruby?确定Ruby中嵌套数组的中值元素?
【发布时间】:2018-12-03 01:18:03
【问题描述】:

我需要一个 Ruby 中的中值计算方法,它也适用于嵌套数组,类似于“uniq”和“sort_by”:对于我可以通过块定义的那些,应该考虑哪些嵌套数组值.

class Array
   def median
      . . .
   end
end

puts [[1,3],[2,5],[3,-4]].median{|z,w| z}

=> [2,5]

puts [[1,3],[2,5],[3,-4]].median{|z,w| w}

=> [1,3]

我确信我应该以某种方式处理“产量”,但我不知道该怎么做。

【问题讨论】:

  • 如何计算嵌套数组的中位数?
  • 第 1 步:定义“嵌套数组的中位数”的含义。第 2 步:写下代码以计算您刚刚定义的内容。第 3 步:如果您在第 2 步遇到问题,请在Stack Overflow 上提问。由于没有“嵌套数组的中位数”之类的东西,很遗憾,除非您完成第 1 步并告诉我们那是什么,否则我们无法为您提供帮助。

标签: arrays ruby multidimensional-array yield median


【解决方案1】:

flatten() 是你的朋友。它将嵌套数组(或任何可枚举的)折叠成单个数组。然后,计算中位数就变得很简单了:

class Array
  def median
    array = self.flatten.sort
    if array.size % 2 == 1
      array[array.size / 2]
    else
      mid = array.size / 2
      (array[mid] + array[mid-1]) / 2.0
    end
  end

  def mean
    self.flatten.reduce(:+) / self.flatten.size.to_f  
  end
end

这可以让你做到:

irb> a
=> [[1, 3], [2, 5], [3, -4]]
irb> a.median
=> 2.5
irb> c
=> [[1, 2, 3, 6], [4, 5, [100]]]
irb> c.median
=> 4
irb> c.mean
=> 17.285714285714285

为了提高性能,您可能需要计算 self.flatten 一次,然后对该数组进行两次算术运算。但是,除非您使用大量数据,否则这可能无关紧要,并且希望 Ruby 会为您优化它。但老实说,我不会担心性能。

[编辑,在@kiddorails 指出我混淆了中位数和平均值之后!谢谢,孩子!]

【讨论】:

  • 因为我们还有更重要的工作要做。因为这是一个非常轻量级的操作。因为任何现代语言都应该抓住如此简单的优化并为我们做这件事。因为当性能确实表明这是一个问题时,分析器会向我们展示值得投入精力的地方,而且几乎可以肯定它不会出现在数组副本中。但我很高兴你问! :)
  • 这不是中位数,而是平均值。
  • 确认!你是多么正确,@kiddorails!让我再试一次。
  • 我不太同意这种编程方法。以这种方式编写代码并制作一个充满定时炸弹的系统所需的时间非常少,它可以随时滴答作响。其次,过早优化是万恶之源;但我认为像上面这样的优化是非常基本的,默认情况下应该有。
  • 这种方式在团队工作时很快就会失控 - N+1 查询是 Rails 中的经典示例,我看到开发人员将基本优化留到最后,这通常会拖垮整体系统。
【解决方案2】:

由于中位数需要排序,您可以委托给sort_by 并处理结果:

class Array
  def median(&block)
    block = :itself unless block_given?

    sorted = sort_by(&block)
    if length.odd?
      sorted[sorted.length / 2]
    else
      sorted[sorted.length / 2 - 1, 2]
    end
  end
end

示例运行:

[13, 23, 11, 16, 15, 10, 26].median # => 15
# hyperbole showing the block is used on single elements
count = 0; [13, 23, 11, 16, 15, 10, 26].median { |a| count += 1 } # => 16
# even length data set
# usually you'd average these, but that becomes trickier with nested arrays
[14, 13, 23, 11, 16, 15, 10, 26].median # =>  [14, 15]

# your examples:
[[1,3], [2,5], [3,-4]].median { |z,_| z} # => [2, 5]
[[1,3], [2,5], [3,-4]].median { |_,w| w } # => [1, 3]

# added [6, -6] to your examples:
[[1,3], [2,5], [3,-4], [6, -6]].median { |z,_| z } # => [[2, 5], [3, -4]]
[[1,3], [2,5], [3,-4], [6, -6]].median { |_,w| w } # => [[3, -4], [1, 3]]

您没有指定偶数长度数组应该发生什么。对于数学中位数(如果我没记错的话),您将平均两个最中心的元素,但随之而来的问题是 2 个不同数组的平均值是什么样的。这采用了简单(对我们而言)返回中心元素的方法,调用者必须决定如何处理它们。 (如果它不是嵌套在里面的另一个数组,如果它是一个人的列表并且你想要按姓氏的中位数,例如)

【讨论】:

    【解决方案3】:

    我假设数组的中位数定义如下。对于包含奇数个元素的数组a,中位数为[m],其中ma 的一个元素,其中e <=> ma.size/2 其他元素e 和@ 是非正数987654329@ 对于剩余的 a.size/2 其他元素 e 是非负数。对于具有偶数个元素的数组,中位数是[m, n],其中mna 的元素,其中m <=> n 是非正数,e <=> m 对于@987654338 是非正数@其他元素ee <=> n对于剩余的a.size/2-1其他元素e是非负数。

    class Array
      def median
        min_by(1+self.size/2, &:itself).pop(self.size.odd? ? 1 : 2)
      end
    end
    
    [2, 4, 5, 3, 1].median
      #=> [3]
    [2, 6, 4, 5, 3, 1].median
      #=> [3, 4]
    [3, 6, 4, 5, 3, 1].median
      #=> [3, 4]
    ['hamster', 'dog', 'fish', 'cat'].median
      #=> ["dog", "fish"]
    [[1, 3], [2, 5], [3, -4]].median
      #=> [[2, 5]]
    [[2,6], [3,-4], [1,3], [2,5]].median
      # => [[2, 5], [2, 6]]
    arr = [[[3,1], 1], [[4], 2], [[2,1], 4, 1], [[3,1], 0], [[1,2,3], 5]]
    arr.median
      #=> [[[3, 1], 0]]
    

    在最后一个例子中

    arr.sort
      #=> [[[1, 2, 3], 5], [[2, 1], 4, 1], [[3, 1], 0], [[3, 1], 1], [[4], 2]]
    

    Enumerable#min_by。可选参数是在 Ruby v2.1 中引入的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-24
      • 2020-09-15
      • 2018-05-18
      • 1970-01-01
      • 2016-04-28
      • 2020-09-15
      • 1970-01-01
      • 2020-05-30
      相关资源
      最近更新 更多