I wrote a function to implement a generalised version of binary search that can find the minimum or maximum index in a given sorted list that satisfies a provided predicate.
My Code
def xbinsearch(pred, lst, type = "min", default = None):
"""
* Finds the minimum or maximum index of a sorted list such that the value at that index satisfies a given predicate.
* Params:
* `pred`: Predicate function.
* Params:
* `idx`: The index of the value to be tested.
* `lst`: The list to search for said value in.
* Return: An ordered pair of the form `(x, y)`.
* `x`: Boolean indicates whether or not `lst[idx]` satisfies the predicate.
* `y`: `-1`, `1` or `None`.
* `-1` indicates that if `lst[idx]` does not satisfy the predicate, then all values below `lst[idx]` also do not satisfy the predicate.
* `1` indicates that if `lst[idx]` does satisfy the predicate, then all values above `lst[idx]` also do not satisfy the predicate.
* `None` is given with `True` for the first index of the ordered pair.
* It is used to determine which "half" to discard in the binary search.
"""
low, hi, best = 0, len(lst)-1, default
while low <= hi:
mid = (low+hi)//2
p = pred(mid, lst)
if p[0]: #The current element satisfies the given predicate.
if type == "min":
if best == default or lst[mid] < lst[best]: best = mid
hi = mid-1
elif type == "max":
if best == default or lst[mid] > lst[best]: best = mid
low = mid+1
elif p[1] == 1: #For all `x` > `lst[mid]` not `P(x)`.
hi = mid - 1
elif p[1] == -1: #For all `x` < `lst[mid]` not `P(x)`.
low = mid + 1
return best
Sample Use Case
Problem Element equals its index
Given a sorted array of distinct integers, write a function
index_equals_value
that returns the lowest index for which array[index] == index.
-1` if there is no such index.
Return
Your algorithm should be very performant.[input] array of integers ( with
0
-based nonnegative indexing )
[output] integerExamples:
input: [-8,0,2,5] output: 2 # since array[2] == 2 input: [-1,0,3,6] output: -1 # since no index in array satisfies array[index] == index
Random Tests Constraints:
Array length: 200 000
Amount of tests: 1 000
Time limit: 1.5 s
My Code
def identity(idx, lst):
tpl, val = [None, None], lst[idx]
tpl[0] = val == idx
if val < idx: tpl[1] = -1
elif val > idx: tpl[1] = 1
return tuple(tpl)
def xbinsearch(pred, lst, type = "min", default = None):
low, hi, best = 0, len(lst)-1, default
while low <= hi:
mid = (low+hi)//2
p = pred(mid, lst)
if p[0]: #The current element satisfies the given predicate.
if type == "min":
if best == default or lst[mid] < lst[best]: best = mid
hi = mid-1
elif type == "max":
if best == default or lst[mid] > lst[best]: best = mid
low = mid+1
elif p[1] == 1: #For all `x` > `lst[mid]` not `P(x)`.
hi = mid - 1
elif p[1] == -1: #For all `x` < `lst[mid]` not `P(x)`.
low = mid + 1
return best
def index_equals_value(lst):
return xbinsearch(identity, lst, default = -1)
Finds the minimum or maximum index
(not value as in the introductory sentence).) \$\endgroup\$