Related
Is there a way to optimize this code?
I'm trying to find an element in the list where the sorted list is rotated. [8,9,10,11,12,6,7] Here the element I'm trying to find is 6 with an index 5.
class Solution:
def search(self, nums: List[int]) -> int:
lenn = len(nums)-1
pivot, mid = 0, (0 + lenn)//2
if(nums[0] > nums[lenn]):
while True:
if(nums[mid] > nums[mid+1]):
pivot = mid+1
break
if(nums[mid] < nums[mid-1]):
pivot = mid
break
if(nums[mid] > nums[lenn]):
mid += 1
if(nums[mid] < nums[lenn]):
mid -= 1
return pivot
Your solution is fast for small lists but not for big ones as it perform a linear-time search. To be faster, you can use a binary search. To do that, you need to use a keep a [start;end] range of values that contains the "pivot". You just need to take the middle item and find which part (left or right) contains increasing items.
Moreover, you can micro-optimize the code. The default implementation of Python is CPython which is a slow interpreter. It optimize almost nothing compared to compilers (of languages like C, Java, Rust) so you should do the optimizations yourself if you want a fast code. The idea is to use a linear search for few items and a binary search for many ones, and to store values in temporary variables not to compute them twice.
Here is the resulting implementation:
def search(nums):
first, last = nums[0], nums[-1]
# Trivial case
if first <= last:
return 0
lenn = len(nums)-1
# Micro-optimized fast linear search for few items (optional)
if lenn < 10:
pivot, mid = 0, lenn//2
while True:
middle = nums[mid]
if middle > nums[mid+1]:
return mid+1
elif middle < nums[mid-1]:
return mid
elif middle > last:
mid += 1
elif middle < last:
mid -= 1
# Binary search for many items
start, end = 0, lenn
while start+1 < end:
mid = (start + end) // 2
midVal = nums[mid]
if midVal < nums[start]:
end = mid
elif midVal > nums[end]:
start = mid
else:
assert False
return start+1
Below I've attached a homework problem. So far my only code is.
def myRange(start, stop=None, step=None):
if stop == None:
stop = start
if step == None:
step = 1
while True:
if step > 0 and start >= stop:
break
elif step < 0 and start <= stop:
break
yield ('%g' % start)
start = start + step
break
lyst = myRange(start, stop=None, step=None)
for num in lyst:
print(num)
myRange(0, 10, 2)
I'm trying to get rid of the yeild and the '%g' statement and replace it with a return statement if anyone has any idea about how to do that. As well as that I am trying to make it so this truly works like a function and you can pass variables into it and get the range as a list, but I'm not sure how to do that either. Any help would be appreciated. I am very lost on this one.
Define and test a function myRange. This function should behave like
Python’s standard range function, with the required and optional
arguments, but it should return a list. Do not use the range function
in your implementation! (Hints: Study Python’s help on range to
determine the names, positions, and what to do with your function’s
parameters. Use a default value of None for the two optional
parameters. If these parameters both equal None, then the function has
been called with just the stop value. If just the third parameter
equals None, then the function has been called with a start value as
well. Thus, the first part of the function’s code establishes what the
values of the parameters are or should be. The rest of the code uses
those values to build a list by counting up or down.)
myRange(start=None, stop=10, step=None):
Dealing with stop=10
You set a default value of stop = 10. However, you created were asked to mimic the built-in
range function. The built in range does not have default stop=10.
So, do not set a default value in your version if
there is no default value in the original
Minimum number of args needed
Your myRange has 3 default arguments. Have you have ever seen the following code?
for x in range():
print(x)
Hint: range requires at least one argument.
Default value for start
10 is the stop. What is the first number printed by the following code? Is it 0? Is it 1? 2? 5? 10? What is it?
for x in range(10):
print(x)
Put the print outside of myRange
The indented block of code should not contain any print statements
def myRange(start=None, stop=10, step=None):
lyst = (list(range(stop)))
# print (lyst) Noooo!
lyst = myRange()
print(lyst) # YES!
You need a return statement
If a function has output we write return output You're supposed to return a list
This function should behave like Python’s standard range function, with the required and optional arguments, but it should return a list.
def myRange(start=None, stop=10, step=None):
lyst = list(range(stop))
# print (lyst) Noooo!
return lyst # New feature! return the output:)
lyst = myRange()
print(lyst) # YES!
Edit
I have decided to give you a function signature. You will still have to fill in the body. Don't worry about most of the code, leave it alone. Simply write your code at the very end, replacing the print("start:", start, "stop:", stop, "step:", step)
is_none = lambda x:\
x == None
count_non_none = lambda args, *, is_none=is_none: {\
sum(1 for _ in filter(is_none, args))
}
def myRange(_a1, _a2=None, _a3=None, *, start=None, stop=None, step=None):
args_non_none = count_non_none((_a1, _a2, _a3))
kwargs_non_none = count_non_none((start, stop, step))
total_non_none = args_non_none + kwargs_non_none
if total_non_none == 1:
# the one arg represents `stop`
assert(not start)
assert(not step)
stop = stop if stop else _a1
elif total_non_none == 2:
if args_non_none == 2:
start = _a1
stop = _a2
else # at least one keyword argument
assert(not (start and step))
L = [_a1, _a2, _a3]
while len(L) > 0:
if not start:
start = L.pop(0)
elif not stop:
stop = L.pop(0)
elif not step:
step = L.pop(0)
elif total_non_none == 3:
L = [_a1, _a2, _a3]
while len(L) > 0:
if not start:
start = L.pop(0)
elif not stop:
stop = L.pop(0)
elif not step:
step = L.pop(0)
else:
raise TypeError("more than 3 arguments were passed in")
if start == None:
start = 0
if step == None:
step = 1
print("start:", start, "stop:", stop, "step:", step)
Python allows you to accept any number of arguments and keyword arguments like this:
def myRange(*args, **kwargs):
# do something
args will be a tuple, and kwargs will be a dictionary.
For example, calling myRange(21, 'foo', name='shado') will have args be (21, 'foo') and kwargs be {'name': 'shado'}.
args and kwargs don't technically have to be args and kwargs but it is convention to name them that.
using this I imagine you can construct some conditionals based on the length of args
def myRange(*args):
if len(args) == 1:
start, end, step = 0, args[0], 1
# conditionals for other cases
another way to do it would be like this:
def myRange(start, stop=None, step=None):
if stop == None and step == None:
start, stop, step = 0, start, 1
# handle the other conditions
basically just handling every case separately
For making the list I would use a while loop.
Defined range w/o using a range function.
def myRange(*args):
myList=[]
if len(args) is 1:
k=0
if args[0] > 0:
while k < args[0]:
myList.append(k)
k=k+1
else:
while k > args[0]:
myList.append(k)
k=k-1
elif len(args) is 2:
k = args[0]
if args[0] < args[1]:
while k < args[1]:
myList.append(k)
k=k+1
else:
while k > args[1]:
myList.append(k)
k=k-1
else:
k=args[0]
if args[0] < args[1]:
while k < args[1]:
myList.append(k)
k=k+args[2]
else:
while k > args[1]:
myList.append(k)
k=k+args[2]
return myList
def myRange(start, step=None, stop=None):
if step == None and stop == None:
start, step, stop = 0, 1, start
while start < stop:
yield start
start += step
elif stop == None:
start, step, stop = start, 1, step
while start < stop:
yield start
start += step
while start < stop:
yield start
start += step
for i in myRange(1, 10, 100):
print(i)
or you can also implement with args too.
def custome_range(*args):
if len(args) == 1:
start, step, stop = 0, 1, args[0]
while start < stop:
yield start
start += step
elif len(args) == 2:
start, step, stop = args[0], 1, args[1]
while start < stop:
yield start
start += step
elif len(args) == 3:
start, step, stop = args[0], args[1], args[2]
while start < stop:
yield start
start += step
for i in custome_range(1, 10, 100):
print(i)
You can write this exclusively through if statements to determine which arguments are given when the function was called, followed by while statements to deliver the numbers in the range_list according to each parameter.
def myRange(a, b = None, step = None):
range_list = []
if b == None:
b = 0
if step == None:
while a > b:
range_list.append(b)
b += 1
while a < b:
range_list.append(a)
a += 1
elif step > 0:
while a < b:
range_list.append(a)
a += step
elif step < 0:
while a > b:
range_list.append(a)
a += step
else:
range_list = [a]
return range_list
I was reading the Binary Search Template II in leetcode:
It is used to search for an element or condition which requires accessing the current index and its immediate right neighbor's index in the array.
def binarySearch(nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if len(nums) == 0:
return -1
left, right = 0, len(nums)
while left < right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid
# Post-processing:
# End Condition: left == right
if left != len(nums) and nums[left] == target:
return left
return -1
I seems to me that the extra condition and nums[left] == target is unnecessary.
When changing:
if left != len(nums) and nums[left] == target:
to just:
if left != len(nums):
...it works perfectly:
def binarySearch(nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if len(nums) == 0:
return -1
left, right = 0, len(nums)
while left < right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid
# Post-processing:
# End Condition: left == right
if left != len(nums):
return left
return -1
Tests:
In [4]: nums = list(range(100))
In [5]: binarySearch(nums, 55)
Out[5]: 55
In [6]: binarySearch(nums, 101)
Out[6]: -1
In [7]: binarySearch(nums, 38)
Out[7]: 38
What's the reason nums[left] == target should be added?
Leetcode's summary on the template (for reference if you could not open its link):
Key Attributes:
An advanced way to implement Binary Search.
Search Condition needs to access element's immediate right neighbor
Use element's right neighbor to determine if condition is met and decide whether to go left or right
Gurantees [sic] Search Space is at least 2 in size at each step
Post-processing required. Loop/Recursion ends when you have 1 element left. Need to assess if the remaining element meets the
condition.
Distinguishing Syntax:
Initial Condition: left = 0, right = length
Termination: left == right
Searching Left: right = mid
Searching Right: left = mid+1
In contrast with canonical version of binary search where the loop terminates as soon as lo > hi is met, in this case the loop terminates when lo == hi. But since the element nums[lo] (which is also nums[hi]) has to be inspected as well, the additional check is added after the loop.
It is guaranteed that the loop terminates only and only when lo == hi, because moving left includes mid element in the future search (else: right = mid) while in canonical version, mid element is excluded from the future search in both cases
Test with nums = [9] and binarySearch(nums, 2). Assuming I understand the code properly, binary_search would return 0 as the "index" of the found item.
You need the last condition to ensure that you actually found the target element.
Otherwise, you're just finding a index for the item closest to the target in the array.
One more example:
IN nums = [1, 3, 4]
IN binary_search(nums, 2)
OUT 2 /* When 2 isn't in the array */
First of all, when you change if left != len(nums) and nums[left] == target: to if left != len(nums):
it seems to work fine because your tests are being made with nums = list(range(100)) which generates the integer sequence 0, 1, 2, ..., 99, as such, every index of nums is the same as its element, that is nums[i] == i. Without nums[left] == target you're actually returning the closest index to the index of your target element, and since with your test every index coincides with its element then it appears to give correct results but it's wrong. The nums[left] == target is the terminating condition that ensures that you actually found your target element in nums.
I am trying to build a function that takes in a list and returns a tuple of (min, max).
For example,
[2,1,4,9,4.5]
would return
(1, 9)
I am trying to use only recursion and want to perform this task without using other things that would make this very easy (such as min(),max(),sort(),sorted(),loop..etc)
So far, I have been able to create function that find maximum
def findmax(alist):
if len(alist) <= 1:
return tuple(alist)
elif len(alist) == 2:
if alist[0] >= alist[1]:
return findmax([alist[0]])
elif alist[0] <= alist[1]:
return findmax([alist[1]])
elif len(alist) > 2:
if alist[0] >= alist[1]:
return findmax([alist[0]] + alist[2:])
elif alist[0] <= alist[1]:
return findmax(alist[1:])
which
findmax([2,1,4,9,4.5])
returns
(9,)
and a function that find minimum (which is not too different)
def findmin(alist):
if len(alist) <= 1:
return tuple(alist)
elif len(alist) == 2:
if alist[0] >= alist[1]:
return findmin([alist[1]])
elif alist[0] <= alist[1]:
return findmin([alist[0]])
elif len(alist) > 2:
if alist[0] >= alist[1]:
return findmin(alist[1:])
elif alist[0] <= alist[1]:
return findmin([alist[0]] + alist[2:])
which
findmin([2,1,4,9,4.5])
returns
(1,)
Is there a way to put this two separate functions into one using only recursion so that it would return a desired result of
(1, 9)
Any help would really be appreciated.
I find that these sorts of problems tend to be simpler than you expect. Let the recursion do the work:
def find_min_max(a_list):
if a_list:
head, *tail = a_list
if tail:
minimum, maximum = find_min_max(tail)
return [head, minimum][minimum < head], [head, maximum][maximum > head]
return head, head
return a_list
USAGE
>>> find_min_max([2, 1, 4, 9, 4.5])
(1, 9)
>>> find_min_max('elephant')
('a', 't')
>>>
This solution is Python 3 specific but can be easily modified for Python 2 & 3 compatibility.
Below, minmax is expressed using continuation-passing style. In this style, it's as if our state variables are pulled out of the aether. For additional examples of other programs written using this style, please see this answer.
from math import inf
def first (xs):
return xs[0]
def rest (xs):
return xs[1:]
def tuple2 (a, b):
return (a, b)
def minmax (xs = [], then = tuple2):
if not xs: # base case: no `x`
return then (inf, -inf)
else: # inductive case: at least one `x`
return minmax \
( rest(xs)
, lambda a, b:
then \
( min (a, first (xs))
, max (b, first (xs))
)
)
print (minmax ([ 2, 1, 4, 9, 4.5 ]))
# (1, 9)
print (minmax ([]))
# (inf, -inf)
min and max are defined as
def min (a, b)
if a < b:
return a
else:
return b
def max (a, b)
if a > b:
return a
else:
return b
To find either max or min separately is easy. What is difficult is to find both max and min through recursive calls. Tail recursion is exactly for this (maintain and update status of variables through recursive calls) and is usually straightforward to write:
def findminmax(L):
def inner(L1, min, max):
if L1 == []:
return (min, max)
elif L1[0] > max:
return inner(L1[1:], min, L1[0])
elif L1[0] < min:
return inner(L1[1:], L1[0], max)
else:
return inner(L1[1:], min, max)
return inner(L[1:], L[0], L[0])
findminmax([2,1,4,9,4.5])
# => (1, 9)
No need for assignment and fancy list indexing. Only the most basic list operation is needed. The recursion structure is clear and very standard (obviously see base case, reduction and function recursive call) and the code is also very readable as plain English.
Update
A little modification to handle the string input and empty list or string input:
def findminmax(LS):
def inner(LS1, min, max):
if not LS1:
return (min, max)
elif LS1[0] > max:
return inner(LS1[1:], min, LS1[0])
elif LS1[0] < min:
return inner(LS1[1:], LS1[0], max)
else:
return inner(LS1[1:], min, max)
try:
return inner(LS[1:], LS[0], LS[0])
except IndexError:
print("Oops! That was no valid input. Try again...")
findminmax([2,1,4,9,4.5])
# => (1, 9)
findminmax([2])
# => (2, 2)
findminmax('txwwadga')
# => ('a', 'x')
findminmax('t')
# => ('t', 't')
findminmax([]) # empty list
# => Oops! That was no valid input. Try again...
findminmax('') # empty string
# => Oops! That was no valid input. Try again...
You can add another def (read comments):
def f(l):
return findmin(l)+findmax(l) # Also you can do: `(findmin(l)[0],findmax(l)[0])`
Now to call it, do:
print(f([2,1,4,9,4.5]))
Output would be:
(1, 9)
You're definitely over-complicating the recursive function. Both minimum and maximum can be returned in a tuple with the following recursive code.
my_list = [2,1,4,9,4.5]
def recursive_min_max(list_a, pos, biggest, smallest):
if pos != len(list_a) - 1:
biggest_new = list_a[pos] if biggest == None else list_a[pos] if list_a[pos] > biggest else biggest
smallest_new = list_a[pos] if smallest == None else list_a[pos] if list_a[pos] < smallest else smallest
return recursive_min_max(list_a, pos + 1, biggest_new, smallest_new)
return (biggest,smallest)
print(recursive_min_max(my_list, 0, None, None))
At each step, the current list item is being compared to the current biggest and smallest elements. If they are bigger/smaller, the current value replaces them.
I am trying to implement the binary search in python and have written it as follows. However, I can't make it stop whenever needle_element is larger than the largest element in the array.
Can you help? Thanks.
def binary_search(array, needle_element):
mid = (len(array)) / 2
if not len(array):
raise "Error"
if needle_element == array[mid]:
return mid
elif needle_element > array[mid]:
return mid + binary_search(array[mid:],needle_element)
elif needle_element < array[mid]:
return binary_search(array[:mid],needle_element)
else:
raise "Error"
It would be much better to work with a lower and upper indexes as Lasse V. Karlsen was suggesting in a comment to the question.
This would be the code:
def binary_search(array, target):
lower = 0
upper = len(array)
while lower < upper: # use < instead of <=
x = lower + (upper - lower) // 2
val = array[x]
if target == val:
return x
elif target > val:
if lower == x: # these two are the actual lines
break # you're looking for
lower = x
elif target < val:
upper = x
lower < upper will stop once you have reached the smaller number (from the left side)
if lower == x: break will stop once you've reached the higher number (from the right side)
Example:
>>> binary_search([1,5,8,10], 5) # return 1
1
>>> binary_search([1,5,8,10], 0) # return None
>>> binary_search([1,5,8,10], 15) # return None
Why not use the bisect module? It should do the job you need---less code for you to maintain and test.
array[mid:] creates a new sub-copy everytime you call it = slow. Also you use recursion, which in Python is slow, too.
Try this:
def binarysearch(sequence, value):
lo, hi = 0, len(sequence) - 1
while lo <= hi:
mid = (lo + hi) // 2
if sequence[mid] < value:
lo = mid + 1
elif value < sequence[mid]:
hi = mid - 1
else:
return mid
return None
In the case that needle_element > array[mid], you currently pass array[mid:] to the recursive call. But you know that array[mid] is too small, so you can pass array[mid+1:] instead (and adjust the returned index accordingly).
If the needle is larger than all the elements in the array, doing it this way will eventually give you an empty array, and an error will be raised as expected.
Note: Creating a sub-array each time will result in bad performance for large arrays. It's better to pass in the bounds of the array instead.
You can improve your algorithm as the others suggested, but let's first look at why it doesn't work:
You're getting stuck in a loop because if needle_element > array[mid], you're including element mid in the bisected array you search next. So if needle is not in the array, you'll eventually be searching an array of length one forever. Pass array[mid+1:] instead (it's legal even if mid+1 is not a valid index), and you'll eventually call your function with an array of length zero. So len(array) == 0 means "not found", not an error. Handle it appropriately.
This is a tail recursive solution, I think this is cleaner than copying partial arrays and then keeping track of the indexes for returning:
def binarySearch(elem, arr):
# return the index at which elem lies, or return false
# if elem is not found
# pre: array must be sorted
return binarySearchHelper(elem, arr, 0, len(arr) - 1)
def binarySearchHelper(elem, arr, start, end):
if start > end:
return False
mid = (start + end)//2
if arr[mid] == elem:
return mid
elif arr[mid] > elem:
# recurse to the left of mid
return binarySearchHelper(elem, arr, start, mid - 1)
else:
# recurse to the right of mid
return binarySearchHelper(elem, arr, mid + 1, end)
def binary_search(array, target):
low = 0
mid = len(array) / 2
upper = len(array)
if len(array) == 1:
if array[0] == target:
print target
return array[0]
else:
return False
if target == array[mid]:
print array[mid]
return mid
else:
if mid > low:
arrayl = array[0:mid]
binary_search(arrayl, target)
if upper > mid:
arrayu = array[mid:len(array)]
binary_search(arrayu, target)
if __name__ == "__main__":
a = [3,2,9,8,4,1,9,6,5,9,7]
binary_search(a,9)
Using Recursion:
def binarySearch(arr,item):
c = len(arr)//2
if item > arr[c]:
ans = binarySearch(arr[c+1:],item)
if ans:
return binarySearch(arr[c+1],item)+c+1
elif item < arr[c]:
return binarySearch(arr[:c],item)
else:
return c
binarySearch([1,5,8,10,20,50,60],10)
All the answers above are true , but I think it would help to share my code
def binary_search(number):
numbers_list = range(20, 100)
i = 0
j = len(numbers_list)
while i < j:
middle = int((i + j) / 2)
if number > numbers_list[middle]:
i = middle + 1
else:
j = middle
return 'the index is '+str(i)
If you're doing a binary search, I'm guessing the array is sorted. If that is true you should be able to compare the last element in the array to the needle_element. As octopus says, this can be done before the search begins.
You can just check to see that needle_element is in the bounds of the array before starting at all. This will make it more efficient also, since you won't have to do several steps to get to the end.
if needle_element < array[0] or needle_element > array[-1]:
# do something, raise error perhaps?
It returns the index of key in array by using recursive.
round() is a function convert float to integer and make code fast and goes to expected case[O(logn)].
A=[1,2,3,4,5,6,7,8,9,10]
low = 0
hi = len(A)
v=3
def BS(A,low,hi,v):
mid = round((hi+low)/2.0)
if v == mid:
print ("You have found dude!" + " " + "Index of v is ", A.index(v))
elif v < mid:
print ("Item is smaller than mid")
hi = mid-1
BS(A,low,hi,v)
else :
print ("Item is greater than mid")
low = mid + 1
BS(A,low,hi,v)
BS(A,low,hi,v)
Without the lower/upper indexes this should also do:
def exists_element(element, array):
if not array:
yield False
mid = len(array) // 2
if element == array[mid]:
yield True
elif element < array[mid]:
yield from exists_element(element, array[:mid])
else:
yield from exists_element(element, array[mid + 1:])
Returning a boolean if the value is in the list.
Capture the first and last index of the list, loop and divide the list capturing the mid value.
In each loop will do the same, then compare if value input is equal to mid value.
def binarySearch(array, value):
array = sorted(array)
first = 0
last = len(array) - 1
while first <= last:
midIndex = (first + last) // 2
midValue = array[midIndex]
if value == midValue:
return True
if value < midValue:
last = midIndex - 1
if value > midValue:
first = midIndex + 1
return False