#arrays #ruby
#массивы #ruby
Вопрос:
У меня есть массив,
array = [
0.43,
0.64, # => peak
0.2,
-0.05,
-0.15, # => trough
0.2, # => peak
-0.1,
-0.5, # => trough
-0.3
]
который имеет два пика и два впадины в данных. Эти пики и впадины не обязательно являются минимальными и максимальными значениями массива. Как мне определить их с помощью программы?
Идеальным результатом было бы:
peak_indexes = [1, 5]
trough_indexes = [4, 7]
Комментарии:
1. Что делает -0.5 отрицательным пиком?
2. @ElChapo поскольку данные перед ним ближе к 0, чем -0,5, это
3. Итак, в этом случае каждый конечный элемент был бы пиком / впадиной?. Пик, если элемент перед ним ближе к 0, впадина, если элемент перед ним дальше от 0.
4. Это разумное замечание. Я бы сказал, исключите последний элемент, тогда, как я полагаю, невозможно определить, является ли это истинным пиком или нет
5. Что, если пик / впадина охватывает несколько элементов, например
[0.1, 0.6, 0.6, 0.2]
?
Ответ №1:
each_cons(3)
извлекает смежные три члена, необходимые для проверки среднего члена, исключая триплеты с первым или последним элементом array
в середине.
with_index(1)
учитывается тот факт, что был пропущен триплет с первым элементом array
в середине, таким образом, нумерация индексов начинается с 1.
Вы не определили, что вы подразумеваете под пиками и впадинами. Если вы хотите использовать локальные значения max и min, то сработает следующее.
array.each_cons(3).with_index(1).select{|a, i| a.max == a[1]}.map(amp;:last)
# => [1, 5]
array.each_cons(3).with_index(1).select{|a, i| a.min == a[1]}.map(amp;:last)
# => [4, 7]
Или, если вы имеете в виду то, что Стефан объясняет в комментарии к моему ответу, то сработает следующее:
array
.each_cons(3)
.with_index(1)
.select{|(a1, a2, a3), i| a1 < a2 amp;amp; a2 > a3}
.map(amp;:last)
# => [1, 5]
array
.each_cons(3)
.with_index(1)
.select{|(a1, a2, a3), i| a1 > a2 amp;amp; a2 < a3}
.map(amp;:last)
# => [4, 7]
Комментарии:
1.
a.max == a[1]
/a.min == a[1]
выдает ложноположительные результаты, если имеется несколько идентичных записей, например[0.1, 0.2, 0.2, 0.3]
. Вместо этого вам следует проверить, действительно ли средний элемент больше (не больше или равен) его соседей, например, через|(a, b, c), i| a < b amp;amp; b > c }
2. Хотелось бы, чтобы в Ruby были цепочки сравнений, как в Python.
a < b > c
выглядело бы так круто.3. @Stefan Я думаю, что это усложнило бы синтаксис. Это выражение следовало бы понимать как единую конструкцию, а не как последовательное применение двух методов. Т.е.
a < b> c
не должно было бы означать(a < b) > c
. Некоторые математические выражения не подходят для программирования. Есть еще один пример$a, b in A$
, который, возможно, можно было бы спутать с множественным присвоением, если бы он был импортирован в Ruby.4.
(a < b) > c
в любом случае не имеет особого смысла, потому чтоa < b
возвращает либоtrue
изfalse
, ни один из которых не отвечает на>
. К сожалению,1 < 2 == true
будет оцениваться по-другому, так что это может привести к нарушению существующего кода. Помимо этого, я думаю, что это очень подходит для программирования.
Ответ №2:
Сначала давайте определим, что означает, что что-то является пиком или впадиной.
- Пик — это значение, которое больше, чем значение слева и справа от него.
- Впадина — это значение, которое меньше значения слева и справа от него.
Это позволит нам определить две полезные функции:
def is_peak?(left_value, value, right_value)
return value > left_value amp;amp; value > right_value
end
def is_trough?(left_value, value, right_value)
return value < left_value amp;amp; value < right_value
end
Теперь мы можем просмотреть каждый элемент в массиве и использовать эти функции, чтобы спросить, является ли элемент пиком или впадиной.
array = [0.43, 0.64, 0.2, -0.05, -0.15, 0.2, -0.1, -0.5]
positive_peak_indexes = []
negative_peak_indexes = []
# Loop through the array
array.each_with_index do |elem, i|
# Make sure we don't get an out of bounds exception
next if (i-1 < 0) || (i 1 >= array.length)
# Now we add to our appropriate arrays if it's a peak/trough
positive_peak_indexes << i if is_peak?(array[i-1], elem, array[i 1])
negative_peak_indexes << i if is_trough?(array[i-1], elem, array[i 1])
end
puts "positive_peak_indexes = #{positive_peak_indexes.to_s}"
puts "negative_peak_indexes = #{negative_peak_indexes.to_s}"
Ответ №3:
У меня не было времени протестировать на большем количестве случаев, так что, возможно, есть какая-то ошибка.
В любом случае, основная идея заключается в том, чтобы заархивировать каждый элемент с его индексом (Перечислимый#each_with_index), затем использовать метод Enumerable#chunk_while для нарезки массива при внесении изменений. Наконец, извлеките из блоков крайние значения.
Лучше использовать фрагмент кода, ary
представляющий собой массив данных.
Сначала фрагменты:
chunks = ary.map.with_index{ |x, i| [x,i] }.chunk_while { |x,y| y.first < x.first }
Затем соберите крайние значения:
peaks = chunks.collect { |e| e.first if e.size > 1 }.compact #=> [[0.64, 1], [0.2, 5]]
trough = chunks.collect { |e| e.last if e.size > 1 }.compact #=> [[-0.15, 4], [-0.5, 7]]