首先构建哈希,将数组中的每个值映射到其频率......
arr = [1, 1, 1, 2, 3] freq = arr.inject(Hash.new(0)) { |h,v| h[v] += 1; h } #=> {1=>3, 2=>1, 3=>1}
...然后使用频率表查找频率最高的元素:
arr.max_by { |v| freq[v] } #=> 1
如果您更喜欢在线飞机,也可以执行`arr.each_with_object(Hash.new(0)){| v,h | h [v] + = 1} .max_by(&:last)` (2认同)
Mike Woodhou.. 26
虽然我喜欢grep解决方案的优雅,并提醒(或教导)我在Enumerable中我忘记(或完全忽略)的方法,但它是缓慢,缓慢,缓慢的.我同意100%创建Array#mode
方法是一个好主意,但是 - 这是Ruby,我们不需要一个作用于数组的函数库,我们可以创建一个mixin,它将必要的函数添加到 Array类本身.
但是inject(Hash)替代方法使用了一种我们也不需要的排序:我们只想要具有最高出现率的值.
这两种解决方案都没有解决多个值可能是模式的可能性.也许这不是问题中的问题(无法说明).我想我想知道是否有平局,无论如何,我认为我们可以在性能上有所改善.
require 'benchmark' class Array def mode1 sort_by {|i| grep(i).length }.last end def mode2 freq = inject(Hash.new(0)) { |h,v| h[v] += 1; h } sort_by { |v| freq[v] }.last end def mode3 freq = inject(Hash.new(0)) { |h,v| h[v] += 1; h } max = freq.values.max # we're only interested in the key(s) with the highest frequency freq.select { |k, f| f == max } # extract the keys that have the max frequency end end arr = Array.new(1_000) { |i| rand(100) } # something to test with Benchmark.bm(30) do |r| res = {} (1..3).each do |i| m = "mode#{i}" r.report(m) do 100.times do res[m] = arr.send(m).inspect end end end res.each { |k, v| puts "%10s = %s" % [k, v] } end
这是样本运行的输出.
user system total real mode1 34.375000 0.000000 34.375000 ( 34.393000) mode2 0.359000 0.000000 0.359000 ( 0.359000) mode3 0.219000 0.000000 0.219000 ( 0.219000) mode1 = 41 mode2 = 41 mode3 = [[41, 17], [80, 17], [72, 17]]
"优化"模式3占用了前一个记录持有者的60%的时间.另请注意多个最高频率的条目.
编辑
几个月后,我注意到Nilesh的回答,提出了这个问题:
def mode4 group_by{|i| i}.max{|x,y| x[1].length <=> y[1].length}[0] end
它不适用于1.8.6开箱即用,因为该版本没有Array#group_by.对于Rails开发人员来说,ActiveSupport有它,虽然看起来比上面的mode3慢了2-3%.然而,使用(优秀)backports宝石可以产生10-12%的增益,并且可以提供1.8.7和1.9个附加功能.
以上仅适用于1.8.6 - 主要仅适用于安装在Windows上的情况.自从我安装了它,这是你从IronRuby 1.0(在.NET 4.0上)得到的:
========================== IronRuby ===================================== (iterations bumped to **1000**) user system total real mode1 (I didn't bother :-)) mode2 4.265625 0.046875 4.312500 ( 4.203151) mode3 0.828125 0.000000 0.828125 ( 0.781255) mode4 1.203125 0.000000 1.203125 ( 1.062507)
因此,如果性能超级关键,请在Ruby版本和操作系统上对选项进行基准测试.YMMV.
首先构建哈希,将数组中的每个值映射到其频率......
arr = [1, 1, 1, 2, 3] freq = arr.inject(Hash.new(0)) { |h,v| h[v] += 1; h } #=> {1=>3, 2=>1, 3=>1}
...然后使用频率表查找频率最高的元素:
arr.max_by { |v| freq[v] } #=> 1
虽然我喜欢grep解决方案的优雅,并提醒(或教导)我在Enumerable中我忘记(或完全忽略)的方法,但它是缓慢,缓慢,缓慢的.我同意100%创建Array#mode
方法是一个好主意,但是 - 这是Ruby,我们不需要一个作用于数组的函数库,我们可以创建一个mixin,它将必要的函数添加到 Array类本身.
但是inject(Hash)替代方法使用了一种我们也不需要的排序:我们只想要具有最高出现率的值.
这两种解决方案都没有解决多个值可能是模式的可能性.也许这不是问题中的问题(无法说明).我想我想知道是否有平局,无论如何,我认为我们可以在性能上有所改善.
require 'benchmark' class Array def mode1 sort_by {|i| grep(i).length }.last end def mode2 freq = inject(Hash.new(0)) { |h,v| h[v] += 1; h } sort_by { |v| freq[v] }.last end def mode3 freq = inject(Hash.new(0)) { |h,v| h[v] += 1; h } max = freq.values.max # we're only interested in the key(s) with the highest frequency freq.select { |k, f| f == max } # extract the keys that have the max frequency end end arr = Array.new(1_000) { |i| rand(100) } # something to test with Benchmark.bm(30) do |r| res = {} (1..3).each do |i| m = "mode#{i}" r.report(m) do 100.times do res[m] = arr.send(m).inspect end end end res.each { |k, v| puts "%10s = %s" % [k, v] } end
这是样本运行的输出.
user system total real mode1 34.375000 0.000000 34.375000 ( 34.393000) mode2 0.359000 0.000000 0.359000 ( 0.359000) mode3 0.219000 0.000000 0.219000 ( 0.219000) mode1 = 41 mode2 = 41 mode3 = [[41, 17], [80, 17], [72, 17]]
"优化"模式3占用了前一个记录持有者的60%的时间.另请注意多个最高频率的条目.
编辑
几个月后,我注意到Nilesh的回答,提出了这个问题:
def mode4 group_by{|i| i}.max{|x,y| x[1].length <=> y[1].length}[0] end
它不适用于1.8.6开箱即用,因为该版本没有Array#group_by.对于Rails开发人员来说,ActiveSupport有它,虽然看起来比上面的mode3慢了2-3%.然而,使用(优秀)backports宝石可以产生10-12%的增益,并且可以提供1.8.7和1.9个附加功能.
以上仅适用于1.8.6 - 主要仅适用于安装在Windows上的情况.自从我安装了它,这是你从IronRuby 1.0(在.NET 4.0上)得到的:
========================== IronRuby ===================================== (iterations bumped to **1000**) user system total real mode1 (I didn't bother :-)) mode2 4.265625 0.046875 4.312500 ( 4.203151) mode3 0.828125 0.000000 0.828125 ( 0.781255) mode4 1.203125 0.000000 1.203125 ( 1.062507)
因此,如果性能超级关键,请在Ruby版本和操作系统上对选项进行基准测试.YMMV.
array.max_by { |i| array.count(i) }
迈克:我发现了一种更快的方法.试试这个:
class Array def mode4 group_by{|i| i}.max{|x,y| x[1].length <=> y[1].length}[0] end end
基准输出:
user system total real mode1 24.340000 0.070000 24.410000 ( 24.526991) mode2 0.200000 0.000000 0.200000 ( 0.195348) mode3 0.120000 0.000000 0.120000 ( 0.118200) mode4 0.050000 0.010000 0.060000 ( 0.056315) mode1 = 76 mode2 = 76 mode3 = [[76, 18]] mode4 = 76
arr = [ 1, 3, 44, 3 ] most_frequent_item = arr.uniq.max_by{ |i| arr.count( i ) } puts most_frequent_item #=> 3
甚至无需考虑频率映射.
这是这个问题的重复: Ruby - Array中的唯一元素
这是问题的解决方案:
group_by { |n| n }.values.max_by(&:size).first
该版本似乎比Nilesh C的答案更快.这是我用来对它进行基准测试的代码(OS X 10.6 Core 2 2.4GHz MB).
感谢Mike Woodhouse的(原始)基准测试代码:
class Array def mode1 group_by { |n| n }.values.max_by(&:size).first end def mode2 freq = inject(Hash.new(0)) { |h,v| h[v] += 1; h } max = freq.values.max # we're only interested in the key(s) with the highest frequency freq.select { |k, f| f == max } # extract the keys that have the max frequency end end arr = Array.new(1_0000) { |i| rand(100000) } # something to test with Benchmark.bm(30) do |r| (1..2).each do |i| r.report("mode#{i}") { 100.times do arr.send("mode#{i}").inspect; end }; end end
以下是基准测试的结果:
user system total real mode1 1.830000 0.010000 1.840000 ( 1.876642) mode2 2.280000 0.010000 2.290000 ( 2.382117) mode1 = 70099 mode2 = [[70099, 3], [70102, 3], [51694, 3], [49685, 3], [38410, 3], [90815, 3], [30551, 3], [34720, 3], [58373, 3]]
正如你所看到的,这个版本的速度提高了大约20%,但忽略了关系.我也喜欢简洁,我个人原样使用它,没有猴子修补到处.:)