Skip to content

Commit 80067d3

Browse files
authored
upper_bound and lower_bound functions added (#351)
1 parent e1134b1 commit 80067d3

File tree

3 files changed

+226
-5
lines changed

3 files changed

+226
-5
lines changed

pydatastructs/linear_data_structures/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
cocktail_shaker_sort,
3434
quick_sort,
3535
longest_common_subsequence,
36-
is_ordered
36+
is_ordered,
37+
upper_bound,
38+
lower_bound
3739
)
3840
__all__.extend(algorithms.__all__)

pydatastructs/linear_data_structures/algorithms.py

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
'cocktail_shaker_sort',
1616
'quick_sort',
1717
'longest_common_subsequence',
18+
'upper_bound',
19+
'lower_bound',
1820
'is_ordered'
1921
]
2022

@@ -796,7 +798,7 @@ def is_ordered(array, **kwargs):
796798
Parameters
797799
==========
798800
799-
array: Array
801+
array: OneDimensionalArray
800802
The array which is to be checked for having
801803
specified ordering among its elements.
802804
start: int
@@ -843,3 +845,136 @@ def is_ordered(array, **kwargs):
843845
if comp(array[i], array[i - 1]):
844846
return False
845847
return True
848+
849+
def upper_bound(array, value, **kwargs):
850+
"""
851+
Finds the index of the first occurence of an element greater than the given
852+
value according to specified order, in the given OneDimensionalArray using a variation of binary search method.
853+
854+
Parameters
855+
==========
856+
857+
array: OneDimensionalArray
858+
The array in which the upper bound has to be found.
859+
start: int
860+
The staring index of the portion of the array in which the upper bound
861+
of a given value has to be looked for.
862+
Optional, by default 0
863+
end: int, optional
864+
The ending index of the portion of the array in which the upper bound
865+
of a given value has to be looked for.
866+
Optional, by default the index
867+
of the last position filled.
868+
comp: lambda/function
869+
The comparator which is to be used
870+
for specifying the desired ordering.
871+
Optional, by default, less than or
872+
equal to is used for comparing two
873+
values.
874+
875+
Returns
876+
=======
877+
878+
index: int
879+
Index of the upper bound of the given value in the given OneDimensionalArray.
880+
881+
Examples
882+
========
883+
884+
>>> from pydatastructs import upper_bound, OneDimensionalArray as ODA
885+
>>> arr1 = ODA(int, [4, 5, 5, 6, 7])
886+
>>> ub = upper_bound(arr1, 5, start=0, end=4)
887+
>>> ub
888+
3
889+
>>> arr2 = ODA(int, [7, 6, 5, 5, 4])
890+
>>> ub = upper_bound(arr2, 5, comp=lambda x, y: x > y)
891+
>>> ub
892+
4
893+
894+
Note
895+
====
896+
897+
DynamicOneDimensionalArray objects may not work as expected.
898+
"""
899+
start = kwargs.get('start', 0)
900+
end = kwargs.get('end', len(array))
901+
comp = kwargs.get('comp', lambda x, y: x < y)
902+
index = end
903+
inclusive_end = end - 1
904+
if comp(value, array[start]):
905+
index = start
906+
while start <= inclusive_end:
907+
mid = (start + inclusive_end)//2
908+
if not comp(value, array[mid]):
909+
start = mid + 1
910+
else:
911+
index = mid
912+
inclusive_end = mid - 1
913+
return index
914+
915+
def lower_bound(array, value, **kwargs):
916+
"""
917+
Finds the the index of the first occurence of an element which is not
918+
less than the given value according to specified order,
919+
in the given OneDimensionalArray using a variation of binary search method.
920+
921+
Parameters
922+
==========
923+
924+
array: OneDimensionalArray
925+
The array in which the lower bound has to be found.
926+
start: int
927+
The staring index of the portion of the array in which the upper bound
928+
of a given value has to be looked for.
929+
Optional, by default 0
930+
end: int, optional
931+
The ending index of the portion of the array in which the upper bound
932+
of a given value has to be looked for.
933+
Optional, by default the index
934+
of the last position filled.
935+
comp: lambda/function
936+
The comparator which is to be used
937+
for specifying the desired ordering.
938+
Optional, by default, less than or
939+
equal to is used for comparing two
940+
values.
941+
942+
Returns
943+
=======
944+
945+
index: int
946+
Index of the lower bound of the given value in the given OneDimensionalArray
947+
948+
Examples
949+
========
950+
951+
>>> from pydatastructs import lower_bound, OneDimensionalArray as ODA
952+
>>> arr1 = ODA(int, [4, 5, 5, 6, 7])
953+
>>> lb = lower_bound(arr1, 5, end=4, comp=lambda x, y : x < y)
954+
>>> lb
955+
1
956+
>>> arr = ODA(int, [7, 6, 5, 5, 4])
957+
>>> lb = lower_bound(arr, 5, start=0, comp=lambda x, y : x > y)
958+
>>> lb
959+
2
960+
961+
Note
962+
====
963+
964+
DynamicOneDimensionalArray objects may not work as expected.
965+
"""
966+
start = kwargs.get('start', 0)
967+
end = kwargs.get('end', len(array))
968+
comp = kwargs.get('comp', lambda x, y: x < y)
969+
index = end
970+
inclusive_end = end - 1
971+
if not comp(array[start], value):
972+
index = start
973+
while start <= inclusive_end:
974+
mid = (start + inclusive_end)//2
975+
if comp(array[mid], value):
976+
start = mid + 1
977+
else:
978+
index = mid
979+
inclusive_end = mid - 1
980+
return index

pydatastructs/linear_data_structures/tests/test_algorithms.py

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
from pydatastructs import (
22
merge_sort_parallel, DynamicOneDimensionalArray,
33
OneDimensionalArray, brick_sort, brick_sort_parallel,
4-
heapsort, matrix_multiply_parallel, counting_sort, bucket_sort,
5-
cocktail_shaker_sort, quick_sort, longest_common_subsequence, is_ordered)
6-
4+
heapsort, matrix_multiply_parallel, counting_sort, bucket_sort, cocktail_shaker_sort, quick_sort, longest_common_subsequence,
5+
is_ordered, upper_bound, lower_bound)
76

87
from pydatastructs.utils.raises_util import raises
98
import random
@@ -157,3 +156,88 @@ def test_is_ordered():
157156
arr4.delete(0)
158157
output = is_ordered(arr4)
159158
assert output == expected_result
159+
160+
def test_upper_bound():
161+
ODA = OneDimensionalArray
162+
arr1 = ODA(int, [3, 3, 3])
163+
output = upper_bound(arr1, 3)
164+
expected_result = 3
165+
assert expected_result == output
166+
167+
arr2 = ODA(int, [4, 4, 5, 6])
168+
output = upper_bound(arr2, 4, end=3)
169+
expected_result = 2
170+
assert expected_result == output
171+
172+
arr3 = ODA(int, [6, 6, 7, 8, 9])
173+
output = upper_bound(arr3, 5, start=2, end=4)
174+
expected_result = 2
175+
assert expected_result == output
176+
177+
arr4 = ODA(int, [3, 4, 4, 6])
178+
output = upper_bound(arr4, 5, start=1, end=3)
179+
expected_result = 3
180+
assert expected_result == output
181+
182+
arr5 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
183+
output = upper_bound(arr5, 6, comp=lambda x, y: x > y)
184+
expected_result = 5
185+
assert expected_result == output
186+
187+
arr6 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
188+
output = upper_bound(arr6, 2, start=2, comp=lambda x, y: x > y)
189+
expected_result = 8
190+
assert expected_result == output
191+
192+
arr7 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
193+
output = upper_bound(arr7, 9, start=3, end=7, comp=lambda x, y: x > y)
194+
expected_result = 3
195+
assert expected_result == output
196+
197+
arr8 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
198+
output = upper_bound(arr8, 6, end=3, comp=lambda x, y: x > y)
199+
expected_result = 3
200+
assert expected_result == output
201+
202+
203+
def test_lower_bound():
204+
ODA = OneDimensionalArray
205+
arr1 = ODA(int, [3, 3, 3])
206+
output = lower_bound(arr1, 3, start=1)
207+
expected_result = 1
208+
assert expected_result == output
209+
210+
arr2 = ODA(int, [4, 4, 4, 4, 5, 6])
211+
output = lower_bound(arr2, 5, end=3)
212+
expected_result = 3
213+
assert expected_result == output
214+
215+
arr3 = ODA(int, [6, 6, 7, 8, 9])
216+
output = lower_bound(arr3, 5, end=3)
217+
expected_result = 0
218+
assert expected_result == output
219+
220+
arr4 = ODA(int, [3, 4, 4, 4])
221+
output = lower_bound(arr4, 5)
222+
expected_result = 4
223+
assert expected_result == output
224+
225+
arr5 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
226+
output = lower_bound(arr5, 5, comp=lambda x, y: x > y)
227+
expected_result = 5
228+
assert expected_result == output
229+
230+
arr6 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
231+
output = lower_bound(arr6, 2, start=4, comp=lambda x, y: x > y)
232+
expected_result = 8
233+
assert expected_result == output
234+
235+
arr7 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
236+
output = lower_bound(arr7, 9, end=5, comp=lambda x, y: x > y)
237+
expected_result = 0
238+
assert expected_result == output
239+
240+
arr8 = ODA(int, [7, 6, 6, 6, 6, 5, 4, 3])
241+
output = lower_bound(arr8, 6, end=3, comp=lambda x, y: x > y)
242+
expected_result = 1
243+
assert expected_result == output

0 commit comments

Comments
 (0)