My for loop randomly gets stuck and doesnt complete its range - python

My for loop keeps stopping in main. It is supposed to call the def binarySearch 10000 times and add up the total number of guesses from each try. I notice when I lower the range down to 100, the for loop works MOST of the time. It still sometimes just freezes and never prints anything.
import random
def binarySearch(left, right, x):
counter = 0 #guesses per try
while left <= right:
counter+=1
mid = (right + left) // 2
if x == mid:
return counter
elif x > mid:
left = mid
elif x < mid:
right = mid
def main():
guesses = 0 # total number of guesses
###Problem here###
for x in range(10001):
lef = 0 #min
rig = 1000 #max
num = random.randint(lef, rig) #random number
guesses += binarySearch(lef, rig, num) #calls binarySearch and sums the total guesses
print("Total guesses from 10000 tries: ", guesses)
main()
EDIT:
I narrowed down the problem to:
elif x < mid:
left = mid
I tested it quite a few times and came to the conclusion that the while loop ends up getting stuck repeating that else if statement. I do not know why this is happening though.

The reason it is getting stuck is because there is an error in the boundary condition. If the number is not equal to mid and the left and right should be equal to mid + 1 and mid - 1 respectively. No need to consider mid again. Since you are considering mid again and again, your condition is never coming out of this cycle and hence the infinite loop.
elif x > mid:
left = mid + 1
elif x < mid:
right = mid - 1
These should be your new values for left and right. No need to consider mid again because it is not equal to the target value, if it were, the top most if the condition would have been true.

Related

First occurrence of a positive integer using binary search

I've tried to write a code that finds the first occurrence of a positive integer in a sorted array using binary search, but it doesn't work.
Here's the code:
def findFirstOccurrence(arr):
(left, right) = (0, len(arr) - 1)
result = -1
while left <= right:
mid = (left + right) // 2
if 0 < arr[mid]:
result = mid
right = mid - 1
elif 0 > arr[mid]:
right = mid - 1
else:
left = mid + 1
return result
Below code will help.
def findFirstOccurrence(arr):
left,right=0,len(arr)-1
while not((left==right) or (right==left+1)):
mid=(left+right)//2
if arr[mid]>0:
right=mid
else:
left=mid
if arr[left]>0:
return left
if arr[right]>0:
return right
return None
print(findFirstOccurrence([-4,-3,-2,-1,0,1,2,5,3,4,6]))
Output
5
The error comes from the lines
elif 0 > arr[mid]:
right = mid - 1
If arr[mid] is negative, we decrease the right pointer — i.e. we look further to the left. But if the array is sorted in ascending order, then anything further to the left of a negative number is still negative, so we'll never reach a positive number and the program will fail.
What we want to do instead is look to the right, where the positive numbers are. The lines
else:
left = mid + 1
already do that in the case that arr[mid] == 0. Removing the two erroneous lines would allow the case that arr[mid] < 0 to fall through and do what we want.
Final code:
if arr[mid] > 0:
result = mid
right = mid - 1
else:
left = mid + 1

Binary Search Guessing Game

I am trying to create a small program that uses binary search algorithm to have the computer itself guess a number given a set of arguments.
The arguments the function takes in are how many 'tries' it has to guess and the values that it can guess between (for example, 5 tries to guess the number in between 0 and 10).
When I run my code, it only seems to run my else: statement, regardless of the arguments I pass in.
I am missing something in my "if" statements but I am stumped and can't figure out what I have done incorrectly.
Thank you so much for your time and help!
import random
def guess_random_number_binary(tries, start, stop):
rand_number = random.randint(start,stop)
num_list = range(start,stop)
lower_bound = start
upper_bound = len(str(stop)) - 1
while lower_bound <= upper_bound:
pivot = (lower_bound + upper_bound) // 2
pivot_value = num_list[pivot]
if pivot_value == rand_number and tries > 0:
print("Found it! " + str(rand_number))
return pivot
if pivot_value > rand_number and tries > 0:
upper_bound = pivot - 1
tries -= 1
print ("incorrect guess" + str(tries) + " Remaining")
else:
lower_bound = pivot + 1
print("Out of tries")
return
guess_random_number_binary(5, 0 ,10)
UPDATE:
import random
def guess_random_number_binary(tries, start, stop):
rand_number = random.randint(start,stop)
num_list = []
lower_bound = start
upper_bound = stop
num_list = range(start,stop+1)
while lower_bound <= upper_bound:
pivot = (lower_bound + upper_bound) // 2
pivot_value = num_list[pivot]
if tries > 0:
if pivot_value == rand_number:
print("Found it! " + str(rand_number))
return pivot
elif pivot_value > rand_number:
upper_bound = pivot - 1
tries -= 1
print ("Guessed " + str(pivot) + " incorrectly \n" + str(tries) + " Tries remaining")
elif pivot_value < rand_number:
lower_bound = pivot + 1
tries -= 1
print ("Guessed " + str(pivot) + " incorrectly \n" + str(tries) + " Tries remaining")
else:
print ("Ran out of tries!")
break
guess_random_number_binary(5, 20 ,30)
I have been trying to debug and even if my new code is over simplified, I hope that it is at least headed in the right direction.
I believe the main issue stands with how I am creating "num_list" as pointed out by an answer below. Receiving an IndexError, which makes sense in theory. However, I cannot seem to find an alternative for creating that list.
Once again, thank you.
You're resetting the value of pivot at the beginning of each loop, so
You passed in the value 10 for stop. So in the line upper_bound = len(str(stop)) - 1,
upper_bound = len(str(stop))-1 = len(str(10) = len ('10')-1 = 1.
Your while loop never runs, because lower_bound is never less than upper_bound.
You perhaps were at some point intending to do upper_bound = len(num_list)-1 and somehow wrote upper_bound = len(str(stop)) - 1 instead. However, even that would not have been correct; the length of range(start,stop) is stop-start, not stop-start+1 (range is not inclusive of stop). And using range in the first place is unnecessarily confusing and would not work for larger start values. For instance, suppose (start, stop) = (20, 30). Then pivot will be 25. But the range object will have only 10 elements, so num_range[pivot] will return an error.

Why does binary search algorithm not work?

I've copied code from the book "grokking algorithms" for finding an item in the list using the binary search algorithm. The code launches great and returns no errors. However, sometimes it just doesn't work for certain numbers, for example if i call it to find "1". What's wrong?
def binary_search(list, item):
low = 0
high = len(list) - 1
while low <= high:
mid = (low + high)/2
guess = list[mid]
if guess == item:
return mid
if guess > item:
high = mid + 1
else:
low = mid + 1
return None
list1 = []
for i in range (1, 101):
list1.append(i)
print(list1)
print(binary_search(list1, 1))
Two issues:
Use integer division (so it will work in Python 3): mid = (low + high)//2
When guess > item you want to exclude the guessed value from the next range, but with high = mid + 1 it still is in the range. Correct to high = mid - 1

Why is my Sum algorithm stuck in a loop/Taking a very long time?

I'm new to the world of algorithms and have attempted to write my own after studying a few. I'm trying to find if the sum of two numbers in an array sum up to a target value. The issue is that it loops continuously and I would be extremely grateful for some help as i just cant seem to see what it is.
arr = [1,2,3,4,4]
target = 8
def findSum(arr, target):
if len(arr) <= 1:
return False
low = 0
high = len(arr) - 1
while low <= high:
for i in range(low, high):
for j in range(high, low, -1):
if (arr[i]+arr[j]) == target:
return(arr[i], arr[j])
elif (arr[i]+arr[j]) > target:
high -= 1
else:
low += 1
return False
findSum(arr, target)
P.S Sorry about the uncommented code, I didn't think it was necessary with the simplicity of it.
You are doing it incorrectly,
You might want to try this,
arr = [1,2,3,4,4]
target = 8
def findSum(arr, target):
start = 0
end = len(arr) - 1
while start < end:
if arr[start] + arr[end] == target:
return (arr[start], arr[end])
elif arr[start] + arr[end] < target:
start += 1
else:
end -= 1
return False
print(findSum(arr, target))
OUTPUT:
(4, 4)
If what you're after is any two elements that sum up to the target value, this is much simpler version:
arr = [1,2,3,4,4]
target = 8
def findSum(arr, target):
for i in range(len(arr)):
for j in range(len(arr)):
if (arr[i]+arr[j]) == target and i != j:
return(arr[i], arr[j])
return False
print(findSum(arr, target))
Problem with your code:
This is your outer loop
for i in range(low, high):
This is your inner loop
for j in range(high, low, -1):
While the outer loop is still at i==0, the inner loop has run len(arr) times, increasing the low from 0 to 4.
Then it will stuck there. Because low == high == 4, it will not leave the while loop, nor run the range.

Binary Search Algorithm Testing Repeated Values

The python code below seems to work fine. If I change the code as per the comments, it still seems to work. Are there conditions where the algorithm will fail if I use high = middle instead of high = middle - 1 and low = middle instead of low = middle + 1?
haystack = [3,5,7,8,9,23,65,89,100]
needle = 89
low = 0
high = len(haystack)
while (low < high):
middle = (high + low)//2
if (needle < haystack[middle] ):
high = middle - 1 # what about just high = middle?
elif (needle > haystack[middle]):
low = middle + 1 # what about just low = middle?
elif (needle == haystack[middle]):
print ("The needle was found at index " + str (middle))
break
That's because you are considering only on cases where the value is in the list (Maybe there is a case where the list contains the element and these lines are needed but i couldn't think of one)
think of the following simple case:
haystack = [1,2]
needle = 3
the rest of the code...
you will be stuck in an infinite loop without the current version of the code.
Note: as #vivek_23 mentioned you are already checking middle so there is no need for that extra iteration.
In order for the loop to complete, each iteration must decrease the range by at least 1. If it doesn't you get an infinite loop.
If you ever reach a point where low is an even number and high = low+1, the calculation for middle will always return low. You will continually test the same location.
The explanation as to why you would enter an infinite loop has to do with your condition:
while (low < high):
Not updating the conditions (low or high) would mean that your loop condition does not change and low (if it begins lower than high) will forever be less than high.
Another note is, it would help (you) to break code up into functions. It will make the debugging process easier for you in the future. Here's a not-so-pythonic implementation:
def binary_search(sorted_list, target_value, low, high):
if (low > high):
return False
mid = (low + high) // 2
if (mid == 0 and sorted_list[mid] != target_value):
return False
if sorted_list[mid] == target_value:
return True
elif sorted_list[mid] < target_value:
return binary_search(sorted_list, target_value, mid + 1, high)
else:
return binary_search(sorted_list, target_value, low, mid)
If you want to ensure you can reach every item in a list, try testing if your algorithm finds everything in the list:
sorted_list = [1, 2, 3, 4, 5, 6, 7, 8, 8, 9]
not_in_list = 42
def test_binary_search(sorted_list):
for each_item in sorted_list:
found = binary_search(sorted_list, each_item, 0, len(sorted_list) - 1)
if not found:
print("{} not found by binary_search".format(each_item))
else:
print("found {} ".format(each_item))
test_binary_search(sorted_list)
Similarly, you'd like your algorithm to behave correctly when given an item that is not in your list.
def test_item_not_found(sorted_list, item):
expected = False
actual = binary_search(sorted_list, item, 0, len(sorted_list) - 1)
status = "Passed" if actual == expected else "Failed"
print("status {} expected: {}, actual: {}".format(status, expected, actual))
test_item_not_found(sorted_list, not_in_list)

Categories

Resources