Skip to content

upper_bound and lower_bound functions added #351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Mar 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion pydatastructs/linear_data_structures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
cocktail_shaker_sort,
quick_sort,
longest_common_subsequence,
is_ordered
is_ordered,
upper_bound,
lower_bound
)
__all__.extend(algorithms.__all__)
137 changes: 136 additions & 1 deletion pydatastructs/linear_data_structures/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
'cocktail_shaker_sort',
'quick_sort',
'longest_common_subsequence',
'upper_bound',
'lower_bound',
'is_ordered'
]

Expand Down Expand Up @@ -796,7 +798,7 @@ def is_ordered(array, **kwargs):
Parameters
==========

array: Array
array: OneDimensionalArray
The array which is to be checked for having
specified ordering among its elements.
start: int
Expand Down Expand Up @@ -843,3 +845,136 @@ def is_ordered(array, **kwargs):
if comp(array[i], array[i - 1]):
return False
return True

def upper_bound(array, value, **kwargs):
"""
Finds the index of the first occurence of an element greater than the given
value according to specified order, in the given OneDimensionalArray using a variation of binary search method.

Parameters
==========

array: OneDimensionalArray
The array in which the upper bound has to be found.
start: int
The staring index of the portion of the array in which the upper bound
of a given value has to be looked for.
Optional, by default 0
end: int, optional
The ending index of the portion of the array in which the upper bound
of a given value has to be looked for.
Optional, by default the index
of the last position filled.
comp: lambda/function
The comparator which is to be used
for specifying the desired ordering.
Optional, by default, less than or
equal to is used for comparing two
values.

Returns
=======

index: int
Index of the upper bound of the given value in the given OneDimensionalArray.

Examples
========

>>> from pydatastructs import upper_bound, OneDimensionalArray as ODA
>>> arr1 = ODA(int, [4, 5, 5, 6, 7])
>>> ub = upper_bound(arr1, 5, start=0, end=4)
>>> ub
3
>>> arr2 = ODA(int, [7, 6, 5, 5, 4])
>>> ub = upper_bound(arr2, 5, comp=lambda x, y: x > y)
>>> ub
4

Note
====

DynamicOneDimensionalArray objects may not work as expected.
"""
start = kwargs.get('start', 0)
end = kwargs.get('end', len(array))
comp = kwargs.get('comp', lambda x, y: x < y)
index = end
inclusive_end = end - 1
if comp(value, array[start]):
index = start
while start <= inclusive_end:
mid = (start + inclusive_end)//2
if not comp(value, array[mid]):
start = mid + 1
else:
index = mid
inclusive_end = mid - 1
return index

def lower_bound(array, value, **kwargs):
"""
Finds the the index of the first occurence of an element which is not
less than the given value according to specified order,
in the given OneDimensionalArray using a variation of binary search method.

Parameters
==========

array: OneDimensionalArray
The array in which the lower bound has to be found.
start: int
The staring index of the portion of the array in which the upper bound
of a given value has to be looked for.
Optional, by default 0
end: int, optional
The ending index of the portion of the array in which the upper bound
of a given value has to be looked for.
Optional, by default the index
of the last position filled.
comp: lambda/function
The comparator which is to be used
for specifying the desired ordering.
Optional, by default, less than or
equal to is used for comparing two
values.

Returns
=======

index: int
Index of the lower bound of the given value in the given OneDimensionalArray

Examples
========

>>> from pydatastructs import lower_bound, OneDimensionalArray as ODA
>>> arr1 = ODA(int, [4, 5, 5, 6, 7])
>>> lb = lower_bound(arr1, 5, end=4, comp=lambda x, y : x < y)
>>> lb
1
>>> arr = ODA(int, [7, 6, 5, 5, 4])
>>> lb = lower_bound(arr, 5, start=0, comp=lambda x, y : x > y)
>>> lb
2

Note
====

DynamicOneDimensionalArray objects may not work as expected.
"""
start = kwargs.get('start', 0)
end = kwargs.get('end', len(array))
comp = kwargs.get('comp', lambda x, y: x < y)
index = end
inclusive_end = end - 1
if not comp(array[start], value):
index = start
while start <= inclusive_end:
mid = (start + inclusive_end)//2
if comp(array[mid], value):
start = mid + 1
else:
index = mid
inclusive_end = mid - 1
return index
90 changes: 87 additions & 3 deletions pydatastructs/linear_data_structures/tests/test_algorithms.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from pydatastructs import (
merge_sort_parallel, DynamicOneDimensionalArray,
OneDimensionalArray, brick_sort, brick_sort_parallel,
heapsort, matrix_multiply_parallel, counting_sort, bucket_sort,
cocktail_shaker_sort, quick_sort, longest_common_subsequence, is_ordered)

heapsort, matrix_multiply_parallel, counting_sort, bucket_sort, cocktail_shaker_sort, quick_sort, longest_common_subsequence,
is_ordered, upper_bound, lower_bound)

from pydatastructs.utils.raises_util import raises
import random
Expand Down Expand Up @@ -157,3 +156,88 @@ def test_is_ordered():
arr4.delete(0)
output = is_ordered(arr4)
assert output == expected_result

def test_upper_bound():
ODA = OneDimensionalArray
arr1 = ODA(int, [3, 3, 3])
output = upper_bound(arr1, 3)
expected_result = 3
assert expected_result == output

arr2 = ODA(int, [4, 4, 5, 6])
output = upper_bound(arr2, 4, end=3)
expected_result = 2
assert expected_result == output

arr3 = ODA(int, [6, 6, 7, 8, 9])
output = upper_bound(arr3, 5, start=2, end=4)
expected_result = 2
assert expected_result == output

arr4 = ODA(int, [3, 4, 4, 6])
output = upper_bound(arr4, 5, start=1, end=3)
expected_result = 3
assert expected_result == output

arr5 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = upper_bound(arr5, 6, comp=lambda x, y: x > y)
expected_result = 5
assert expected_result == output

arr6 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = upper_bound(arr6, 2, start=2, comp=lambda x, y: x > y)
expected_result = 8
assert expected_result == output

arr7 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = upper_bound(arr7, 9, start=3, end=7, comp=lambda x, y: x > y)
expected_result = 3
assert expected_result == output

arr8 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = upper_bound(arr8, 6, end=3, comp=lambda x, y: x > y)
expected_result = 3
assert expected_result == output


def test_lower_bound():
ODA = OneDimensionalArray
arr1 = ODA(int, [3, 3, 3])
output = lower_bound(arr1, 3, start=1)
expected_result = 1
assert expected_result == output

arr2 = ODA(int, [4, 4, 4, 4, 5, 6])
output = lower_bound(arr2, 5, end=3)
expected_result = 3
assert expected_result == output

arr3 = ODA(int, [6, 6, 7, 8, 9])
output = lower_bound(arr3, 5, end=3)
expected_result = 0
assert expected_result == output

arr4 = ODA(int, [3, 4, 4, 4])
output = lower_bound(arr4, 5)
expected_result = 4
assert expected_result == output

arr5 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = lower_bound(arr5, 5, comp=lambda x, y: x > y)
expected_result = 5
assert expected_result == output

arr6 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = lower_bound(arr6, 2, start=4, comp=lambda x, y: x > y)
expected_result = 8
assert expected_result == output

arr7 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = lower_bound(arr7, 9, end=5, comp=lambda x, y: x > y)
expected_result = 0
assert expected_result == output

arr8 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
output = lower_bound(arr8, 6, end=3, comp=lambda x, y: x > y)
expected_result = 1
assert expected_result == output