4

I have a list of integers and I am trying to achieve O(log n) by using a recursive algorithm to identify a dip in a list of integers. A dip is any number which is immediately followed by and follows a number equal than or greater than itself, and follows, such that x >= dip <= y. First and last elements can be considered dips as long as the number next to them is greater than or equal to these elements.

I have been able to achieve O(n) by simply iterating through the list, however I'm trying to use an approach similar to the binary search algorithm to achieve a faster result. I only need to find a single dip in the list.

My problem is when I separate the list into left and right of the midpoint I eventually reach smaller lists with one/two elements but they are not necessarily pits as they do not take into consideration the numbers just outside of their slices.

Can anyone help me?

def find_dip(lst):
     if len(lst) == 1:
          return 0
     elif len(lst) == 2:
          if lst[0] <= lst[1]:
               return 0
          else:
               return 1
     else:
          ans = False
          mid = len(lst) // 2
          print("test")
          if lst[mid-1] >= lst[mid] <= lst[mid+1]:
               ans = mid
          elif ans == False and len(lst[mid:]) > 2:
               if lst[mid] >= lst[mid+1] <= lst[mid+2]:
                    ans = mid+1
          elif ans == False and len(lst[:mid]) > 2:
               if lst[mid-2] >= lst[mid-1] <= lst[mid]:
                    ans = mid-1      
          elif ans == False:
               ans = find_dip(lst[:mid])
          else:
               ans = find_dip(lst[mid+1:])
          return ans
smci
  • 32,567
  • 20
  • 113
  • 146
  • 1
    Your question will have a much better reception here if you add the code blocks you've written, even though they don't meet your performance requirements. – Ryne Everett Mar 16 '15 at 01:45
  • I'm not sure you can achieve O(log n) time with arbitrary input. Binary search only works logarithmically because you know the input is sorted, for instance. In this case, linear is probably your lower bound. – user3758171 Mar 16 '15 at 02:08
  • 1
    It is definitely impossible to get O(log n) in the worst case, since all the elements might have to be checked at some point (because the list has no property, so no information can be obtained about its elements without reading them first): the worst case has to be O(n). – Eric O. Lebigot Mar 16 '15 at 02:15

2 Answers2

6

The answer is yes. Based on the following link finding a peak (the exact opposite of dip) needs O(log n) time. But the difference is superficial and the algorithm described can be adapted in an exactly similar way. This is a python version:

def find_dip(lst):
    start, end = 0, len(lst) - 1
    while start < end:
        if end - start == 1: return start if lst[start] <= lst[end] else end
        mid = (start + end) / 2
        a, b, c = lst[mid - 1: mid + 2]
        if a >= b <= c: return mid
        if a <= b: end = mid - 1
        else: start = mid + 1
    return end
JuniorCompressor
  • 19,631
  • 4
  • 30
  • 57
  • Nice. The key "trick" is the definition of "dip" at the two endpoints. It means that either (a) at least one of the endpoints must be a dip, which you can check in O(1) time, or (b) both endpoints have a lower neighbour, which means that there must be a dip *somewhere* between them -- because if there wasn't, it could only mean that the list was infinitely long. In the second case, if you pick a new point somewhere in between to try, you find that, similarly, it's either (a) a dip; or (b) there must be at least one lower neighbour, in which case you can start over on that "side". – j_random_hacker Mar 16 '15 at 13:37
0

Since you're only interested in finding any single dip, a binary-search approach will work, with some tweaking.

i) Instead of inspecting a middle element on each iteration, inspect the middle two (adjacent values).

ii) Compare these two 'midpoints' and select the smaller one, along with the half of the set that's on the same side of the midpoint as this selected value.

iii) Repeat this, as you would with a regular binary search.

iv) You'll have found your dip when the set you're inspecting is down to just one value.

EG: 1,2,3,4,6,2,8,3,6,9,0,2

i) middle two values are 2 and 8 as shown in bold above.

ii) 2 < 8, so select the 2, along with the LEFT half of your set:

1,2,3,4,6,2

iii) repeat.

Have fun coding it up! ^.^

NOTE: There will be a slight niggle if you run into midpoint pairs that are equal. You will need to then compare them to a third adjacent term. If that term is also equal to the other two, then take the middle one of the three as the dip. Else, select the half of the set with the smaller value and return to step (i) to continue the iteration.

jarekwg
  • 375
  • 3
  • 8