【问题标题】:Find element(s) closest to average of array查找最接近数组平均值的元素
【发布时间】:2014-03-31 22:23:14
【问题描述】:

执行以下操作的“红宝石”方式是什么?我仍在考虑更多的命令式编程,并没有真正适应 ruby​​ 的思考。我想要做的是找到大小最接近数组平均值的元素,例如,考虑以下数组

[1,2,3] 

平均值为 2.0。我要编写的方法返回最接近其上下平均值的元素,在本例中为 1 和 3。

另一个例子会更好地说明这一点:

[10,20,50,33,22] avg is 27.0 method would return 22 and 33.

【问题讨论】:

  • 什么? [1,2,3] 的平均值不是6,而是2总和6。然后1362 最接近?请使用准确的术语和详细信息进行编辑。如果数组恰好包含平均值怎么办?三个值是平均值?
  • 另外,今后请注意,您应该在问题中包含您迄今为止尝试解决问题的代码。本网站旨在帮助人们得到正确的答案,而不是为他们。尽管这就是我刚刚所做的。 :)
  • 您希望为这两个数组返回哪些值,这两个数组的平均值均为 3:[1,2,3,4,5][1,2,3,3,4,5]
  • +1 表示想以ruby 的方式进行:)

标签: ruby arrays algorithm average


【解决方案1】:

这不是最有效的,但它(以我的拙见)相当 Ruby 风格。

class Array
  # Return the single element in the array closest to the average value
  def closest_to_average
    avg = inject(0.0,:+) / length
    min_by{ |v| (v-avg).abs }
  end
end

[1,2,3].closest_to_average
#=> 2 

[10,20,50,33,22].closest_to_average
#=> 22 

如果你真的想要 n 个最接近的项目,那么:

class Array
  # Return a number of elements in the array closest to the average value
  def closest_to_average(results=1)
    avg = inject(0.0,:+) / length
    sort_by{ |v| (v-avg).abs }[0,results]
  end
end

[10,20,50,33,22].closest_to_average     #=> [22] 
[10,20,50,33,22].closest_to_average(2)  #=> [22, 33] 
[10,20,50,33,22].closest_to_average(3)  #=> [22, 33, 20] 

这是如何工作的

avg = inject(0.0,:+) / length
是:
avg = self.inject(0.0){ |sum,n| sum+n } / self.length
的简写 我从0.0 的值开始,而不是0,以确保总和是浮点数,因此除以长度不会得到整数舍入值。

sort_by{ |v| (v-avg).abs }
根据数字与平均值之差(从低到高)对数组进行排序,然后:
[0,results]
从该数组中选择第一个 results 个条目。

【讨论】:

  • inject(:+) 只是获取数组中所有值的总和的更短的方法
  • @SparkyRobinson 但如果数组为空(除法时会出错),将返回nil 而不是0.0。这种极端情况是我使用这种形式的唯一原因。不过,请 +1 指出这一点。
  • 请注意,@Sparky 提醒您不必将 :+ 转换为 inject 的 proc。我认为在讨论初始值时,可能已经错过了。
  • @CarySwoveland FWIW 没有错过,但我没有意识到它适用于两个参数的调用,即inject(0.0,:+);感谢您检查和发现真相的动力。让我感到困扰的是,这一核心方法具有这种便利性,而许多其他方法却没有。但是,它的可读性和方便性稍高一些,因此我编辑了响应以使用它(即使它只是 Ruby 语法中记忆魔法的另一种一次性案例)。
  • 省略inject 的初始值可能会更好一些,因此如果没有浮点数,则可以用整数算术求和,然后除以length.to_f。能够将符号传递给inject 是一个经常被忽视的简洁功能。我喜欢的一个:[{a: 1},{b: 2},{c: 3}].inject(:merge) => {:a=>1, :b=>2, :c=>3}.
【解决方案2】:

我假设需要的是数组中小于平均值的最大元素和大于平均值的数组最小值。当且仅当数组具有至少两个元素并且它们不完全相同时,这样的值才存在。假设条件适用,我们只需将其从单词转换为符号:

avg = a.reduce(:+)/a.size.to_f
[ a.select { |e| e < avg }.max, a.select { |e| e > avg }.min ]

另一种方式,效率稍低:

avg = a.reduce(:+)/a.size.to_f
b = (a + [avg]).uniq.sort
i = b.index(avg)
[ b[i-1], b[i+1] ]

【讨论】:

  • 也许b=a+[avg] 这样就不会污染原始数组了。另请注意,原件中的两个元素就足够了。 +1 用于猜测需求和实施。
猜你喜欢
  • 2021-06-18
  • 1970-01-01
  • 2017-12-06
  • 1970-01-01
  • 2021-04-23
  • 2014-12-24
  • 2014-06-11
  • 2013-12-29
  • 1970-01-01
相关资源
最近更新 更多