Python code to find the max values via recursive - python

I have a question about my Python code to find the max value within a list. The functional code is as following:
def large(x):
if len(x) == 1:
return x.pop(0)
else:
temp = x.pop(0)
previous = large(x)
if previous >= temp:
return previous
else:
return temp
But before that, I tried:
def large(x):
if len(x) == 1:
return x.pop(0)
else:
temp = x.pop(0)
if large(x) >= temp:
return large(x)
else:
return temp
And it will return the error message as:
<ipython-input-74-dd8676a7c4e6> in large(x)
3 return x.pop(0)
4 else:
----> 5 temp = x.pop(0)
6 if large(x) >= temp:
7 return large(x)
IndexError: pop from empty list
The toy data would be:
inputlist = [6,1,3,2,3,4,5,6]
large(inputlist)
Thank you for your help in advance. I can't find the main cause of this error. As for me, these two codes are completely same.

The problem with
if large(x) >= temp:
return large(x)
is that you end up calling large(x) (and therefore pop) more than once, which is removing elements from the list.
Personally, I would more go for this style than using a mutating function like pop.
def large(x):
if len(x) == 1:
return x[0]
remainder = large(x[1:])
return x[0] if x[0] > remainder else remainder

The same solution as OneCricketeer's
but without creating list slices upon every recursive call unnecessarily.
It also handles an empty list.
def large(x):
def rec(y):
try:
v = next(y)
except StopIteration:
return None
r = rec(y)
if r is None:
return v
return v if v > r else r
return rec(iter(x))
inputlist = [6,1,3,2,3,4,5,6]
print(large(inputlist))
print(large([]))
which produces
6
None

This does not answer why the original is incorrect. Rather, it lays down a 'standard pattern' that can be used for implementing a number of recursive problems.
I am wondering that how should I eliminate the number of elements in each round with index rather than pop?
Don't "eliminate" the elements :-)
Many recursive-friendly problems operate by reducing the range each step. This includes finding the max value (or any operation that can be expressed as a fold), a binary search, a top-down merge sort, etc. Many of these problems are themselves expressed in pseudo-code using arrays and sub-problem reduction by adjusting the ranges of each recursive call. In the case of a max/binary-search this also avoids any mutations to the original object.
Thus, a recursive max function can written as the following. Note that this form of passing in the working state is Tail-Call Friendly. While I find this form is easier to express certain problems, it does not really matter in Python as [C]Python does not support Tail-Call Optimizations^.
def my_max(lst, m=None, i=0):
# base-case: return result of work
if len(lst) == i:
return m
# Compute max through here
c = lst[i]
m = c if m is None or c > m else m
# Call recursive function increasing the index by 1.
# This is how the problem advances.
return my_max(lst, m, i + 1)
The above example also use default arguments instead of a helper method. Here is an alternative that uses the recursive result — which is often how recursive functions are introduced — as well as a discrete helper method.
def my_max(lst):
# Wrapper can ensure helper pre-conditions.
# In this case that is a non-Empty list per the base case check.
if not lst:
return None
return my_max_helper(lst, 0)
def my_max_helper(lst, i):
# base case: last item in list returns itself
if len(lst) - 1 == i:
return lst[i]
c = lst[i]
m = my_max_helper(lst, i + 1)
return c if c > m else m
In both cases temporarily variables are used to avoid duplicate expressions; while sometimes merely a stylistic choice, this consistency would have mitigated the original issue due to avoiding the unexpected side-effect of additional pop-mutation.
The above methods should be called with a list or other sequence that supports O(1) indexed lookups. In particular, the 'index' approach is not suitable, and will not work with, generator objects. There are other answers that cover this — just beware to avoid potential list slices like h,*t=l or l[1:] which can lead to bad performance bounds.
^There are modules in Python which can emulate TCO through spring-boarding.

Since this is a recursion exercise, and not something we'd do in system code, I'd go with descriptive code over efficient code and do something like:
def largest(array):
if array:
head, *tail = array
if tail and head < (result := largest(tail)):
return result
return head
return None
if __name__ == "__main__":
from random import choices
array = choices(range(100), k=10)
print(array, '->', largest(array))
OUTPUT
> python3 test.py
[46, 67, 0, 22, 23, 20, 30, 7, 87, 50] -> 87
> python3 test.py
[83, 77, 61, 53, 7, 65, 68, 43, 44, 47] -> 83
> python3 test.py
[36, 99, 47, 93, 60, 43, 56, 90, 53, 44] -> 99
>
If you really need to be efficient, I'd recommend doing so safely. Specifically, not exposing an API with special arguments that the caller is not supposed to use, e.g.:
def my_max(lst, m=None, i=0):
As they can supply values for these extra arguments that will make your code fail, and ultimately blame it on you. Ditto for exposing internal functions that the caller might call instead of the intended one:
def my_max(lst, m=None, i=0):
def my_max_helper(lst, i):
Accidentally calling my_max_helper() with a bogus value for the poorly named i argument. Instead, I'd consider nesting your functions to avoid such calling errors:
def largest(array):
def largest_recursive(array, index):
a = array[index]
if len(array) - index != 1:
if (b := largest_recursive(array, index + 1)) > a:
return b
return a
if array:
return largest_recursive(array, 0)
return None

Related

Recursively running a for loop within a for loop (python)

I'm new to python and had the idea of trying to use it to solve the numbers game on the TV show countdown. (rules for those unfamiliar). I googled and it turns out this has been done before - But I didn't understand the code properly and then thought why not just give it a go myself. I have searched and there are other people looking for recursive solutions but I couldn't get them to work for my example (Apologies, I am very new to this after all).
What I'm trying to do is to take a list of numbers, then loop through applying operations to pairs of them and replacing that pair with the output. This would repeat recursively until either we find the number we are looking for, or the list of numbers is reduced to size 1.
My function "single_move_generator" is a generator producing tuples of the form ((a,b,operation), answer, numbers left to use). I want to feed this final part of the tuple back into the function as the new list, but also keep track of the first part as it is the 'history' of how we achieved our answer. At the moment I have the following:
target = 155
numbers_to_use = [6, 25, 3, 2]
for a in single_move_generator(numbers):
if a[1] == target:
print(a[0])
for b in single_move_generator(a[2]):
if b[1] == target:
print(a[0],b[0])
quit()
for c in single_move_generator(b[2]):
if c[1] == target:
print(a[0],b[0],c[0])
quit()
produces:
(25, 6, 'add') (3, 2, 'add') (31, 5, 'multiply')
But I'd like to able to give it a larger list of numbers and have it just continue until the list reaches size one. I suspect I need a while loop - but this attempt doesn't work. It doesn't find the target or keep track of the history of moves.
numbers_available = numbers
while len(numbers_available) >1 and target not in numbers_available:
for a in single_move_generator(numbers_available):
if a[1] == target:
print("Target Found", a)
break
numbers_available = a[2]
numbers_available = a[2]
I feel like there must be a pythonic way of doing this which is far neater than I've done - any hints would be greatly appreciated. Thanks!
Based on your idea of using tuples (i, j, operation), I wrote the following. This is a recursive solution, as the main function calls itself back.
from itertools import combinations, product
def check_operation(i, j, operation):
"""
Check whether 'i operation j' is permitted.
"""
if operation == '/' and j == 0:
return False
elif operation == '/' and i%j != 0:
return False
# if not playing with negative values
#elif operation == '-' and i-j < 0:
# return False
else:
return True
def countdown(target, numbers, trackback):
if target in numbers:
print trackback
for possibility in product(combinations(numbers,2), ['+', '*', '/', '-']):
new_numbers = [k for k in numbers] # copy list, use list.copy in python 3
i, j = possibility[0][0], possibility[0][1]
operation = possibility[1]
new_numbers.remove(i)
new_numbers.remove(j)
if not check_operation(i, j, operation):
continue
new_numbers.append(eval('%i%s%i' % (i, operation, j)))
countdown(target, new_numbers, trackback+[possibility])
countdown(155, [6, 25, 3, 2], [])
It only works if a solution exists, since it won't intend to get as close to the solution as possible. However it will return all the solutions, not only one.
Based on what you posted, this should work for you:
def solve(numbers, target):
for op, res, nums in single_move_generator(numbers):
if res == target:
return [op]
ops = solve(nums, target)
if ops:
return [op] + ops
print(*solve(numbers_to_use, target))
It should be equivalent to the nested for loop you posted.
Bottom of the recursion is hit when res == target. Python functions return None by default, so if a recursive call to solve returned a truthy value then it must have hit the target. ops will contain the last operation if it was the bottom of the recursion. It is then appended to the operation that launched the recursive call and returned to an upper level. So the function will return all operations at the top level.

How to run down a list with recursion?

At first, I had to do it without recursion (just by looping which is pretty easy).
Now I have to do it with recursion, but I am not allowed to use any loop.
I guess I have to run down the list with recursion, but I don't quite understand what should be my base, or the reduction...
def long_strings_rec(strings, N):
'''
strings - a list of strings
N - an integer
Returns all strings in 'strings' of length bigger then 'N'
(This function is recursive and does not use loops)
'''
# Your code for question #2 (second function) starts here
# Your code for question #2 (second function) ends here
Any ideas? Can I have maybe an example of how to use recursion to take actions on lists indexes?
I used the helper function to do that, like #7stud suggested:
def helper (strings, K, results):
if len(strings) == 0:
return 0
elif len(strings[0]) > K:
results.append(strings[0])
strings.pop(0)
else:
strings.pop(0)
helper(strings, K, results)
return results
def long_strings_rec (strings, N):
'''
strings - a list of strings
N - an integer
Returns all strings in 'strings' of length bigger then 'N'
(This function is recursive and does not use loops)
'''
# Your code for question #2 (second function) starts here
return helper(strings, N, [])
# Your code for question #2 (second function) ends here
Worked like a charm. Hope it's not buggy.
Here's an example of how to use an accumulator:
def sum(nums):
return helper(nums, 0) #0 is the initial value for the accumulator
def helper(nums, total): #total is the accumulator
if len(nums) == 0:
return total
else:
num = nums.pop()
return helper(nums, total+num)
print sum([1, 2, 3])
--output:--
6
Basically, you redefine sum() so that it takes an additional accumulator parameter variable, then have sum() call the new function.
See if you can apply those principles to your problem.
As bjorn pointed out in the comments, you could do it like this too:
def mysum(nums, total=0):
if len(nums) == 0:
return total
else:
num = nums.pop()
return sum(nums, total+num)
print mysum([1, 2, 3])
--output:--
6

Most efficient way to check if numbers in a list is divisible by another number

So I just finished a coding test yesterday and I'm a tad neurotic. I was asked to create a class or function to check if elements in a list were all divisible by a scalable list of elements. This is the best I could come up with and was wondering if this could be improved. Thanks! And to get in front of it, I deliberately used a partial instead of lambda. To me it is much cleaner, and allows for better code re-use. Plus, I think Guido strongly discourages the use of Lambda and advises people switch to partials.
from functools import partial
def is_divisible(mod_vals, num):
"""A partial that runs a number against the list of modulus arguments, returning a bool value"""
for mod in mod_vals:
if num%mod != 0:
return False
return True
def divisible_by_factor(*mod_vals):
"""Returns a list based off a scalable amount of modulus arguments, no range support currently"""
comparison_list = []
div_partial = partial(is_divisible, (mod_vals))
for i in range(1, 100):
if div_partial(num=i):
comparison_list.append(i)
return comparison_list
>>> def divisible_by_factor(mod_vals):
>>> return [i for i in range(1, 100) if all(i % j == 0 for j in mod_vals)]
>>> print divisible_by_factor([2, 3, 5])
[30, 60, 90]
For every i test whether it's divisible by all provided values. Keep only values that pass this test.

Python return statement not running

I am creating a program to figure out the highest number of decimals in a list of numbers. Basically, a list with [123, 1233] would return 4 because 1233 has four numbers in it and it is the largest. Another example would be that [12, 4333, 5, 555555] would return 6 because 555555 has 6 numbers.
Here is my code.
def place(listy):
if len(listy) == 1:
decimal = len(str(listy[0]))
print(decimal)
else:
if len(str(listy[0])) >= len(str(listy[1])):
new_list = listy[0:1]
for i in listy[2:]:
new_list.append(i)
place(new_list)
else:
place(listy[1:])
Now, when I use print(decimal) it works, but if I change print(decimal) to return decimal, it doesn't return anything. Why is this? How do I fix this? I have come across these return statements which doing run a lot of times. Thanks in advance!
When you do a recursive call (i.e. when place calls place, and the called place returns a value, then the calling place must return it as well (i.e. the return value "bubbles up" to the initial caller).
So you need to replace every recursive call
place(...)
with
return place(...)
As others have said, there are easier solutions, such as using max(). If you want to keep a recursive approach, I would refactor your code as follows:
def place2(listy):
if len(listy) < 1:
return None
elif len(listy) == 1:
return len(str(listy[0]))
else:
v0, v1 = listy[0], listy[1]
if v1 > v0:
return place2(listy[1:])
else:
return place2([listy[0]]+listy[2:])
Although this is tail-recursive, Python does not really care so this approach will be inefficient. Using max(), or using a loop will be the better solution in Python.
It's not that the return doesn't do anything, it's that you don't propagate the return from your recursive call. You need a few more returns:
def place(listy):
if len(listy) == 1:
decimal = len(str(listy[0]))
return decimal
else:
if len(str(listy[0])) >= len(str(listy[1])):
new_list = listy[0:1]
for i in listy[2:]:
new_list.append(i)
return place(new_list) # <-- return added
else:
return place(listy[1:]) # <-- return added
You can see the print at any level, but to get it back to the caller it needs to be propagated.
The function does return the value, but it's not printing it out.
A simple way to solve this is, just call the function within a print statement.
That is:
print(place(listy))
If all you want is to find the maximum length of a list of integers, consider:
max([len(str(n)) for n in N])
For example
N = [1,22,333,4444]
max([len(str(n)) for n in N]) # Returns 4
N = [12, 4333, 5, 555555]
max([len(str(n)) for n in N]) # Returns 6
Note: This will only work for positive integers.
Or more simply:
len(str(max(N)))
Which will also only work for positive integers.
Use ''global variable'' (google it) to access and change a variable defined outside of your function.

Why is my python function returning multiple brackets?

I'm somewhat of a noob to python but I'm trying to create a recursive function which works just like the built in range function:
def Range (lo, hi):
if lo >= hi:
return []
else:
return [lo, Range (lo+1,hi)]
but its returning multiple lists.
Instead of [3,4,5,6], which is what I want, its returning [3,[4,[5,[6,[]]]]]
Why is this and how do I fix it?
When you recurse like that, Range returns a list each time:
Range(3,7)
# translates to
[3, Range(4,7)]
# which translates to
[3, [4, Range(5,7)]]
# etc.
In order to avoid this, add your lists together:
def Range (lo, hi):
if lo >= hi:
return []
else:
return [lo] + Range(lo+1, hi)
EDIT:
As #delnan points out, this function is very inefficient - it both recurses in a language without tail-call optimization* and it generates two (possibly three) new lists for each level of recursion. #mipadi's answer is more performant because it creates only one list (the acc or accumulator argument) and passes it as it recurses.
* This may not be true for the Python language, but I'm 99% sure it is true for the most common implementation of Python, namely CPython.
Your Range function returns a list, so in your last line you are returning a list within a list. What you probably should do is maintain an accumulator and add values to that:
def Range(lo, hi, acc=None):
if acc is None:
acc = []
if lo >= hi:
return acc
else:
acc.append(lo)
return Range(lo+1, hi, acc)
def Range (lo, hi):
if lo >= hi:
return []
else:
return [lo] + Range (lo+1, hi)
but you might get StackOverflow
Each recursion into Range returns a list, which is the second element in the list for the previous recursion. Of course Python has a built-in function for this, but if you want to build it yourself, you probably just want to end with
return [lo] + Range(lo+1, hi)

Categories

Resources