Python Binary Search - python

I'm a beginner programmer and I wanted to make my own binary search algorithm from scratch without any help. It's safe to say it's not going well. I was hoping someone could find the error in my code. I fixed a few issues, but now I've ran into the issue where if can't find values greater than the middle index, and it is not ending once it reaches a single value not equal to the target.
import random
userInput = 100
numbers = []
for i in range(100):
numbers.append(random.randint(0, 100))
#Adding 100 to use as a test
numbers.append(100)
numbers.sort()
print(numbers)
def binarySearch(list, target):
midIndex = int(len(list)/2)
midValue = list[midIndex]
if midValue == target:
print("We found " + str(target))
elif target > midValue:
newList = numbers[midIndex:]
binarySearch(newList, target)
elif target < midValue:
newList = numbers[:midIndex]
binarySearch(newList, target)
elif len(list) == 1 and list[0] != target:
print(target + " is not in the list")
else:
print("It's not in the list")
binarySearch(numbers, userInput)

There are two main issues with your code:
mid is being used to represent both an index and a value.
The end of binarySearch() is never reached.
Issue #1: mid
When creating newList using a slice of list, you are using mid as an index:
elif target > mid:
newList = numbers[mid:]
However, mid is not an index. It is the value in the middle of list:
mid = list[int(len(list)/2)]
Try using two variables:
midIndex = int(len(list)/2)
midValue = list[midIndex]
Issue #2: binarySearch()
binarySearch() never reaches the final elif to see if target is not in the list.
The final elif is only reached after checking the following conditions:
if midValue == target:
...
elif target > midValue:
...
elif target < midValue:
...
Clearly, if midValue and target are two numbers, one of these comparisons must return True.
Because of the order of the checks performed, binarySearch() never ends up getting to this section:
elif len(list) == 1 and list[0] != target:
...
To fix this issue, try rearranging your if statements so that binarySearch() reaches this part of the code.

Related

list out of range in python adjacent numbers question

I have a question which is asking to compare 2 numbers in a list, specifically if the adjacent numbers are positive or negatives
However I am stuck on the first part of the question. My idea is to compare the first number using its index with the second number so i+1, but inevitably is goes out of range.
I am missing something here, help is appreciated.
my_list=[-1,2,-3,-4,-5,1,2]
for i in range(len(my_list)):
print (my_list[i])
print (my_list[i+1])
I have been working on it and this is the full questions
Given a sequence of numbers, find and print the first pair of adjacent elements which have the same sign. If there is no such pair, print NONE.
I'm not allowed to use zip in this case.
However I cannot do the last bit where it ask pro print none if no pairs are there
s = input()
my_list_str = s.split()
my_list = []
for beta in my_list_str:
my_list.append(int(beta))
for i in range(len(my_list)-1):
if my_list[i]>0 and my_list[i+1] >0:
print (my_list[i], end =' ')
print (my_list[i+1])
break
elif my_list[i]<0 and my_list[i+1] <0:
print (my_list[i], end =' ')
print (my_list[i+1])
break
Two points:
Most of the time it is considered "unpythonic" to use indices to iterate over a list
The for-loop has the option to close with an else-condition: The code in the else condition is only executed if the for-loop isn't left via a break
So, you could try the following:
for first, second in zip(my_list[:-1], my_list[1:]):
if (first < 0 and second < 0) or (first > 0 and second > 0):
print(first, second)
break
else:
print("NONE")
EDIT: If you need to use indices, then you could do:
for i in range(len(my_list) - 1):
first, second = my_list[i], my_list[i+1]
if (first < 0 and second < 0) or (first > 0 and second > 0):
print(first, second)
break
else:
print("NONE")
If you are not allowed to use the else-option of the for-loop, then you could try:
found = False
for i in range(len(my_list) - 1):
first, second = my_list[i], my_list[i+1]
if (first < 0 and second < 0) or (first > 0 and second > 0):
print(first, second)
found = True
break
if not found:
print("NONE")
Because you want to print (my_list[i+1])
Your list size is 7 -> when i = 6 -> [i+1] = 7
=> my_list[7] <- out of range
You can do it like below:
my_list=[-1,2,-3,-4,-5,1,2]
list_len = len(my_list)
for i in range(list_len-1):
print(f'comparison {i+1}')
print (my_list[i])
print (my_list[i+1])
my_list=[-1,2,-3,-4,-5,1,2]
list_len = len(my_list)
x = 7 # any positive number
for i in range(x):
if i < list_len:
print (my_list[i])
if i+1 < list_len:
print (my_list[i+1])

How to check elements in a list WITHOUT using for loops?

Apologies if the title of the question is phrased badly. I am currently trying to make a function that takes in a list of integers from 1 to n, where n is the length of the list. The function should return the first value that is repeated in the list. Duplicates are NOT always next to one another. If one or more integers is less than 1 or if it is not a list, the function should return -1. If there are no duplicates, return 0.
This is my current code:
def find_duplicates(ls):
if type(ls) != list:
return -1
non_dupe = []
i = 0
while i < len(ls):
if ls[i] < 1:
return -1
break
if ls.count(i) > 1:
return i
break
else:
non_dupe.append(i)
i += 1
if len(non_dupe) == len(ls):
return 0
While this code works for a majority of test cases, it doesn't seem to pass
print(find_duplicates([1, 2, 2, 0]))
as it returns 2 instead of the expected -1. I am relatively new to Python and I can't seem to be able to fix this error. I've tried searching for ways to counter this problem but I am not allowed to use for loops to check through a list. Any help is greatly appreciated.
EDIT: I am not allowed to use any of the following but anything else is accepted.
for loops
min() / max()
enumerate() / zip ()
sort()
negative indexing e.g ls[-1]
list slicing
Your code returns a duplicate prematurely; traversing the list, the function first finds 2 as a duplicate, return it, and halts the function immediately. But it has not seen the 0 at the end.
So, you need to let the function see the list all the way towards the end, looking for a negative number. If a negative number is found along the way, you can halt the function. If it does not see a negative number until the end, then let it return the duplicate value:
def find_duplicates(ls):
if not isinstance(ls, list): # check whether ls is a list
return -1
dup = 0
seen = [] # list of numbers seen so far
i = 0 # index
while i < len(ls):
if ls[i] < 1: # if a negative number is found, return -1
return -1
if ls[i] in seen and dup == 0:
dup = ls[i]
seen.append(ls[i])
i += 1
return dup
print(find_duplicates([1, 2, 2, 0])) # -1
print(find_duplicates([1, 1, 2, 2, 3])) # 1
Problem is beacause you are breaking while loop when find a duplicated. In that case, function is finding first the duplicated.
Try this:
def find_duplicates(ls):
if type(ls) is not list:
return -1
duplicated = 0
i = 0
while i < len(ls):
if ls[i] < 1:
return -1
if ls.count(ls[i]) > 1 and duplicated == 0
duplicated = ls[i]
i += 1
return duplicated
Your test case returns 2 because 2 stay at lower indexes comparing to 0.
I would suggest to sort the list before moving on:
def find_duplicates(ls):
if type(ls) != list:
return -1
sorted_list = ls.sorted() #Assign sorted `ls` to another variable, while keeping the order of `ls` intact
non_dupe = []
i = 0
while i < len(ls):
if ls[i] < 1:
return -1
break
if ls.count(i) > 1:
return i
break
else:
non_dupe.append(i)
i += 1
if len(non_dupe) == len(ls):
return 0
Another method I would recommend is using set - a built-in data type of Python. Maybe you should consider trying this approach later on when all test cases are passed. Have a look at this Tutorial for set usage: https://www.w3schools.com/python/python_sets.asp.
You were very close. Try this:
def find_duplicates(ls):
if type(ls) != list:
return -1
non_dupe = []
i = 0
while i < len(ls):
if ls[i] < 1:
return -1
elif ls[i] in non_dupe:
return ls[i]
else:
non_dupe.append(i)
i += 1
return 0
my_list = [1,2,2,0]
result = list(set(filter(lambda x: my_list.count(x) > 1 , my_list)))
# Result => [2]
I hope this solves your problem

How to find the second lowest and second highest element in a list?

So, the function works with no errors, but the output for min2(2nd lowest value) in the list is incorrect. I cant seem to find the solution.
Python 3.8.6
def max2min2(list1):
max1=list1[0]
min1=list1[0]
max2=None
min2=None
for item in list1:
if item>max1:
max2=max1
max1=item
elif max2==None or max2<item:
max2=item
if item<min1:
min2=min1
min1=item
elif min2==None or min2>item:
min2=item
return max2,min2
list1 = [1,2,3]
max2,min2=max2min2(list1)
print(min2,max2) # 1 2
With the simple input list of [1,2,3] the output of maxmin2 is (1,2), although the expected output is (2,2).
Now if this does not to need to be speed optimized, simple way would be just take a set of the numbers, sort them, and take the second element from each end:
vals = [1,1,3,2,2]
filtered_vals = sorted(set(vals))
and then
# Second lowest
In [37]: filtered_vals[1]
Out[37]: 2
# Second highest
In [36]: filtered_vals[-2]
Out[36]: 2
add some Exception and special case handling if needed.
A simple and readable solution is to sort the list first, then directly index the values you want. I added a unique argument which specifies whether you want to look at the number values (most intuitive) or keep duplicate values in the list (so that the second highest number in [1,2,2] is 2).
def second_lowest_and_highest_using_sort(nums, unique=True):
if unique:
nums = list(set(nums))
if len(nums) == 1:
raise ValueError('Second lowest/highest number is undefined for a list of length 1.')
nums = sorted(nums)
return (nums[1], nums[-2])
A more verbose approach without sorting first:
def second_lowest_and_highest(nums, unique=True):
if unique:
nums = list(set(nums))
if len(nums) == 1:
raise ValueError('Second lowest/highest number is undefined for a list of length 1.')
lowest, highest = float('inf'), float('-inf')
second_lowest, second_highest = None, None
low_delta, high_delta = float('inf'), float('inf')
for num in nums:
low_delta_new = num - lowest
if low_delta_new < 0:
second_lowest = lowest
lowest = num
elif low_delta_new <= low_delta:
second_lowest = num
low_delta = low_delta_new
high_delta_new = num - highest
if high_delta_new > 0:
second_highest = highest
highest = num
elif high_delta_new <= high_delta:
second_highest = num
high_delta = high_delta_new
return (second_lowest, second_highest)

list indices must be integers or slices, not NoneType

I was dealing with this problem when I am trying to create quick sort on my own
I don't know how to fix the error and I most certainly do not know if any other problem exists in my code.
def Qsort(list):
if len(list) < 2:
return list
Qsorthelper(list, 0, len(list)-1)
def Qsorthelper(list, first, last):
if last>first:
split=partition(list, first, last)
Qsorthelper(list, first, split-1)
Qsorthelper(list, split+1, last)
def median(list):
if len(list) %2 == 0:
return (len(list)//2+len(list)//2-1)/2
else:
return len(list)//2
def finditem(list, num):
for i in range(0,len(list)-1):
if num == list[i]:
return i
def choosepivot(list):
if list[0] > list[len(list)-1] > list[median(list)] or list[0] <
list[len(list)-1] < list[median(list)]:
return list[len(list)-1]
elif list[len(list)-1] > list[0] > list[median(list)] or
list[len(list)-1] < list[0] < list[median(list)]:
return list[0]
else:
return list[median(list)]
def partition(list, first, last):
pivot = choosepivot(list)
list.append(pivot)
list.remove(pivot)
left = list[first]
right = list[last-1]
done = False
while not done:
while left <= right and list[left] <= pivot:
left = left + 1
while right >= left and list[right] >= pivot:
right = right - 1
if right < left:
done = True
else:
temp = list[left]
list[left] = list[right]
list[right] = temp
temp = list[finditem(list,pivot)]
list[finditem(list,pivot)] = list[right]
list[right] = temp
return right
alist = [54,26,93,17,77,31,44,55,20]
Qsort(alist)
print(alist)
I don't know what is the problem that caused the error but I really want to fix it
any suggestions or answers?
edit:
I changed the finditem part to fix the i not finding the last element:
def finditem(alist, num):
for i in range(0,len(alist)):
if num == alist[i]:
return i
and I got this error:
File "C:\Users\user\Desktop\ITB\failed quick sort.py", line 59, in partition
alist[finditem(alist,pivot)] = alist[right]
IndexError: list index out of range
>>>
temp = list[finditem(list,pivot)]
list[finditem(list,pivot)] = list[right]
list[right] = temp
this ipart is the part where I switch the final item with the pivot to complete one cycle, something is wrong here but I want to know how.
The code causing the mentioned error is in your finditem function, which stops before the last element. If the item you are trying to find happens to be the last element, None is returned, and is used as an index for list (You shouldn't use list as a variable name, since it has another meaning in python).
This isn't the only problem though. The median function can also return floats, so anything indexing with it (like this list[median(list)]) could fail for even sized lists.
There are additional errors as well. I would recommend testing each function individually, and perhaps looking at some existing implementations for guidance.

Generate triangular numbers

This function is supposed to take an integer, generate the list of triangular numbers within range of that integer, check that list for the longest list of numbers whose sum == number and return them in a list, otherwise if there is no such list within that range of triangular numbers, return an empty list. I thought I had it somewhat, and it runs on python tutor.com, but when I run it in IDLE, nothing happens.
def checkio(number):
x = 4
lst = [1, 3, 6]
new = []
if number == 0:
return []
elif number == 1:
return [1]
elif number == 3:
return []
elif number == 6:
return []
elif number == 4:
return [1, 3]
elif number == 7:
return [1, 6]
elif number == 10:
return [1, 3, 6]
elif number > 10:
while number > lst[-1]: # Generates a list of all the triangular numbers up to number
for item in range(lst[-1]):
lst.append(x + lst[-1])
x += 1
go = []
start = 0
end = 0
count = 0
while count < len(lst) * 2:
if sum(lst[start:end+1]) < number:
end += 1
count += 1
elif sum(lst[start:end+1]) > number:
start += 1
count += 1
elif sum(lst[start:end+1]) == number:
go.append(lst[start:end+1])
break
return go
if count >= len(lst) * 2:
return []
In the code you post you are just declaring a function. In order to run it, you have to make a call to that function. In your case, it receives one argument, so you have to pass it inside the parentheses ():
number = 5 # for example
checkio(number) # this is the function call
As Bakuriu commented: If you want to get a result change the order of this lines:
elif sum(lst[start:end+1]) == number:
go.append(lst[start:end+1])
break
return go
To :
elif sum(lst[start:end+1]) == number:
go.append(lst[start:end+1])
return go
break
This will return a value before escaping the while loop. As noted in the comments (thanks Andrea Corbellini) you can also remove the break statement and it will work well. Because after the return statement by definition escapes the function.
Also to run in idle once defined (you copied the code and pressed return), call it as Christian says.
This way you will check if works.
Note that you don't check in the ifelse clauses for the numbers 2, 5, 8 and 9. If you call this function with checkio(5), like suggested by Crhistian, it will not return anything because it doesn't have anything to return!

Categories

Resources