-1

The implementation from Geeksforgeeks https://www.geeksforgeeks.org/find-indices-of-all-local-maxima-and-local-minima-in-an-array/ is wrong. If you have consecutive-duplicates, things will gall apart!

Example 1: values = [ 1, 2, 3, 7, 11, 15, 13, 12, 11, 6, 5, 7, 11, 8]
    The default implementation correctly identify "15" as a peak.

Example 2: values = [ 1, 2, 3, 7, 11, 15, 15, 13, 12, 11, 6, 5, 7, 11, 8]
    The default implementation will mark "11" as local maxima because there are two consecutive 15's.

Below is code from geekforgeeks, with problem highlighted - when making greater/lesser comparison with left and right , if your neighbour's values == , then look further left or right:

def findLocalMaximaMinima(n, arr):

    # Empty lists to store points of
    # local maxima and minima
    mx = []
    mn = []

    # Checking whether the first point is
    # local maxima or minima or neither
    if(arr[0] > arr[1]):
        mx.append(0)
    elif(arr[0] < arr[1]):
        mn.append(0)

    # Iterating over all points to check
    # local maxima and local minima
    for i in range(1, n-1):

        # Condition for local minima
        if(arr[i-1] > arr[i] < arr[i + 1]):     <-- Problem is here
            mn.append(i)

        # Condition for local maxima
        elif(arr[i-1] < arr[i] > arr[i + 1]):    <-- Problem is here
            mx.append(i)

    # Checking whether the last point is
    # local maxima or minima or neither
    if(arr[-1] > arr[-2]):
        mx.append(n-1)
    elif(arr[-1] < arr[-2]):
        mn.append(n-1)

        # Print all the local maxima and
        # local minima indexes stored
    if(len(mx) > 0):
        print("Points of Local maxima"\
            " are : ", end ='')
        print(*mx)
    else:
        print("There are no points of"\
            " Local maxima.")

    if(len(mn) > 0):
        print("Points of Local minima"\
            " are : ", end ='')
        print(*mn)
    else:
        print("There are no points"\
            " of Local minima.")
Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70
user3761555
  • 851
  • 10
  • 21
  • The function doesn't pick out `11` at index `4`. In fact, it completely misses the local maximum at indices `5` and `6`. The `11` that is marked as the local maximum is the `11` at the second-last location in `values` – Pranav Hosangadi Jan 05 '23 at 04:05
  • Does this answer your question? [Large list, find all minima of list (python)](https://stackoverflow.com/questions/29608044/large-list-find-all-minima-of-list-python) I know your question addresses geeksforgeeks's code _specifically_, but the linked duplicate illustrates the basic method and the rest of the code in geeksforgeeks's solution is irrelevant fluff anyway – Pranav Hosangadi Jan 05 '23 at 04:15
  • Thanks might have done the job, but I stumbled across geek's version and used it, only to find it's buggy. Thus attempt to fix (And share the fix) – user3761555 Jan 05 '23 at 04:17
  • Your post has only reinforced my opinion of that website though -- IMO it's full of bugs, wrong info, and plagiarism. – Pranav Hosangadi Jan 05 '23 at 04:18
  • lol yea, those are interviews questions and coding challenges! – user3761555 Jan 05 '23 at 04:20
  • @PranavHosangadi i read https://stackoverflow.com/questions/29608044/large-list-find-all-minima-of-list-python the solution is concise and beautiful. However the result is values of the minima, not index. Imagine you're working with stock price or other time series data. You need the index or location of where the minima is. (Look up can do but that's not realiable as it can come back with >1 index or dates) – user3761555 Jan 05 '23 at 04:53
  • Returning the index is trivially easy -- just select `i` instead of `y` in the list comprehension from the selected answer. – Pranav Hosangadi Jan 05 '23 at 06:11
  • Oh right, i think you are correct actually. I need have another look to see if "Large list..." the solution there if it can handle consecutive duplicates as well. – user3761555 Jan 05 '23 at 07:15
  • I think that code have same problem in comparison (< or >, try fixing with <= or >= wont do it): Example case, I think it will also flag the second "6" as a maxima: [ 8, 7, 6, 6, 4, 3, 2, 1, 3, 5, 7, 9] – user3761555 Jan 05 '23 at 07:18

2 Answers2

0

Here's the fix (For some reason I can't upload the code there, so upload here instead):

from datetime import datetime
from typing import List

def is_greater(index1, index2, values, increment_when_eq : bool):
    if values[index1]>values[index2]:
        return True
    elif values[index1]<values[index2]:
        return False
    else:
        # Case when: values[index1] == values[index2]
        index2_shifted = index2+1 if increment_when_eq else index2-1
        if index2_shifted < len(values):
            return is_greater(index1=index1, index2 = index2_shifted, values = values, increment_when_eq = increment_when_eq)
        else:
            return False
 
def find_local_max_min(values : List):
    mx = []
    mn = []

    n = len(values)
    if n==0:
        return None

    if(values[0] > values[1]):
        mx.append(0)
    elif(values[0] < values[1]):
        mn.append(0)
    
    for i in range(1, n-1):
        if (not is_greater(i, i-1, values, False) and not is_greater(i, i+1, values, True)):
            mn.append(i)
        elif(is_greater(i, i-1, values, False) and is_greater(i, i+1, values, True)):
            mx.append(i)

    if(values[-1] > values[-2]):
        mx.append(n-1)
    elif(values[-1] < values[-2]):
        mn.append(n-1)

    return {
        'local_max' : mx,
        'local_min' : mn
    }

if __name__ == '__main__':
    values = [ 1, 2, 3, 7, 11, 15, 15, 13, 12, 11, 6, 5, 7, 11 , 8, 19, 19, 18, 18, 18, 15, 7, 3]
    start = datetime.now()
    local_min_max = find_local_max_min(values)
    local_max = local_min_max['local_max']
    local_min = local_min_max['local_min']
user3761555
  • 851
  • 10
  • 21
  • You don't even need to do all this. Simply change the conditions to `arr[i-1] >= arr[i] < arr[i + 1]` for the local minimum and `arr[i-1] <= arr[i] > arr[i + 1]` for the local maximum. – Pranav Hosangadi Jan 05 '23 at 04:14
  • Why do you need `datetime`? – Pranav Hosangadi Jan 05 '23 at 04:22
  • sorry you dont, cut and paste issue. no need datetime – user3761555 Jan 05 '23 at 04:33
  • hi @PranavHosangadi, on "You don't even need to do all this. Simply change the conditions to arr[i-1] >= arr[i] < arr[i + 1] for the local minimum and arr[i-1] <= arr[i] > arr[i + 1] for the local maximum" ---> I think it'd fail on this case and identify the second "6" as a maxima: [ 8, 7, 6, 6, 4, 3, 2, 1, 3, 5, 7, 9] – user3761555 Jan 05 '23 at 07:03
  • My bad, I misplaced the `=` it should be on the `>` in the max condition. See https://tio.run/##bVLhboIwEP7PU1z8A3V1sTqdM7onmE/A@qMZZWsCJ6mYsBifnbVXVFggQK5fv@@79u6q3/rniMu2zXQOucHs4/ilioNqTKkOBt0/QQ7KWraNwD1lA3tIZYgxxLQweeJY6VzCu6enQnaKoHpWVaUxS@aMQF08@LsRPvb4hOZHCwYMglX4rRPBAWeip@jszEy4A@zJ0dysDTzBwH6YwrD7xv1Y5LO72dwMR32ank@/EnQS0s0W47XwNxhW45H1nwYHGsKtrs8WnR1321HkRCffjhRcbRYclhxeOQi3EKvuc5BYBGzNYXUnbCSP0g0t1/S@kNxTKQjMNykj3wWXyPfB59tG3UzwMAvj81No9JdjYYxIUVmDdTJN88lFXTlc4lI18TShBpcNC5DBGKbQgciukzAEzsYjGs@ltqrW5C05nHS1jz8x5m4r85GLWdv@AQ – Pranav Hosangadi Jan 05 '23 at 07:31
0

I agree to Pranav that simply fix equality when making comparison will rectify the problem. The fix will be much more concise. Thanks Pranav

def findLocalMaximaMinima(n, arr):
    mx = []
    mn = []

    if(arr[0] > arr[1]):
        mx.append(0)
    elif(arr[0] < arr[1]):
        mn.append(0)

    for i in range(1, n-1):
        if(arr[i-1] >= arr[i] < arr[i + 1]):
            mn.append(i)
        elif(arr[i-1] < arr[i] >= arr[i + 1]):
            mx.append(i)

    if(arr[-1] > arr[-2]):
        mx.append(n-1)
    elif(arr[-1] < arr[-2]):
        mn.append(n-1)

    return mx, mn

arrs = [[ 1, 2, 3, 7, 11, 15, 15, 13, 12, 11, 6, 5, 7, 11, 8],
[4, 5, 6, 6, 6, 4, 3, 2, 1, 3, 5, 7, 9]]
for arr in arrs:

    mx, mn = findLocalMaximaMinima(len(arr), arr)
    print(*[f"{a}, {'max'*(i in mx)}, {'min' * (i in mn)}" for i, a in enumerate(arr)], sep='\n', end='\n\n')

https://tio.run/##bVLhboIwEP7PU1z8A3V1sTqdM7onmE/A@qMZZWsCJ6mYsBifnbVXVFggQK5fv@@79u6q3/rniMu2zXQOucHs4/ilioNqTKkOBt0/QQ7KWraNwD1lA3tIZYgxxLQweeJY6VzCu6enQnaKoHpWVaUxS@aMQF08@LsRPvb4hOZHCwYMglX4rRPBAWeip@jszEy4A@zJ0dysDTzBwH6YwrD7xv1Y5LO72dwMR32ank@/EnQS0s0W47XwNxhW45H1nwYHGsKtrs8WnR1321HkRCffjhRcbRYclhxeOQi3EKvuc5BYBGzNYXUnbCSP0g0t1/S@kNxTKQjMNykj3wWXyPfB59tG3UzwMAvj81No9JdjYYxIUVmDdTJN88lFXTlc4lI18TShBpcNC5DBGKbQgciukzAEzsYjGs@ltqrW5C05nHS1jz8x5m4r85GLWdv@AQ

user3761555
  • 851
  • 10
  • 21