I have the following code. It's about as simple as I can make it. Does anyone know of a slick way to turn this recursion into a loop?
The problem is that I can run into a recusion limit. I've thought of some ways to rewrite it, but they're not pretty at all.
My nicest thought at this point is that I could get it into some tail recursion form, but I'm not sure how to do that.
def blackbox(c, i): #This is a different function in production
if i > 5:
return range(0,1)
else:
return range(0,c+i)
def recurse(c, length):
if length == 0:
return [[]]
return [l + [j] for j in blackbox(c, length) for l in recurse(c - j, length - 1)]
Example: recurse(6, 1000) throws an error is way over the recursion limit.
Cool, mostly useless fact: Using range(i, c + 1) for the black box returns all the lists with length length with sum at most c.
EDIT: I'm aware I can memoize the code, but that doesn't fix recursion limit. In this example, memoizing helps the speed a lot, but in my situation it doesn't, so I'm not concerned with it.
EDIT 2: Updated blackbox so the value of recurse(6,1000) is reasonable.
One way can be to use your own stack of generator functions instead:
def blackbox(c, i):
return range(0, c + i) #This code is actually quite different, treat it as a black box
# For testing at the end
def recurse(c, length):
if length == 0:
return [[]]
return [l + [j] for j in blackbox(c, length) for l in recurse(c - j, length - 1)]
# Non-recursive variant following:
gen_stack = []
def gen_driver():
prevResult = None
while gen_stack:
try:
if prevResult is not None:
gen_stack[-1].send(prevResult)
prevResult = None
else:
next(gen_stack[-1])
except StopIteration as si:
prevResult = si.value
del gen_stack[-1]
return prevResult
def nonrecurse(c, length):
if length == 0:
return [[]]
# Unfortunately the concise list comprehension doesn't work
result = []
for j in blackbox(c, length):
gen_stack.append(nonrecurse(c - j, length - 1))
for l in (yield):
result.append(l + [j])
return result
gen_stack.append(nonrecurse(6, 10))
# Testing equality of both variants
print(gen_driver() == recurse(6,10))
# No crash but I didn't wait until it was ready
gen_stack.append(nonrecurse(6, 1000))
Slightly shorter variant but needs more care:
gen_stack = []
def gen_driver():
prevResult = None
while gen_stack:
try:
if prevResult is not None:
gen_stack.append(gen_stack[-1].send(prevResult))
prevResult = None
else:
gen_stack.append(next(gen_stack[-1]))
except StopIteration as si:
prevResult = si.value
del gen_stack[-1]
return prevResult
def single_generator(value):
return value
yield # Mark it as generator function
def nonrecurse(c, length):
if length == 0:
return single_generator([[]])
return [l + [j] for j in blackbox(c, length) for l in (yield nonrecurse(c - j, length - 1))]
gen_stack.append(nonrecurse(6, 10))
# Testing equality of both variants
print(gen_driver() == recurse(6,10))
While in the first variant nonrecurse was a generator function, it is now a usual function returning generators where the list comprehension is a generator on its own.
Related
I'm trying to implement in-place partitioning for quicksort, but am stuck.
This is my code:
def quicksort(b):
print("Passed b is : ",b)
if len(b)<=1 :
return
p=b[0]
i=1
for j in range(0,len(b)):
if b[j]<p:
temp=b[j]
b[j]=b[i]
b[i]=temp
i=i+1
temp2=b[0]
b[0]=b[i-1]
b[i-1]=temp2
print("Parially sorted b is :",b)
print("Left half is :", b[:i-1], " Pivot is ",b[i-1], " Right half is ",b[i:])
print("\n")
quicksort(b[:i-1])
quicksort(b[i:])
b=[3,1,7,2,6,5,9]
quicksort(b)
print(b)
What am I missing?
First of all, please use whitespace around operators so your code is comprehensible. Use black if you aren't sure how to format your code.
As mentioned in the comments, your implementation isn't in-place. An in-place algorithm is one that doesn't use auxiliary space and instead moves the elements around on the existing data structure. Slicing (the [:] syntax) makes a copy of your list, an immediate breach of contract.
You probably got steered off course beginning with your function parameters: def quicksort(b):. This pretty much forces you to slice. The correct parameters for an in-place algorithm are def quicksort(lst, start_index, end_index):, where the indices designate the subsequence of the list that a particular quicksort call should operate on. Each function call must not touch any other portion of the list outside the designated range. The code should set all indices relative to these parameters.
Of course, you don't want to burden the client with having to specify these initial indices correctly, so the normal approach is to use default arguments:
def quicksort(lst, start_index=0, end_index=None):
if end_index is None:
end_index = len(lst)
...
...or a wrapper call:
def quicksort(lst):
def qsort(lst, start_index, end_index):
...
qsort(lst, 0, len(lst))
With that in place, you're ready for the logic, which looks generally OK. After a cleanup and some spot tests, we have:
def quicksort(lst):
def qsort(lst, start_i, end_i):
if end_i - start_i <= 1:
return
pivot = lst[start_i]
i = start_i + 1
for j in range(start_i + 1, end_i):
if lst[j] < pivot:
lst[j], lst[i] = lst[i], lst[j]
i += 1
lst[i-1], lst[start_i] = lst[start_i], lst[i-1]
qsort(lst, start_i, i - 1)
qsort(lst, i, end_i)
qsort(lst, 0, len(lst))
if __name__ == "__main__":
from random import randint
for _ in range(3000):
lst = [randint(-3000, 3000) for _ in range(1000)]
cpy = lst[:]
quicksort(lst)
if sorted(cpy) != lst:
print("FAIL")
I am trying to reverse the array in groups but I am getting this error:
:---- for i in arr: TypeError: 'NoneType' object is not iterable.
What's wrong with my code?
def reverseSubarray(arr,n,k):
if k == 1:
return
i = 0
while i < n:
l = i
r = min(i+k-1, n-1)
while l < r:
temp = arr[l]
arr[l] = arr[r]
arr[r] = temp
l += 1
r -= 1
i += k
return arr
def main():
n = int(input().strip())
string = input().strip().split()
arr=[]
for j in string:
arr.append(int(j.strip()))
k=int(input().strip())
arr = reverseSubarray(arr,n,k)
for i in arr:
print(i,end=' ')
if __name__ == "__main__":
main()
So the problem is that you're actually returning None. This happens because most likely you're giving k=1 so it will go to that line where you return nothing, which will return this error when trying to iterate.
You can treat the problem with a try-catch block on arr=reverseSubarray(arr,n,k) that will return a message like k cannot be 1
You can reverse an array in groups in python as given below,
def reverseSubarray(arr, N, K):
for i in range(0, len(arr),K):
l=arr[i:i+K]
l.reverse()
arr[i:i+K] =l
return arr
While your error was indeed coming from the fact that your function is returning None as other answers have pointed out, you have also written the function in a very non-pythonic style. Here is an example of how you could rewrite it more succintly:
def reverseInGroups(self, arr, N, K):
for i in range(0, N, K):
arr[i:i+K] = reversed(arr[i:i+K])
return arr
range(0, N, K) will return an iterator that goes from 0 to N-1 in steps of K. In other word, i will successively have value: 0, K, 2K, 3K, 4K, etc. until the last multiple of K that is less than N. Here is the documentation for more details.
arr[i:i+K] will refer to the slice of arr between indices i and i+K-1 or, put another way, [arr[i], arr[i+1], arr[i+2], ..., arr[i+K-1]]. It stops at i+K-1 so that you can naturally use arr[i:i+K] and arr[i+K:] without counting arr[i+K] twice.
reversed... reverses an iterator. Here's the doc.
I wanted to write code such that I could find the k-th largest number using quick-sort and wrote the following in LeetCode, which LeetCode will call upon findKthLargest first
class Solution(object):
def partition(self, arr,left,right):
piv = arr[right]
i = left-1
counter = left
while (counter<right):
if (arr[counter]<piv):
i = i+1
tmp = arr[counter]
arr[counter]=arr[i]
arr[i]=tmp
counter = counter+1
temp = arr[i+1]
arr[i+1]=arr[right]
print('pivot '+str(piv)+' at '+str(i+1))
arr[right]=temp
print("at the nmoment "+str(arr))
return (i+1)
def helper(self,arr,left,right,k):
if (left>=right):
return
p = self.partition(arr,left,right)
print("p is now "+str(p))
if (p==len(arr)-k):
return int(arr[p])
self.helper(arr,left,p-1,k)
self.helper(arr,p+1,right,k)
def findKthLargest(self, nums, k):
f= self.helper(nums,0,len(nums)-1,k)
print(f)
I've even printed (arr[p]) INSIDE the helper method and it gave me the correct answer however inside of the method findKthLargest the variable f shows up as a none type and I was wondering where did I go wrong? At the moment I believe that it is returning a none type since inside of the recursive loops when checking if (left>=right) inside of the helper method it returns none
The problem is that your helper function does not always return a value. Only in the base case, where the if condition is true, it will return a numeric value. But it should also return that same number where the corresponding recursive calls are made.
So change:
self.helper(arr,left,p-1,k)
self.helper(arr,p+1,right,k)
to:
result = self.helper(arr,left,p-1,k)
if result is not None:
return result
return self.helper(arr,p+1,right,k)
This way the deepest return value will bubble up the recursion tree, and a success in the first recursive call will avoid that the second recursive call is made.
Hard to debug your code, this'd pass though with less statements:
class Solution:
def findKthLargest(self, nums, k):
def kth_smallest(nums, k):
if nums:
pos = partition(nums, 0, len(nums) - 1)
if k > pos + 1:
return kth_smallest(nums[pos + 1:], k - pos - 1)
elif k < pos + 1:
return kth_smallest(nums[:pos], k)
else:
return nums[pos]
def partition(nums, left, right):
res = left
while left < right:
if nums[left] < nums[right]:
nums[left], nums[res] = nums[res], nums[left]
res += 1
left += 1
nums[res], nums[right] = nums[right], nums[res]
return res
return kth_smallest(nums, len(nums) + 1 - k)
I know there is a O(n) time complexity solution for this problem, here for example.
I am just curious why my naive approach in O(2^n) is not working in Python.
Algorithm:
I am just finding the permutations recursively and when the kth element is added, i am returning it. However I get the return result as None. I am not sure why None is returned by my function.
class Solution(object):
# Time complexity O(2 ^ n)
def getPermutation(self, n, k):
char_list = map(str, range(1, n + 1)) #convert to strin
used = [False] * len(char_list)
result = []
kthArray = self._getPermutation_helper(result, char_list, used, [], k)
print kthArray #kthArray is always None
def _getPermutation_helper(self, result, char_list, used, cur, k):
if len(char_list) == len(cur):
result.append(cur + [])
print len(result)
print cur
if len(result) == k:
print "cur in kth is {0}".format(cur)
return cur #cur is printed correctly but not returned
for i in range(len(char_list)):
if not used[i]:
cur.append(char_list[i])
used[i] = True
self._getPermutation_helper(result, char_list, used, cur, k)
# back track
used[i] = False
cur.remove(char_list[i])
def main():
pgm = Solution()
pgm.getPermutation(3, 6)
if __name__ == "__main__":
main()
Why isn't the correct value returned?
Because you are returning cur to a previous call of the same function, from which you don't return it further down to the first call.
You need to keep propagating the found solution until the first call. For example:
for i in range(len(char_list)):
if not used[i]:
cur.append(char_list[i])
used[i] = True
# Here we do a recursive call, which might find the answer we're looking for.
# So we save its return value and, if it's not None, we return it.
r = self._getPermutation_helper(result, char_list, used, cur, k)
if r is not None:
return r
I am attempting to implement heap sort using the psuedo code from the book Intro to Algorithms. The following is what I have:
def parent(i):
return i/2
def left(i):
return 2*i
def right(i):
return 2*i+1
def max_heapify(seq, i, n):
l = left(i)
r = right(i)
if l <= n and seq[n] > seq[i]:
largest = l
else:
largest = i
if r <= n and seq[r] > seq[largest]:
largest = r
if largest != i:
seq[i], seq[largest] = seq[largest], seq[i]
max_heapify(seq, largest, n)
def heap_length(seq):
return len(seq) - 1
def build_heap(seq):
n = heap_length(seq)
for i in range(n/2,0,-1):
max_heapify(seq, i, n)
def sort(seq):
build_heap(seq)
heap_size = heap_length(seq)
for i in range(heap_size,1,-1):
seq[1], seq[i] = seq[i], seq[1]
heap_size = heap_size - 1
max_heapify(seq, 1, heap_size)
return seq
I am having issues with understanding passing by value or by reference in Python. I have looked at the following question and it seems that I am passing the list by value. My questions is how to return the correctly sorted list either by reference or by value?
arrays are always passed by reference
if you want to pass by value use slice
my_func(my_array[:]) #send copy
my_func(my_array) #array is modified inside and changes are reflected in original