Хотя я обожаю решение grep за его элегантность и за напоминание (или обучение) о методе в Enumerable, который я забыл (или полностью упустил из виду), он медленный, медленный, медленный. Я согласен на 100%, что создание метода Array#mode
- хорошая идея, однако, это Ruby, нам не нужна библиотека функций, которые работают с массивами, мы можем создать миксин, который добавляет необходимые функции в сам класс 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% времени предыдущего рекордсмена. Обратите также внимание на несколько записей с самой высокой частотой.
EDIT
Через несколько месяцев я заметил ответ Нилеша , который предложил это:
def mode4
group_by{|i| i}.max{|x,y| x[1].length <=> y[1].length}[0]
end
Он не работает с 1.8.6 из коробки, потому что эта версия не имеет Array # group_by. ActiveSupport имеет его для разработчиков Rails, хотя кажется, что он на 2-3% медленнее, чем mode3 выше. Однако использование (превосходного) 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 .