How to speed up recursive call on Python linked list? - python

I have recursive function named min_max; it is passed a linked list; it returns a 2-tuple containing the minimum value followed by the maximum value. If the linked list is empty, then min_max returns (None, None). For example, if we called this function as min_max(list_to_ll([1, 3, 7, 2, 0])) the returned result would be (0, 7).
class LN:
def __init__(self,value,next=None):
self.value = value
self.next = next
def list_to_ll(l):
if l == []:
return None
front = rear = LN(l[0])
for v in l[1:]:
rear.next = LN(v)
rear = rear.next
return front
def min_max(ll):
if ll == None:
return (None,None)
else:
result = (ll.value,ll.value)
if ll.next != None:
return (min(result[0],min_max(ll.next)[0]),max(result[1],min_max(ll.next)[1]))
else:
return result
if __name__ == '__main__':
print('\nTesting min_max')
print(min_max(None))
print(min_max(list_to_ll([0])))
print(min_max(list_to_ll([7, 3, 5, 2, 0])))
print(min_max(list_to_ll([1, 3, 5, 2, 7])))
values = [i for i in range(1,100)]
print(values)
print(min_max(list_to_ll(values)))
My question is how do I speed up my recursive call min_max on the linked list with probably 100 elements? I can pass the test_case for linked with 5-7 elements but with 100 elements my function is not efficient. Thanks.

Currently, this line:
return (min(result[0],min_max(ll.next)[0]),max(result[1],min_max(ll.next)[1]))
calls min_max recursively twice to get the minimum and maximum - this means you make 2**n calls to min_max instead of n, which is a huge penalty. Instead, consider:
min_, max_ = min_max(ll.next) # call once
return min(min_, ll.value), max(max_, ll.value) # use the two values

Related

how to Find the largest element in a list using recursion python?

i = 0
def find_max(seq):
if i == len(seq) - 1:
return seq[0]
else:
first = seq[i]
i = i + 1
max_of_rest = find_max(seq)
return max(first, max_of_rest)
I do not know what is wrong with this function? It is a infinite loop.
Please check out the following solution and follow comments:
def find_biggest(_list, max_element, first_run):
"""
_list is a list of floats or integers or both,
max element is used to store max value,
first run checks if _list is not empty
"""
if first_run and not _list: # check if _list is not empty
raise ValueError("_list should have float or integer values inside")
first_run = False
if not _list: # exit from recursion, we checked all elements
return max_element
element = _list.pop() # take one element
if type(element) not in (int, float,): # check element type
raise TypeError("_list should contain only int or float values")
if element >= max_element: # check if it is more than max
max_element = element
return find_biggest(_list, max_element, first_run) # next step of recursion
if __name__ == "__main__":
# test
print(find_biggest([-1, 4, 2, 3, 1, 0, 10, 3, 1, 7], 0, True))
# print(find_biggest([], 0, True)) # empty case
# print(find_biggest([-1, 4, 2, 3, "1", 0, 10, 3, 1, 7], 0, True)) # string in list
You can check this:
def find_max(seq):
if len(seq) == 1:
return seq[0]
else:
if seq[0] > seq[1]:
seq.pop(1)
else:
seq.pop(0)
return find_max(seq)
Your code has a lot of indentation issues, that may skip execution of some lines.
Your code should look like this:
i = 0
def find_max(seq):
global i
if i == len(seq) - 1:
return seq[0]
else:
first = seq[i]
i = i + 1
max_of_rest = find_max(seq)
return max(first, max_of_rest)
You missed the global, and thus there is no definition of i inside the function.
Your code contains an IndentationError and does not reduce its data on recursive calls - hence data never getting shorter - hence never ending recursion:
def find_max(seq):
if i == len(seq) - 1: # fixed indentation here and below
return seq[0]
else:
first = seq[i]
i = i + 1
max_of_rest = find_max(seq) # never reducing your data size, hence endless
return max(first, max_of_rest)
This would be a fixed recursive solution:
def find_max(seq):
if not seq:
return None # or raise ValueError if you want find_max([]) to crash
if len(seq) == 1:
return seq[0]
else:
return max(seq[0], find_max(seq[1:]))
The problem is inheritently bad for recursive solutions, it is far better to solve it linearly (no max(..) calls needed):
def find_max_lin(seq):
if not seq:
return None
m = seq[0]
for value in seq[1:]:
m = m if value < m else value
return m
or even better simply use the built in max(sequence):
def find_max_builtin(seq):
# no need to create your own function for that though
return max(seq)
See ternary operator for an explanation of what m = m if value < m else value does.
You are using uneeded i variable, in recursion you have base case (your first if), and recursion case, which in this case would be accessing first and second element of your list. As you already checked that the list seq has more than 1 element, you can confidently access positions 0 and 1 of the list.
In your specific case, you are not really using recursion because you never reduce your case, but instead you increment an i variable, whilst recursion is based on always calling the same function with a "simpler" or reduced problem.
With that in mind, several things can be improved in your solution.
i = 0 # Not adviced
def find_max(seq):
# Here you want to check length, making it
# depend on i = problems
if i == len(seq) - 1:
return seq[0]
else:
first = seq[i] # Remove all references to i
i = i + 1 # Remove
# Here you want to call the function with the list
# except the element you know is lower, making the problem
# smaller until you get to the base case
# Instead you are calling find_max with the same
# sequence you had (infinite loop) and returning a
# totally different result.
max_of_rest = find_max(seq)
return max(first, max_of_rest)
A complete solution based on your code would look like this
def find_max(seq):
if len(seq) == 0:
return None
if len(seq) <= 1:
return seq[0]
else:
current_max = max(seq[0], seq[1])
reduced_seq = [current_max] + seq[2:]
return find_max(reduced_seq)
i = 0
def find_max(seq):
global i
if i == len(seq) :
return seq[0]
else:
first = seq[i]
i = i + 1
max_of_rest = find_max(seq)
return max(first, max_of_rest)
print(find_max([-10,2,4,-5]))
thank me later

Improving Python user defined max function

I am new to programming and am trying to understand how to "think" more programmatically in Python. I wrote a function that returns the largest element in an array without using max:
def max_userdefined(array):
"""
Finds largest value of nonempty array of numbers in O(n)
:param: list/tuple of values
:return: max value
"""
try:
assert isinstance(array, list or tuple)
result = array[0]
for element in array:
if element > result:
result = element
return result
except IndexError:
return 'Please pass in a nonempty array'
except AssertionError:
return 'Please pass in a list or tuple'
except TypeError:
return 'Please make sure elements of array are all floats or ints'
How can I improve the above code? Open to any criticism or if anyone can recommend a good Pythonic style guide.
def max_userdefined(array):
myMax = l[0]
for num in l:
if myMax < num:
myMax = num
return myMax
print max_userdefined ([1,2,3,4,5])
Output
5
Or You can do something like following by using sorted.
def max_userdefined(array):
return sorted(array)[-1]
>>> def umax(array):
global biggest
for num in array:
if num > biggest:
biggest = num
>>> biggest = int()
>>> nums = (1, 9, 6, 4, 5, 3, 2, 7, 0)
>>> umax(nums)
>>> biggest
9
For returning the greatest number from a list the simple algorithm followed is compare two numbers and keep the larger number in a variable, then print the variable OR you can use python shortcut using sort.
Using user defined function:
def greatest_ele_list(listobj):
greatest=listobj[0]
for i in range(len(listobj)):
if listobj[i]>greatest:
greatest=listobj[i]
else:
pass
return greatest
print greatest_ele_list([4,5,3,8,1])
Using sort:
def greatest_ele_list(listobj):
listobj2=listobj.sort()
return listobj2[-1]
print greatest_ele_list([4,5,3,8,1])
Output: >>> 8

Python recursion in appending lists

I want to append to a list recursively but I cannot come up with a function that works. The function takes two arguments times and data. times should be the number of times to append the data.
Here is my code so far:
def replicate_recur(times, data):
result2 = []
if times == 0:
result2.append(data)
else:
result2.append(data)
replicate_recur(times - 1, data)
return result2
You could use a intermediate list to append to in each recursive call. That avoids these redefinition problems you're encountering currently:
def replicate_recur(times, data, result=None):
if result is None: # create a new result if no intermediate was given
result = []
if times == 1:
result.append(data)
else:
result.append(data)
replicate_recur(times - 1, data, result) # also pass in the "result"
return result
When called:
>>> replicate_recur(4, 2)
[2, 2, 2, 2]
To make your code work, you need to extend the list in the current execution with the output of the next recursive call. Also, the lowest depth of the recursion should be defined by times = 1:
def replicate_recur(times, data):
result2 = []
if times == 1:
result2.append(data)
else:
result2.append(data)
result2.extend(replicate_recur(times - 1, data))
return result2
On another note, you can simply replicate your list with:
def replicate(times, data):
return [data]*times
You can use xrange for this, there is no point to use recursion unless it is a coding test.
def replicate(times, data):
result2 = []
for i in xrange(times):
result2.append(data)
return result2
Same function can be written in a recursive way like this:
def replicate_recur(times, data, listTest=None):
# If a list has not been passed as argument create an empty one
if(listTest == None):
listTest = []
# Return the list if we need to replicate 0 more times
if times == 0:
return listTest
# If we reach here at least we have to replicate once
listTest.append(data)
# Recursive call to replicate more times, if needed and return the result
replicate_recur(times-1, data, listTest)
return listTest
Because your redefining result2 everytime. Keep result2 outside the function and it should work.
Also you could consider doing data*times to replicate if data is a list or simply do
(result2.append(data))*times
In the recursion, each time replicate_recur is called, a fresh result2 in new name space is created.
[data] * times
Would do what you are trying to achieve.
Python uses 'Pass-by-Object-reference', which is why either of the below code should do the trick in your case.
def replicate_recur(times, data, result2 = []):
if times == 1:
result2.append(data)
else:
result2.append(data)
replicate_recur(times - 1, data)
return result2
When called:
>>> replicate_recur(4, 2)
[2, 2, 2, 2]
Alternatively, you could create the result2 list and pass it as an argument to the function. The argument is 'Passed by Object reference', so the same result object is being modified inside the function as well.
def replicate_recur(times, data):
if times == 1:
result2.append(data)
else:
result2.append(data)
replicate_recur(times - 1, data)
return result2
When called:
>>> result2 = []
>>> replicate_recur(4, 2)
[2, 2, 2, 2]
Refer to the below link to learn more about Pass by Object Reference.
Python:Pass By Object Reference

How does a recursive function returns a tuple of lists?

Question is:
Define a recursive function named separate; it is passed a predicate and a list; it returns a 2-tuple whose 0 index is a list of all the values in the argument list for which the predicate returns True, and whose 1 index is a list of all the values in the argument list for which the predicate returns False. The call separate(predicate.is_positive,[1,-3,-2,4,0,-1,8]) returns ([1,4,8], [-3,-2,0,-1]). Note 0 is not positive. Hint: like the fast version of the power function in the notes, you can define and bind (but not rebind) a local name or can write a nested function (like square in power) to help with the computation.
Here is the example of his power function:
def power(a,n):
def square(n) : n*n
if n == 0:
return 1
else:
if n%2 == 1:
return a*power(a,n-1)
else:
return square( power(a,n//2) )
My attempt:
def separate(p,l):
l1=[]
l2=[]
if l == []:
return [],[]
else:
if p(l[0]):
l1=([l[0]]+map_pos(p,l[1:]))
return l1,l2
else:
l2.extend([l[0]]+separate(p,l[1:]))
return l1,l2
calling this function:
print(predicate.is_positive,[1, -3, -2, 4, 0, -1, 8]) will gives me:
TypeError: can only concatenate list (not "tuple") to list
Note predicate.is_positive is a function from predicate module which takes an int and return True if int is positive.
Can someone please help me with this? With actual code will be nice really appreciated.
This may be whay you are attempting to do
def separate(p, L):
if L == []:
return [], []
l1, l2 = separate(p, L[1:])
item = L[0]
if p(item):
l1.append(item)
else:
l2.append(item)
return l1, l2
It's not very efficient due to the L[1:] which creates a new list for each item
you can use a default argument to avoid making the slices
def separate(p, L, idx=0):
if idx == len(L):
return [], []
l1, l2 = separate(p, L, idx + 1)
item = L[idx]
if p(item):
l1.append(item)
else:
l2.append(item)
return l1, l2
It still looks clumsy. It's not really a task that calls for a recursive solution

data structure list class in python

I am working on a data structure list class in python. I would like to get the largest item in the list.
inlist = self.data
if inlist[0] > inlist[1]:
largest = inlist[0]
else:
largest = inlist[1]
for item in inlist[2]:
if item > largest:
largest = item
return largest
With the above getting stuck at largest gets returns
<bound method ListData.largest2 of <list.ListData instance at 0x2b35602c3440>>
while the data
[2, 5]
Trust the loop to get all the indicies rather than specifying them yourself.
if len(self.data) == 0:
return None
result = self.data[0]
for item in self.data:
if item > result:
result = item
return result
that for loop is going through all your data. Trying to coerce an index got you into trouble.
You can use the build in max() function.
mylist = [2, 5]
mymax = max(mylist)

Categories