Infinite Recursion in Python Function if Argument Too Long - python

I wrote this recursive function that returns the largest value in a list of integers:
def max_r(l: [int]) -> int:
if len(l) == 1:
return l[0]
else:
return l[0] if max_r(l[1:]) < l[0] else max_r(l[1:])
The call max_r([1,4,3,2,3,4,89,2,30,1] returns 89, but calling the function on a longer list:
max_r([96, 84, 87, 81, 94, 74, 65, 42, 45, 76, 5, 37, 86, 8, 46, 54, 62, 63, 35, 85, 16, 23, 18, 57, 51, 90, 58, 33, 47, 10, 64, 49, 67, 29, 71, 30, 9, 99, 75, 3, 97, 32, 59, 25, 27, 72, 61])
results in infinite recursion. Why?

It isn't infinitely recursive, but you are doing the same recursive call twice when you don't need to. Seems to complete quickly enough with the following change:
def max_r(l: [int]) -> int:
if len(l) == 1:
return l[0]
else:
result = max_r(l[1:])
return l[0] if result < l[0] else result
This isn't just a matter of calling the function recursively twice as many times, I'm not sure on the exact growth rate but it seems be exponential since each extra recursive call will be making more extra recursive calls.

Related

Collatz Conjecture Sequence iterations and multiple divisor

For the case, the sequence does not end in 1. How can I create a code that has some way of stopping the execution if 1 is not reached after a certain number of iterations? I just don't know how to add this to my code below. And is there a way to make it work with a list of divisors such as [2, 3]? I'm using Python.
import numpy
#change the function to have 3 input args, the number, the multiplier and the
divisor
def collatz(n,multiplier,divisor):
list1 = [n]
if n == 1 :
return [1]
elif n % 2 and n % 3 == 0 :
#edit the function's input args for both the else and elif loops
list1.extend(collatz(n//divisor,multiplier,divisor))
else:
list1.extend(collatz(n*multiplier+1,multiplier,divisor))
return list1
#driver function to get the input number, multiplier and divisor
if __name__=="__main__":
n=int(input("Enter any positive integer N: "))
multiplier=int(input("Enter the multiplier: "))
divisor=int(input("Enter the divisor: "))
print("\n",collatz(n,multiplier,divisor))
You will get a stack overflow error after ~1000 recursions I believe.The way to override this: https://stackoverflow.com/a/3323013/10875953. But 2^64 is immensly large, you won't be able to do that. Rather try making a iterative function.
You should use your divisor parameter in the condition that select the operation on the number.
To make this easier to manage, you shouldn't use recursion. A simple while loop will suffice:
def collatz(n,divs=2,mult=3,inc=1,maxSize=-1):
result = []
while n not in result and len(result)!=maxSize:
result.append(n)
n = (n*mult+inc)if n%divs else n//divs
return result + ['...']*(n not in result)
output:
print(collatz(20))
[20, 10, 5, 16, 8, 4, 2, 1]
print(collatz(30,2,6,2,maxSize=10))
[30, 15, 92, 46, 23, 140, 70, 35, 212, 106, '...']
print(collatz(30,2,6,2))
[30, 15, 92, 46, 23, 140, 70, 35, 212, 106, 53, 320, 160, 80,
40, 20, 10, 5, 32, 16, 8, 4, 2, 1]
print(collatz(30,2,5,3))
[30, 15, 78, 39, 198, 99, 498, 249, 1248, 624, 312, 156]
print(collatz(60,4,3,-2,maxSize=10))
[60, 15, 43, 127, 379, 1135, 3403, 10207, 30619, 91855, '...']

Python Bubble Sort Working, but incorrectly?

I am fairly new to python, and was trying to make my own bubble sort recursively. While error testing, i changed the code to this:
from sys import setrecursionlimit
setrecursionlimit(10000)
def bubbleSort(arr):
for index in range (len(arr)-1):
if arr[index] > arr[index+1]:
arr[index], arr[index+1] = arr[index+1], arr[index]
return bubbleSort(arr)
array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
array.reverse()
print(array)
sorted_array = bubbleSort(array)
print(array)
This initially prints a list from 99-0, and then after sorting, reverses the list, as is the idea of this algorithm. I just have no idea why this still works despite the fact that I assigned the sorted list to a new variable, printed the old variable, and it still prints a sorted list. In addition to this, I don't understand why the function doesn't return NoneType having finished sorting. Basically, my algorithm works, but I have no idea why. Can anyone help?
Just to help you understand how list works in Python.
Python call by reference or call by value
Python utilizes a system, which is known as “Call by Object Reference” or “Call by assignment”. In the event that you pass arguments like whole numbers, strings or tuples to a function, the passing is like call-by-value because you can not change the value of the immutable objects being passed to the function. Whereas passing mutable objects can be considered as call by reference because when their values are changed inside the function, then it will also be reflected outside the function.
Example 1:
# Python code to demonstrate
# call by value
string = "Geeks"
def test(string):
string = "GeeksforGeeks"
print("Inside Function:", string)
# Driver's code
test(string)
print("Outside Function:", string)
output-1
Inside Function: GeeksforGeeks
Outside Function: Geeks
Example 2
# Python code to demonstrate
# call by reference
def add_more(list):
list.append(50)
print("Inside Function", list)
# Driver's code
mylist = [10,20,30,40]
add_more(mylist)
print("Outside Function:", mylist)
Output-2
Inside Function [10, 20, 30, 40, 50]
Outside Function: [10, 20, 30, 40, 50]

Dealing with list of lists

I have a list with around 60000 characters.
The package I'm using takes only lists above 999 characters...
So for this example I have to run the function 60000/999 = 61 times.
Here is how a list looks like as an example:
liste=[ 'item1', 'item2', 'item3', 'item4'...]
Here is the issue, this number of characters will not be the same over time it can be less or more, so I have to take the length of the list into account.
Here is the code I'll use:
ids = function(liste)
for id in ids:
print(id)
I guess an idea should be to do a list of lists, the first big one including the 61 lists of 999 characters for each one and then do a loop:
for lists in list:
ids = function(lists)
for id in ids:
print(id)
Does someone have a better idea and/or knows how to create a list of lists depending on the length of the first big list?
It sounds like you want to process a long list in shorter chunks. You don't need to pre-process the list into a list of short lists. Here's a generator to break a list into sublists. Adapt as needed:
# Quick way to create a long list of numbers.
# I used a size that isn't a multiple of the chunk size to show that it doesn't matter.
items = list(range(105))
# Function to return smaller lists of the larger list.
def chunk(items,n):
for i in range(0,len(items),n):
yield items[i:i+n]
# Break the larger list into length 10 lists.
for subitems in chunk(items,10):
print(subitems) # process as you want...
Output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69]
[70, 71, 72, 73, 74, 75, 76, 77, 78, 79]
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89]
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
[100, 101, 102, 103, 104]
So your code would look something like:
for sublist in chunk(liste,999):
taxids = accession.taxid(sublist)
for tax in taxids:
print(tax)
You seem to be new to Python. While creating lists in python, you don't need to give a length for that list. Why make a list of lists when you can make a list of strings, and still use it as a list of string? So, you DON'T have to the length into account.
I am not very clear about the question you asked, so correct me if I'm wrong. Also, I'm assuming that accession is a user-made function (made by you), to add new elements to the list.
Following is the code for an already defined liste
# I am assuming the list 'liste' is already defined
taxids = []
for i in liste:
taxids.append(list(i))
print(taxids)
Following is the code for when you want take inputs directly
n = input("no. of inputs that are to be given(the no. of elements that you need in the list): ")
for i in range():
elem = input("Enter the string: ")
taxids.append(list(elem))
print(taxids)
Let me know if this help!

for loop based on dynamic list python

Edit:
The desired behaviour of the program is to find the number sequences that have an increasing trend, so I want to generate from ks list a list like this:
desiredList=[[97,122],[98,111],[98,101,103,103,104]]
I have the following, my goal is to run the for loop based on the length of the list, the list length gets changed inside the for loop itself. Python takes into account only the length before the for loop, when the length of the list is changed in the loop it still takes the older value before the loop. Here is the code:
ks=[97,122,111,98,111,98,101,103,103,104,97]
splitLine=2
counter=[]
for i in range(0,len(ks)):
a=ks[i:splitLine]
while len(a)>1:
for j in range(0,len(a)):
m=j
n=j+1
if(a[m]-a[n]<=0):
c=c+1
k=splitLine+c-1
a.append(ks[k]) #When append happens, the for loop still takes the older value of len(a) instead of new value
else:
a.pop(-1)
counter.append(a)
splitLine=splitLine+1
a=[]
break
A quick fix for your looping problem would be to swap out your for loop for a while loop. Change this:
for j in range(0,len(a)):
# <loop contents>
to this:
j = 0
while j < len(a):
# <loop contents>
j += 1
The for loop is grabbing values of j out of a range (a list in Python 2, and a generator object in Python 3). This range is calculated when the for loop is run the first time; it will not update after that, no matter what you do to a.
The while loop gives you more control in this situation, because you can specify the condition under which you want to exit the loop.
Your implementation is probably nesting too many loops for the problem it is trying to solve.
This first implementation contains an error. See below for the fix.
Try something along these lines perhaps:
l = [97,122,111,98,111,98,101,103,103,104,97]
out = []
acc = []
for v in l:
if len(acc)==0 or v >= acc[-1]:
acc.append(v)
else:
if len(acc) > 1:
out.append(acc)
acc = [v]
print(out)
>>>[[97, 122], [98, 111], [98, 101, 103, 103, 104]]
That previous code is slow and can drop the last found fragment. I found that error while running random tests on it to try an optimized version. The following code shows the original code with the correction and the optimized version which can be 30% faster.
def original(l):
out = []
acc = []
added = False
for v in l:
if len(acc)==0 or v >= acc[-1]:
acc.append(v)
else:
added = False
acc = [v]
if acc is not None and len(acc)>1 and not added:
added = True
out.append(acc)
return out
def optimized(l):
out = []
acc = None
tmp = None
deb_v = False
for v in l:
prev = acc[-1] if (acc is not None and len(acc)) else tmp
if prev is not None and v >= prev:
if tmp is not None:
acc = []
acc.append(tmp)
out.append(acc)
tmp = None
acc.append(v)
else:
acc = None
tmp = v
return out
# The original test data
l = [97,122,111,98,111,98,101,103,103,104,97]
assert original(l) == optimized(l) == [[97,122],[98,111],[98,101,103,103,104]]
# A list that triggered last-fragment-dropped error
l = [57, 16, 6, 19, 40, 3, 4, 13, 2, 70, 85, 65, 32, 69, 54, 51, 95, 74, 92, 46, 45, 26, 0, 61, 99, 43, 67, 71, 97, 10, 18, 73, 88, 47, 33, 82, 25, 75, 93, 80, 23, 37, 87, 90, 49, 15, 35, 63, 17, 64, 5, 72, 89, 21, 50, 8, 41, 86, 31, 78, 52, 76, 56, 42, 77, 36, 11, 60, 39, 22, 68, 27, 24, 28, 59, 96, 29, 38, 12, 79, 53, 9, 83, 94, 34, 14, 7, 48, 30, 20, 66, 62, 91, 58, 81, 1, 98, 44, 55, 84]
assert original(l) == optimized(l)
# Random testing
import random
l = list(range(100))
random.shuffle(l)
assert original(l) == optimized(l)
# Timing!
import timeit
print(timeit.timeit("original(l)", globals={"l":l, "original": original}))
# 43.95869998800117
print(timeit.timeit("optimized(l)", globals={"l":l, "optimized": optimized}))
# 34.82134292599949
As Moinuddin says, the root of your problem isn't clear to us. However, the code below shows how you can keep iterating over a list as its length changes:
def iterate_for_static_list_length(l):
for i in range(len(l)):
yield i
l.append(object())
def iterate_for_dynamic_list_length(l):
for i, _ in enumerate(l):
yield i
l.append(object())
if __name__ == '__main__':
l = [object()] * 3
print('Static implementation')
for value in iterate_for_static_list_length(l):
input(value)
print('\nDynamic implementation')
for value in iterate_for_dynamic_list_length(l):
input(value)
Output
Static implementation
0
1
2
Dynamic implementation
0
1
2
3
4
5
6
7
8
This program will keep going forever. In your code I can see that you conditionally append to the list within the loop, so it seems like it should terminate eventually.

ValueError in python.

Here is my code:
def f(x): return x%2!=0 and x%3!=0
primes = filter(f , range (6,50))
for x in primes:
for a in filter(f, range(2,x-1)):
if x%a == 0:
primes.remove(x);
print(primes)
I get this error:
File "primes.py", line 12, in <module>
primes.remove(x);
ValueError: list.remove(x): x not in list
But the funny thing is if i write like that:
def f(x): return x%2!=0 and x%3
primes = filter(f , range (6,20))
for x in primes:
for a in filter(f, range(2,x-1)):
if x%a == 0:
primes.remove(x);
print(primes)
Why? and how can i fix it?
Let's put in some print statements to see what's going on:
In [81]: %paste
def f(x): return x%2!=0 and x%3!=0
primes = filter(f , range (6,50))
print primes
for x in primes:
for a in filter(f, range(2,x-1)):
if x%a == 0:
print x
primes.remove(x);
print(primes)
## -- End pasted text --
[7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49]
25
35
35
So your problem is that you're producing the number 35 twice (and you get an exception the second time because it's not there after you remove it the first time!)
Let's look at the result:
>>> primes = filter(f , range (6,50))
>>> primes
[7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49]
You have the value 35 here, which is 5*7. In your nested loop, when a is 5, the condition is met and 35 is removed from the list of primes; when a goes to 7, the condition is met again, but this time 35 is already removed, so a exception is raised.
So a simple fixed is like the one below. such that x is removed only once when the condition is first met:
for x in primes:
for a in filter(f, range(2,x-1)):
if x%a == 0:
primes.remove(x)
break
But now it will skip the element right after a non-prime number, such as 95 in this list:
primes=[7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49, 53, 55, 59, 61, 65, 67, 71, 73, 77, 79, 83, 85, 89, 91, 95, 97]
which is fixed by making a slice of primes:
for x in primes[:]:
for a in filter(f, range(2,x-1)):
if x%a == 0:
primes.remove(x)
break

Categories