Как проверить, все ли элементы в моем массиве встречаются не более двух раз?
Я использую Ruby 2.4. Пусть говорят, что у меня есть массив строк (все это просто string-i-fied (это слово?) Целые числа...
["1", "2", "5", "25", "5"]
Как написать функцию, которая сообщает мне, если все элементы в массиве встречаются не более двух раз в массиве? Например, этот массив
["1", "3", "3", "55", "3", "2"]
вернет false
, потому что "3"
происходит три раза, но этот массив
["20", "10", "20", "10"]
вернет true
, потому что ни один из элементов не встречается более двух раз.
Ответы
Ответ 1
С моей точки зрения, это может быть довольно простое решение:
def no_more_than_twice_occur?(array)
array.none? { |el| array.count(el) > 2 }
end
no_more_than_twice_occur?(["1", "3", "3", "55", "3", "2"]) # => false
no_more_than_twice_occur?(["20", "10", "20", "10"]) # => true
Ответ 2
Вы можете определить частоту следующим образом:
frequency = array.reduce(Hash.new(0)) do |counts, value|
counts[value] += 1
counts
end
# => { "1" => 1, "3" => 3, "55" => 1, "2" => 1 }
И вы можете проверить, происходит ли какое-либо из них более двух раз:
frequency.values.max > 2
Если вы хотите красиво обернуть его, вы можете добавить его в Enumerable:
module Enumerable
def frequency
f = Hash.new(0)
each { |v| f[v] += 1 }
f
end
end
И тогда ваше условие так же просто, как:
array.frequency.values.max > 2
Примечание: это происходит как часть Facets.
Ответ 3
Перечислимый # group_by сделает тяжелый подъем для этого:
def no_element_present_more_than_twice?(a)
a.group_by(&:itself).none? do |_key, values|
values.count > 2
end
end
p no_element_present_more_than_twice?(["1", "3", "3", "55", "3", "2"])
# => false
p no_element_present_more_than_twice?(["20", "10", "20", "10"])
Ответ 4
Попробуйте это
count = Hash.new(0)
array.none? { |each| (count[each] += 1) > 2 }
# => true or false
Как это работает?
-
Hash.new(0)
создает хэш со значением по умолчанию 0
-
none?
проверяет блок для всех элементов и возвращает ли элемент не соответствует
-
count[each] += 1
увеличивает счетчик (нет nil
, поскольку значение по умолчанию 0
)
Это оптимальное решение, так как оно ломается, как только будет найден первый оскорбительный элемент. Все остальные решения, размещенные здесь, либо сканируют весь массив, либо имеют еще худшую сложность.
NB, если вы хотите узнать, какие элементы отображаются более двух раз (например, для печати сообщения об ошибке) используйте find
или find_all
вместо none?
.
Ответ 5
Я взял на себя все, чтобы сравнить все ваши варианты для вас:)
Running each test 1024 times. Test will take about 34 seconds.
_akuhn is faster than _vlasiak by 16x ± 1.0
_vlasiak is faster than _wayne by 3.5x ± 0.1
_wayne is faster than _cary by 10.0% ± 1.0%
_cary is faster than _oneneptune by 10.09% ± 1.0%
_oneneptune is similar to _coreyward
_coreyward is faster than _tadman by 10.0% ± 1.0%
_tadman is faster than _sagarpandya82 by 10.0% ± 1.0%
_sagarpandya82 is faster than _glykyo by 80.0% ± 1.0%
Как вы можете видеть, ответ @akuhn работает намного лучше, чем другие алгоритмы, потому что он выходит раньше, как только совпадение найдено.
Примечание.. Я отредактировал ответы для получения того же результата, но не изменил их для оптимизации.
Вот script, который подготовил тесты:
require 'fruity'
arr = Array.new(1000) { |seed|
# seed is used to create the same array on each script run,
# hence the same benchmark results will be produced
Random.new(seed).rand(1..10).to_s
}
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
compare do
_coreyward do
arr.reduce(Hash.new(0)) { |counts, value|
counts[value] += 1
counts
}.max[1] <= 2
end
_wayne do
arr.group_by(&:itself).none? do |_key, values|
values.count > 2
end
end
_sagarpandya82 do
arr.sort_by(&:to_i).each_cons(3).none? { |a,b,c| a == b && b == c }
end
_tadman do
arr.sort.slice_when { |a,b| a != b }.map(&:length).max.to_i <= 2
end
_cary do
arr.difference(arr.uniq*2).empty?
end
_akuhn do
count = Hash.new(0)
arr.none? { |each| (count[each] += 1) > 2 }
end
_oneneptune do
arr.each_with_object(Hash.new(0)) { |element,counts|
counts[element] += 1
}.values.max < 3
end
_glykyo do
arr.uniq.map{ |element| arr.count(element) }.max <= 2
end
_vlasiak do
arr.none? { |el| arr.count(el) > 2 }
end
end
Ответ 6
Здесь другой способ, используя метод Array#difference
:
def twice_at_most?(arr)
arr.difference(arr.uniq*2).empty?
end
где Array#difference
определяется следующим образом:
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
После того, как я нашел много применений для Array#difference
, я предложил, чтобы он был принят в качестве основного метода. В документе по этой ссылке объясняется, как работает этот метод, и приводятся примеры его использования.
Попробуйте.
twice_at_most? [1, 4, 2, 4, 1, 3, 4]
#=> false
Здесь
arr.uniq*2
#=> [1, 4, 2, 3, 1, 4, 2, 3]
arr.difference(arr.uniq*2)
#=> [4]
Другой пример:
twice_at_most? [1, 4, 2, 4, 1, 3, 5]
#=> true
Ответ 7
Здесь все в одном методе для вас.
def lessThanThree(arr)
arr.each_with_object(Hash.new(0)) { |element,counts| counts[element] += 1 }.values.max < 3
end
В основном, принимает массив, выполняет итерацию, создавая хэш и подсчитывая каждое вхождение, тогда метод значений просто создает массив всех значений (значений), а затем max находит наибольшее значение. Мы проверяем, если это меньше трех, если это так, верните true, иначе верните false. Вы можете заменить true или false блоком кода.
Ответ 8
Чтобы избежать много временных накладных расходов, просто sort
массива, а затем разделите его на куски похожих элементов. Затем вы можете найти самый длинный фрагмент:
def max_count(arr)
arr.sort.slice_when { |a,b| a != b }.map(&:length).max.to_i
end
max_count(%w[ 1 3 3 55 3 2 ])
# => 3
max_count(%w[ 1 3 55 3 2 ])
# => 2
max_count([ ])
# => 0
Ответ 9
Просто для удовольствия здесь одним способом, используя each_cons
и используя none?
, как использовал Уэйн Конрад в своем ответе.
arr.sort_by(&:to_i).each_cons(3).none? { |a,b,c| a == b && b == c }
Ответ 10
Для каждого уникального элемента массива подсчитывайте, сколько раз этот элемент появляется в массиве. Из этих значений проверьте, является ли max равным <= 2.
def max_occurence_at_most_2?(array)
array.uniq.map{ |element| array.count(element) }.max <= 2
end
Не оптимизирован для скорости.