Skip to content

Commit

Permalink
Interpolation search - fix endless loop bug, divide 0 bug and update …
Browse files Browse the repository at this point in the history
…description (TheAlgorithms#793)

* fix endless loop bug, divide 0 bug and update description

fix an endless bug, for example, if collection = [10,30,40,45,50,66,77,93], item = 67.

fix divide 0 bug,  when right=left it is not OK to point = left + ((item - sorted_collection[left]) * (right - left)) // (sorted_collection[right] - sorted_collection[left])

update 'sorted' to 'ascending sorted' in description to avoid confusion

* delete swap files

* delete 'address' and add input validation
  • Loading branch information
weixuanhu authored and poyea committed May 18, 2019
1 parent f3608ac commit b6c3fa8
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 24 deletions.
80 changes: 59 additions & 21 deletions searches/interpolation_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,23 @@

def interpolation_search(sorted_collection, item):
"""Pure implementation of interpolation search algorithm in Python
Be careful collection must be sorted, otherwise result will be
Be careful collection must be ascending sorted, otherwise result will be
unpredictable
:param sorted_collection: some sorted collection with comparable items
:param sorted_collection: some ascending sorted collection with comparable items
:param item: item value to search
:return: index of found item or None if item is not found
"""
left = 0
right = len(sorted_collection) - 1

while left <= right:
#avoid devided by 0 during interpolation
if sorted_collection[left]==sorted_collection[right]:
if sorted_collection[left]==item:
return left
else:
return None

point = left + ((item - sorted_collection[left]) * (right - left)) // (sorted_collection[right] - sorted_collection[left])

#out of range check
Expand All @@ -31,66 +38,97 @@ def interpolation_search(sorted_collection, item):
if current_item == item:
return point
else:
if item < current_item:
right = point - 1
else:
left = point + 1
if point<left:
right = left
left = point
elif point>right:
left = right
right = point
else:
if item < current_item:
right = point - 1
else:
left = point + 1
return None


def interpolation_search_by_recursion(sorted_collection, item, left, right):

"""Pure implementation of interpolation search algorithm in Python by recursion
Be careful collection must be sorted, otherwise result will be
Be careful collection must be ascending sorted, otherwise result will be
unpredictable
First recursion should be started with left=0 and right=(len(sorted_collection)-1)
:param sorted_collection: some sorted collection with comparable items
:param sorted_collection: some ascending sorted collection with comparable items
:param item: item value to search
:return: index of found item or None if item is not found
"""
point = left + ((item - sorted_collection[left]) * (right - left)) // (sorted_collection[right] - sorted_collection[left])

#avoid devided by 0 during interpolation
if sorted_collection[left]==sorted_collection[right]:
if sorted_collection[left]==item:
return left
else:
return None

point = left + ((item - sorted_collection[left]) * (right - left)) // (sorted_collection[right] - sorted_collection[left])

#out of range check
if point<0 or point>=len(sorted_collection):
return None

if sorted_collection[point] == item:
return point
elif sorted_collection[point] > item:
return interpolation_search_by_recursion(sorted_collection, item, left, point-1)
elif point<left:
return interpolation_search_by_recursion(sorted_collection, item, point, left)
elif point>right:
return interpolation_search_by_recursion(sorted_collection, item, right, left)
else:
return interpolation_search_by_recursion(sorted_collection, item, point+1, right)
if sorted_collection[point] > item:
return interpolation_search_by_recursion(sorted_collection, item, left, point-1)
else:
return interpolation_search_by_recursion(sorted_collection, item, point+1, right)

def __assert_sorted(collection):
"""Check if collection is sorted, if not - raises :py:class:`ValueError`
"""Check if collection is ascending sorted, if not - raises :py:class:`ValueError`
:param collection: collection
:return: True if collection is sorted
:raise: :py:class:`ValueError` if collection is not sorted
:return: True if collection is ascending sorted
:raise: :py:class:`ValueError` if collection is not ascending sorted
Examples:
>>> __assert_sorted([0, 1, 2, 4])
True
>>> __assert_sorted([10, -1, 5])
Traceback (most recent call last):
...
ValueError: Collection must be sorted
ValueError: Collection must be ascending sorted
"""
if collection != sorted(collection):
raise ValueError('Collection must be sorted')
raise ValueError('Collection must be ascending sorted')
return True


if __name__ == '__main__':
import sys

user_input = raw_input('Enter numbers separated by comma:\n').strip()

"""
user_input = raw_input('Enter numbers separated by comma:\n').strip()
collection = [int(item) for item in user_input.split(',')]
try:
__assert_sorted(collection)
except ValueError:
sys.exit('Sequence must be sorted to apply interpolation search')
sys.exit('Sequence must be ascending sorted to apply interpolation search')
target_input = raw_input('Enter a single number to be found in the list:\n')
target = int(target_input)
"""

debug = 0
if debug == 1:
collection = [10,30,40,45,50,66,77,93]
try:
__assert_sorted(collection)
except ValueError:
sys.exit('Sequence must be ascending sorted to apply interpolation search')
target = 67

result = interpolation_search(collection, target)
if result is not None:
print('{} found at positions: {}'.format(target, result))
Expand Down
11 changes: 8 additions & 3 deletions searches/quick_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,21 @@ def _partition(data, pivot):
"""
less, equal, greater = [], [], []
for element in data:
if element.address < pivot.address:
if element < pivot:
less.append(element)
elif element.address > pivot.address:
elif element > pivot:
greater.append(element)
else:
equal.append(element)
return less, equal, greater

def quickSelect(list, k):
#k = len(list) // 2 when trying to find the median (index that value would be when list is sorted)

#invalid input
if k>=len(list) or k<0:
return None

smaller = []
larger = []
pivot = random.randint(0, len(list) - 1)
Expand All @@ -41,4 +46,4 @@ def quickSelect(list, k):
return quickSelect(smaller, k)
#must be in larger
else:
return quickSelect(larger, k - (m + count))
return quickSelect(larger, k - (m + count))
93 changes: 93 additions & 0 deletions searches/test_interpolation_search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import unittest
from interpolation_search import interpolation_search, interpolation_search_by_recursion

class Test_interpolation_search(unittest.TestCase):
def setUp(self):
# un-sorted case
self.collection1 = [5,3,4,6,7]
self.item1 = 4
# sorted case, result exists
self.collection2 = [10,30,40,45,50,66,77,93]
self.item2 = 66
# sorted case, result doesn't exist
self.collection3 = [10,30,40,45,50,66,77,93]
self.item3 = 67
# equal elements case, result exists
self.collection4 = [10,10,10,10,10]
self.item4 = 10
# equal elements case, result doesn't exist
self.collection5 = [10,10,10,10,10]
self.item5 = 3
# 1 element case, result exists
self.collection6 = [10]
self.item6 = 10
# 1 element case, result doesn't exists
self.collection7 = [10]
self.item7 = 1

def tearDown(self):
pass

def test_interpolation_search(self):
self.assertEqual(interpolation_search(self.collection1, self.item1), None)

self.assertEqual(interpolation_search(self.collection2, self.item2), self.collection2.index(self.item2))

self.assertEqual(interpolation_search(self.collection3, self.item3), None)

self.assertEqual(interpolation_search(self.collection4, self.item4), self.collection4.index(self.item4))

self.assertEqual(interpolation_search(self.collection5, self.item5), None)

self.assertEqual(interpolation_search(self.collection6, self.item6), self.collection6.index(self.item6))

self.assertEqual(interpolation_search(self.collection7, self.item7), None)



class Test_interpolation_search_by_recursion(unittest.TestCase):
def setUp(self):
# un-sorted case
self.collection1 = [5,3,4,6,7]
self.item1 = 4
# sorted case, result exists
self.collection2 = [10,30,40,45,50,66,77,93]
self.item2 = 66
# sorted case, result doesn't exist
self.collection3 = [10,30,40,45,50,66,77,93]
self.item3 = 67
# equal elements case, result exists
self.collection4 = [10,10,10,10,10]
self.item4 = 10
# equal elements case, result doesn't exist
self.collection5 = [10,10,10,10,10]
self.item5 = 3
# 1 element case, result exists
self.collection6 = [10]
self.item6 = 10
# 1 element case, result doesn't exists
self.collection7 = [10]
self.item7 = 1

def tearDown(self):
pass

def test_interpolation_search_by_recursion(self):
self.assertEqual(interpolation_search_by_recursion(self.collection1, self.item1, 0, len(self.collection1)-1), None)

self.assertEqual(interpolation_search_by_recursion(self.collection2, self.item2, 0, len(self.collection2)-1), self.collection2.index(self.item2))

self.assertEqual(interpolation_search_by_recursion(self.collection3, self.item3, 0, len(self.collection3)-1), None)

self.assertEqual(interpolation_search_by_recursion(self.collection4, self.item4, 0, len(self.collection4)-1), self.collection4.index(self.item4))

self.assertEqual(interpolation_search_by_recursion(self.collection5, self.item5, 0, len(self.collection5)-1), None)

self.assertEqual(interpolation_search_by_recursion(self.collection6, self.item6, 0, len(self.collection6)-1), self.collection6.index(self.item6))

self.assertEqual(interpolation_search_by_recursion(self.collection7, self.item7, 0, len(self.collection7)-1), None)



if __name__ == '__main__':
unittest.main()

0 comments on commit b6c3fa8

Please sign in to comment.