If you're looking for a functional example, you could probably do (in Ruby)
def derivative_signs(list)
(0..list.length-2).map { |i| (list[i+1] - list[i]) <=> 0 }
## <=> is the "rocketship" operator which is basically: be -1 if number is negative
## be 0 if number is zero
## be 1 if number is positive
## Alternatively, one could use x / x.abs, with an exception if x == 0
end
def derivative(list)
(0..list.length-2).map { |i| list[i+1] - list[i] }
end
Coming from calculus, minimums/maximums are when the first derivative changes signs. So one could look through derivative_signs(arr)
and find all of the sign changes.
first_derivative_signs = derivative_signs(arr)
# => [1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1]
Alternatively, you could also do
second_derivative = derivative(derivative_signs(arr))
On the list you provided, you'll get:
[0, 0, 0, -2, 0, 2, 0, 0, -2, 0, 0, 2]
It's clear to see that values with second derivative -2
are maximums, and values with second derivative 2
are minimums. The index of the second derivative is the index of the original list + 1. So the second_derivative[4]
that is a -2
corresponds to arr[5]
(7), which is a maximum.
Why do we do a "normal" derivative the second time, instead of a derivative_sign?
This is because when a value repeats twice in a row, you'll get unwanted behavior.
For example, consider
[1, 3, 6, 6, 7, 5, 4, 6, 9, 10, 8, 7, 5, 10]
# first_derivative_signs => [1, 1, 0, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1]
# second_derivative_signs => [0, -1, 1, -1, 0, 1, 0, 0, -1, 0, 0, 1]
# second_derivative => [0, -1, 1, -2, 0, 2, 0, 0, -2, 0, 0, 2]
Note that second_derivative_signs
throws us some "false" minimums/maximums, while second_derivative
, when we check for only -2
and 2
, is good.