Hi I am doing DSA problems and found a problem called as ceiling of the element in sorted array. In this problem there is a sorted array and if the target element is present in the sorted array return the target. If the target element is not found in the sorted array we need to return the smallest element which is greater than target. I have written the code and also done some test cases but need to check if everything works correctly. This problem is not there on leetcode where I could run it with many different cases. Need suggestion/feedback if the problem is solved in the correct way and if it would give correct results in all cases
class Solution:
#My approch
def smallestNumberGreaterThanTarget(self, nums, target):
start = 0
end = len(nums)-1
if target > nums[end]:
return -1
while start <= end:
mid = start + (end-start)//2
if nums[mid] == target:
return nums[mid]
elif nums[mid] < target:
if nums[mid+1] >= target:
return nums[mid+1]
start = mid + 1
else:
end = mid-1
return nums[start]
IMO, the problem can be solved in a simpler way, with only one test inside the main loop. The figure below shows a partition of the real line, in subsets associated to the values in the array.
First, we notice that for all values above the largest, there is no corresponding element, and we will handle this case separately.
Now we have exactly N subsets left, and we can find the right one by a dichotomic search among these subsets.
if target > nums[len(nums)-1]:
return None
s, e= 0, len(nums);
while e > s:
m= e + ((s - e) >> 1);
if target > nums[m]:
s= m+1
else:
e= m
return s
We can formally prove the algorithm using the invariant nums[s-1] < target <= nums[e], with the fictional convention nums[-1] = -∞. In the end, we have the bracketing nums[s-1] < target <= nums[s].
The code errors out with an index out-of-range error for the empty list (though this may not be necessary because you haven't specified the problem constraints).
A simple if guard at the top of the function can fix this:
if not nums:
return -1
Otherwise, it seems fine to me. But if you're still not sure whether or not your algorithm works, you can always do random testing (e.g. create a linear search version of the algorithm and then randomly generate inputs to both algorithms, and then see if there's any difference).
Here's a one-liner that you can test against:
input_list = [0, 1, 2, 3, 4]
target = 0
print(next((num for num in input_list if num >= target), -1))
Related
I was going through LeetCode problem 34. Find First and Last Position of Element in Sorted Array, which says:
Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value.
If target is not found in the array, return [-1, -1].
You must write an algorithm with O(log n) runtime complexity.
Since the question wanted logn run-time, I implemented the binary-search logic. But I am not sure, and think that, with the extra-while loop inside the base condition, I actually go to O(n) in the worst case. Is that true?
class Solution(object):
def searchRange(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
left = 0
right = len(nums) - 1
pos = [-1,-1]
while left <= right:
middle = (left + right) // 2
"""
This is pure binary search until we hit the target. Once
we have hit the target, we expand towards left and right
until we find the number equal to the target.
"""
if nums[middle] == target:
rIndex = middle
while rIndex + 1 < len(nums) and nums[rIndex + 1] == target:
rIndex += 1
pos[1] = rIndex
lIndex = middle
while lIndex - 1 >= 0 and nums[lIndex - 1] == target:
lIndex -= 1
pos[0] = lIndex
break
elif target > nums[middle]:
left = middle + 1
else:
right = middle - 1
return pos
Here is what I think for an example array that looks like:
input = [8,8,8,8,8,8,8] , target = 8
When the base condition nums[middle] == target hits, I will need to iterate the complete array and this makes it run-time complexity as O(n), right?
Interestingly, this solution is faster than 95% of the submissions!! But I think there is some issue with LeetCode!!!
Yes, you are right, the loop degrades the worst case time complexity. You rightly identified what happens when the input array has only duplicates of the target value, and no other value.
The solution is to perform two binary searches: one that prefers going to the left side, and one that prefers to go to the right side of the target value.
If the test cases do not thoroughly test this O(n) behaviour, this O(n) solution will not come out as a bad one.
Could anyone explain exactly what's happening under the hood to make the recursive approach in the following problem much faster and efficient in terms of time complexity?
The problem: Write a program that would take an array of integers as input and return the largest three numbers sorted in an array, without sorting the original (input) array.
For example:
Input: [22, 5, 3, 1, 8, 2]
Output: [5, 8, 22]
Even though we can simply sort the original array and return the last three elements, that would take at least O(nlog(n)) time as the fastest sorting algorithm would do just that. So the challenge is to perform better and complete the task in O(n) time.
So I was able to come up with a recursive solution:
def findThreeLargestNumbers(array, largest=[]):
if len(largest) == 3:
return largest
max = array[0]
for i in array:
if i > max:
max = i
array.remove(max)
largest.insert(0, max)
return findThreeLargestNumbers(array, largest)
In which I kept finding the largest number, removing it from the original array, appending it to my empty array, and recursively calling the function again until there are three elements in my array.
However, when I looked at the suggested iterative method, I composed this code:
def findThreeLargestNumbers(array):
sortedLargest = [None, None, None]
for num in array:
check(num, sortedLargest)
return sortedLargest
def check(num, sortedLargest):
for i in reversed(range(len(sortedLargest))):
if sortedLargest[i] is None:
sortedLargest[i] = num
return
if num > sortedLargest[i]:
shift(sortedLargest, i, num)
return
def shift(array, idx, element):
if idx == 0:
array[0] = element
return array
array[0] = array[1]
array[idx-1] = array[idx]
array[idx] = element
return array
Both codes passed successfully all the tests and I was convinced that the iterative approach is faster (even though not as clean..). However, I imported the time module and put the codes to the test by providing an array of one million random integers and calculating how long each solution would take to return back the sorted array of the largest three numbers.
The recursive approach was way much faster (about 9 times faster) than the iterative approach!
Why is that? Even though the recursive approach is traversing the huge array three times and, on top of that, every time it removes an element (which takes O(n) time as all other 999 elements would need to be shifted in the memory), whereas the iterative approach is traversing the input array only once and yes making some operations at every iteration but with a very negligible array of size 3 that wouldn't even take time at all!
I really want to be able to judge and pick the most efficient algorithm for any given problem so any explanation would tremendously help.
Advice for optimization.
Avoid function calls. Avoid creating temporary garbage. Avoid extra comparisons. Have logic that looks at elements as little as possible. Walk through how your code works by hand and look at how many steps it takes.
Your recursive code makes only 3 function calls, and as pointed out elsewhere does an average of 1.5 comparisons per call. (1 while looking for the min, 0.5 while figuring out where to remove the element.)
Your iterative code makes lots of comparisons per element, calls excess functions, and makes calls to things like sorted that create/destroy junk.
Now compare with this iterative solution:
def find_largest(array, limit=3):
if len(array) <= limit:
# Special logic not needed.
return sorted(array)
else:
# Initialize the answer to values that will be replaced.
min_val = min(array[0:limit])
answer = [min_val for _ in range(limit)]
# Now scan for smallest.
for i in array:
if answer[0] < i:
# Sift elements down until we find the right spot.
j = 1
while j < limit and answer[j] < i:
answer[j-1] = answer[j]
j = j+1
# Now insert.
answer[j-1] = i
return answer
There are no function calls. It is possible that you can make up to 6 comparisons per element (verify that answer[0] < i, verify that (j=1) < 3, verify that answer[1] < i, verify that (j=2) < 3, verify that answer[2] < i, then find that (j=3) < 3 is not true). You will hit that worst case if array is sorted. But most of the time you only do the first comparison then move to the next element. No muss, no fuss.
How does it benchmark?
Note that if you wanted the smallest 100 elements, then you'd find it worthwhile to use a smarter data structure such as a heap to avoid the bubble sort.
I am not really confortable with python, but I have a different approach to the problem for what it's worth.
As far as I saw, all solutions posted are O(NM) where N is the length of the array and M the length of the largest elements array.
Because of your specific situation whereN >> M you could say it's O(N), but the longest the inputs the more it will be O(NM)
I agree with #zvone that it seems you have more steps in the iterative solution, which sounds like an valid explanation to your different computing speeds.
Back to my proposal, implements binary search O(N*logM) with recursion:
import math
def binarySearch(arr, target, origin = 0):
"""
Recursive binary search
Args:
arr (list): List of numbers to search in
target (int): Number to search with
Returns:
int: index + 1 from inmmediate lower element to target in arr or -1 if already present or lower than the lowest in arr
"""
half = math.floor((len(arr) - 1) / 2);
if target > arr[-1]:
return origin + len(arr)
if len(arr) == 1 or target < arr[0]:
return -1
if arr[half] < target and arr[half+1] > target:
return origin + half + 1
if arr[half] == target or arr[half+1] == target:
return -1
if arr[half] < target:
return binarySearch(arr[half:], target, origin + half)
if arr[half] > target:
return binarySearch(arr[:half + 1], target, origin)
def findLargestNumbers(array, limit = 3, result = []):
"""
Recursive linear search of the largest values in an array
Args:
array (list): Array of numbers to search in
limit (int): Length of array returned. Default: 3
Returns:
list: Array of max values with length as limit
"""
if len(result) == 0:
result = [float('-inf')] * limit
if len(array) < 1:
return result
val = array[-1]
foundIndex = binarySearch(result, val)
if foundIndex != -1:
result.insert(foundIndex, val)
return findLargestNumbers(array[:-1],limit, result[1:])
return findLargestNumbers(array[:-1], limit,result)
It is quite flexible and might be inspiration for a more elaborated answer.
The recursive solution
The recursive function goes through the list 3 times to fins the largest number and removes the largest number from the list 3 times.
for i in array:
if i > max:
...
and
array.remove(max)
So, you have 3×N comparisons, plus 3x removal. I guess the removal is optimized in C, but there is again about 3×(N/2) comparisons to find the item to be removed.
So, a total of approximately 4.5 × N comparisons.
The other solution
The other solution goes through the list only once, but each time it compares to the three elements in sortedLargest:
for i in reversed(range(len(sortedLargest))):
...
and almost each time it sorts the sortedLargest with these three assignments:
array[0] = array[1]
array[idx-1] = array[idx]
array[idx] = element
So, you are N times:
calling check
creating and reversing a range(3)
accessing sortedLargest[i]
comparing num > sortedLargest[i]
calling shift
comparing idx == 0
and about 2×N/3 times doing:
array[0] = array[1]
array[idx-1] = array[idx]
array[idx] = element
and N/3 times array[0] = element
It is difficult to count, but that is much more than 4.5×N comparisons.
I would like to 1)simplify the code below using iteration 2)implement it using recursion
This code features an equation similar to the Fibonacci series the difference being the previous answer is multiplied by a function in this case just 2 by index.
The algorithm will input different images and calculate the total incremented points, the flattened optimal matrix will give the highest value.
Note that list_c should give the highest value.
sum_list=[]
list_a= [1,0,1,0,1]
list_b= [1,0,0,1,1]
list_c= [1,1,1,0,0]
def increment(input_list):
global i #i added it because i received an error
""" returns
Incrementing_answer = previous_answer + (list[i+1])
for which previous_answer begins with list[0]
if list[0] =0 then list[0]=-1
for example, list_a should be evaluated as follows
- ans = 1+2*(1)
= 3
- ans = 3+ 2*(0) --since since its 0 ,-1 is replaced
= 3+ 2*(-1)
= 1
- ans = 1+2(1)
=3
and so on
Incrementing_answers = sum_list=[3,1,3,1,3] =11
"""
for i in range(0,len(input_list)):
if input_list[i] == 0 :
input_list[i] == -1
ans = input_list[i]+2*input_list[i]
sum_list.append(ans)
else:
ans = ans+input_list[i]
sum_list.append(ans)
return sum(sum_list)
Previous answers have been helpful, the code above does not work
1)I would like corrections
2)Is it possible to solve the same problem using recursion
3) I also just realised the code does not work well for large arrays(preprocesed_images)
4) for lists or arrays that include floating points I get the error ('
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()')
3)Feedback on using good programming practices
4) Any additional advice on how to tackle the problem is Welcome, the inputted images will be captured using cv2 and the algorithm has to pick optimal images in real-time, to solve the puzzle.
Thanks very much in advance
Global variables are discouraged over passing arguments as parameters to function
Use of PEP 8 naming conventions is encouraged (i.e. name function increment rather than Increment)
Single letter variable names are discouraged (i.e. no idea of what a, l, p represents) (adopting Dan's comment below).
Code
def increment(a, l, p):
"""
Returns in an incrementing value ,using the formula a(n)=a(n)+l(i+1)
were a(n) starts with the starts with l[0] and then l(i+1)
"""
for i in range(len(l)):
if l[i] == 0: # equation
a += -2 # no change to l
else:
a += 2*l[i]
p.append(a)
return sum(p)
Usage
l = [1,0,1,0,1]
p=[]
a = 0
print(f'Increment: {increment(a, l, p)}')
print(f'l unchanged: {l}')
print(f'Updated p: {p}')
a = p[-1] # a is last p
print(f'Updated a: {a}')
Output
Increment: 6
l unchanged: [1, 0, 1, 0, 1]
Updated p: [2, 0, 2, 0, 2]
Updated a: 2
a doesn't need to be a global and p & l should be passed as argumets -in your version you tied your implementation of your function to the implementation of the calling code - a better implementation would be :
I don't fully understand why you need a at all, but I think this code does what you need :
def Increment( initial, results ):
"""
Returns in an incrementing value ,using the formula a(n)=a(n)+l(i+1)
were a(n) starts with the starts with l[0] and then l(i+1)
,if l[i]= 0,-1 is calculated instead.
"""
last = results[-1] if results else 0
for index, value in enumerate(initial):
term = -1 if value == 0 else value
last += 2* term
results.append(last)
return sum(results)
l = [1,0,1,0,1]
p=[]
r = Increment(initial=l, results=p)
print(r)
If you do need the a value outside the function it will just be p[-1]
I think the above code replicates the functionality, without changing your l list (which you indicated you didn't need.
I am working on a hard but stupid bisect search problem and debugging for hours.
Find Minimum in Rotated Sorted Array II
Find Minimum in Rotated Sorted Array II
Hard
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).
Find the minimum element.
The array may contain duplicates.
Example 1:
Input: [1,3,5]
Output: 1
Example 2:
Input: [2,2,2,0,1]
Output: 0
Note:
This is a follow up problem to Find Minimum in Rotated Sorted Array.
Would allow duplicates affect the run-time complexity? How and why?
The widely accepted answer takes O(n) time,
class SolutionK:
def findMin(self, nums):
lo, hi = 0, len(nums)-1
while lo < hi:
mid = (hi +lo) // 2
if nums[mid] > nums[hi]:
lo = mid + 1
elif nums[mid] < nums[hi]:
hi = mid
else:
hi -= 1
return nums[lo]
# why not min(nums) or brute force
I think the problem might be solved by a recycled array.
Since there are duplicates, we can find the rightmost max, then max + 1 is the minimal.
#the mid
lo = 0
hi = len(nums)
mid = (lo+hi) // 2
mid = mid % len(nums)
and the terminating condition
if nums[mid-1] <= nums[mid] > nums[mid+1]: return mid as the peak.
Unfortunately I cannot design the decreasing conditions.
Could you please give some hints?
You can indeed use bisection. In case the array consists of only unique numbers and has been rotated either the leftmost or the rightmost value will be out of order with respect to the middle point. That is array[0] <= array[len(array) // 2] <= array[-1] will be False. On the other hand this condition may hold if:
the array is not rotated at all,
or there are duplicates such as [1, 1, 1, 1, 2] => (rotate left 1) [1, 1, 1, 2, 1].
So we can separately check the left and right part of the condition (array[0] and array[-1] respectively) and in case one of them is invalidated check the corresponding sub-array (the left and right sub-array respectively). In case neither condition is invalidated we need to check both sides and compare.
The following is an example implementation (it only uses min where there are less than three elements involved, i.e. a simple comparison could be made as well):
def minimum(array):
if len(array) <= 2:
return min(array)
midpoint = len(array) // 2
if array[0] > array[midpoint]:
return minimum(array[:midpoint+1])
elif array[midpoint] > array[-1]:
return minimum(array[midpoint+1:])
else: # Possibly dealing with duplicates.
return min(minimum(array[:midpoint]),
minimum(array[midpoint:]))
from collections import deque
from random import randint, choices
for test in range(1000):
l = randint(10, 100)
array = deque(sorted(choices(list(range(l // 2)), k=l)))
array.rotate(randint(-len(array), len(array)))
array = list(array)
assert min(array) == minimum(array)
I want to achieve it in O(log n) complexity.using the idea of binary search it can be achieved . I put it this way: let L be a list ;L= [12,10,9,7,6,5,8,9,11] so the expected outcome should be 5. Is there a simple algorithm in python to do it?
def binse(l,lo,hi):
n =len(l)
lo = 0
hi =n
mid =(lo+hi)//2
if (hi-lo)<2:
return lo
if l[mid]<l[mid-1] and l[mid]<l[mid+1]:
return l[mid]
elif l[mid]<l[mid-1] and l[mid]>l[mid+1]:
return binse(l,mid+1,hi)
elif l[mid]>l[mid-1] and l[mid]<l[mid+1]:
return binse(l,lo,mid)
else:
return
l = [13,11,5,6,7,8,9,11,13]
lo =0
hi =len(l)
print(binse(l,lo,hi))
def mini(lis):
s, e = 0, len(lis)-1
while True:
m = (s+e)/2
if lis[m] > lis[m+1]:
s = m
elif lis[m-1] < lis[m]:
e = m+1
else:
return m
This should work.
You said it - use binary search. At each probe, take two successive entries and determine whether the values are increasing or decreasing. Based on that, you know which region to further sub-divide.
BTW, I'm ignoring the case of equality
Update based on posted code:
The first thing binse does is overwrite the lo and hi parameters - bad!
The return lo line is clearly wrong as it is not returning an array value
Need to be a bit careful about some edge cases including getting near the endpoints and also when you have a sequence of equal values
The last "return" is also clearly wrong as it has no value.
The first item is the reason you are seeing the infinite recursion.