Python: Why is this recursion failing? - python

Why am I getting maximum recursion results of [] in this simple recursion example?
# generate data
df = pd.DataFrame({'id': [1, 2, 2, 3, 4, 5, 6, 7],
'parent': [np.nan, 1, 2, 2, np.nan, 1, 1, 5]})
parents = df.parent.dropna().unique().astype(int)
def find_parent(init_parent):
init_parent = [init_parent] if isinstance(init_parent, int) else [init_parent]
if len(init_parent) == 0:
return init_parent
else:
return find_parent(df.loc[df['parent'].isin(init_parent)]['id'].tolist())
# max recursion of [] results
find_parent(parents[1])

def find_parent(init_parent):
init_parent = [init_parent] if isinstance(init_parent, int) else [init_parent]
if len(init_parent) == 0: # this only returns true on an empty array
return init_parent # you're getting [] because this return
else:
return find_parent(df.loc[df['parent'].isin(init_parent)]['id'].tolist())
# run ops
find_parent(df.loc[df['parent'].isin(init_parent)]['id'].tolist())
your maximum return is reached when there is no parent. You overwrite the init parent in the line above, check if it's an empty array, and then return that empty array.

Related

Index of an element in a nested list

I'm struggling with an exercise for a few days.
Given was following nested list:
[1, [5, 62, 6], 4, [99, [100, 200, 600, [1000, [2000]]]], [74, 41, 16], 7, [8], [[[400]]]]
And this function body:
def find_element(liste, find, index = 0):
I have to find an element in the nested list and the function should return the exact index of the found element, for example [1,0] for 5 or [3, 1, 3, 1, 0] for 2000.
The function has to be recursive.
My problem is the function has to return false if the element is not in the list.
This is my code:
def find_element(liste, find, index = 0):
indexList = []
if len(liste) == index:
return indexList
if liste[index] == find:
indexList.append(index)
else:
if type(liste[index]) == list:
indexList.extend(find_element(liste[index], find))
if indexList:
indexList.insert(0, index)
else:
indexList.extend(find_element(liste, find, index + 1))
return indexList
I tried a second function that returns false when the list is empty or an if condition if the index is 0 and the indexList is empty, but all I got were a RecursionError or a TypeError.
Ajax1234's answer works, but if you need something a little more simple this may be better:
def find_idx(input_list, elem):
for i in range(len(input_list)):
if isinstance(input_list[i], list):
result = find_idx(input_list[i], elem)
if result:
return [i] + result
elif input_list[i] == elem:
return [i]
return False
input_list = [1, [5, 62, 6], 4, [99, [100, 200, 600, [1000, [2000]]]], [74, 41, 16], 7, [8], [[[400]]]]
print(find_idx(input_list, 2000))
# Output: [3, 1, 3, 1, 0]
This is basically a DFS (https://en.wikipedia.org/wiki/Depth-first_search). If you think of your data structure as a tree, your list entries are nodes since they themselves can contain other lists, just as a node in a tree can point to other nodes. The magic is in returning False if nothing was found at the very end of the method, but recursively searching all sublists before you get to that point. Also, you have to check whether your list entry is itself a list, but this is just an analogy to the fact that a tree can have nodes that do point to other nodes, and nodes that do not (leaf nodes, or plain old numbers in your case).
You can use recursion with a generator:
def find_element(l, elem):
def get_elem(d, c = []):
for i, a in enumerate(d):
if a == elem:
yield c+[i]
elif isinstance(a, list):
yield from get_elem(a, c+[i])
return False if not (r:=list(get_elem(l))) else r[0]
data = [1, [5, 62, 6], 4, [99, [100, 200, 600, [1000, [2000]]]], [74, 41, 16], 7, [8], [[[400]]]]
print(find_element(data, 2000))
Output:
[3, 1, 3, 1, 0]
I agree generators are a nice fit for this problem. I would separate the program logic into two separate functions, dfs and find_element -
def dfs(ls, r = []):
if isinstance(ls, list):
for (i, v) in enumerate(ls):
yield from dfs(v, [*r, i])
else:
yield (r, ls)
def find_element(ls, q):
for (k, v) in dfs(ls):
if v == q:
return k
return None
print(find_element(input, 5))
# [1, 0]
print(find_element(input, 2000))
# [3, 1, 3, 1, 0]
print(find_element(input, 999))
# None
Or you could fix your original program using a fourth parameter, r = [] -
def find_element(ls, q, i = 0, r = []):
if i >= len(ls):
return None
elif isinstance(ls[i], list):
return find_element(ls[i], q, 0, [*r, i]) \
or find_element(ls, q, i + 1, r)
elif ls[i] == q:
return [*r, i]
else:
return find_element(ls, q, i + 1, r)
print(find_element(input, 5))
# [1, 0]
print(find_element(input, 2000))
# [3, 1, 3, 1, 0]
print(find_element(input, 999))
# None

Python all combinations from parameterized list

Good day everyone, I need to get all possible combination from the list with parameters.
for example, we can have a list with parameters, where:
the parameter has only value, in this case, this parameter will not be changed
the parameter has a lower and high limit, in this case, the parameter will be changed from the lower limit to high limit wit some step
here is my a code
from typing import List
class Parameter:
def __init__ (self, value = None, lowLimit = None, hightLimit = None, step = None):
self.value = value
self.lowLimit = lowLimit
self.hightLimit = hightLimit
self.step = step
data: List[Parameter] = [Parameter(1), Parameter(2, 2, 3, 1), Parameter(3, 3, 4, 1)]
def recursion(array: List[Parameter], skip: int):
for index, value in enumerate(array):
if index < skip: continue
if value.lowLimit is not None and value.hightLimit is not None:
temp = [it.value for it in array]
init = value.lowLimit
while init <= value.hightLimit :
temp[index] = init
init += value.step
yield temp
def combination(array: List[Parameter]):
for index, value in enumerate(array):
if value.lowLimit is None and value.hightLimit is None : continue
for d in recursion(array, index):
yield d
for d in combination(data):
print(d)
and this what I would like to get:
[1, 2, 3]
[1, 2, 4]
[1, 2, 5]
[1, 3, 3]
[1, 2, 3]
[1, 3, 3]
but my code does not work, and I need some help to implement this task. Could someone help, please?
Not sure I really understand what you are asking for, but I think you can convert your Parameters to ranges and then get the itertools.product of those:
import itertools
data = [Parameter(1), Parameter(2, 2, 3, 1), Parameter(3, 3, 5, 2)]
ranges = [[p.value] if p.lowLimit is None else
range(p.lowLimit, p.hightLimit+1, p.step) for p in data]
print(ranges)
# [[1], range(2, 4), range(3, 6, 2)]
for d in itertools.product(*ranges):
print(d)
# (1, 2, 3), (1, 2, 5), (1, 3, 3), (1, 3, 5)
(Note: Output differs as I slights changed the data to actually use a step != 1)

Delete occurrences of an element if it occurs more than n times in Python

How can I fix my code to pass the test case for Delete occurrences of an element if it occurs more than n times?
My current code pass one test case and I'm sure that the problem is caused by order.remove(check_list[i]).
However, there is no way to delete the specific element with pop() because it is required to put an index number rather than the element in pop().
Test case
Test.assert_equals(delete_nth([20,37,20,21], 1), [20,37,21])
Test.assert_equals(delete_nth([1,1,3,3,7,2,2,2,2], 3), [1, 1, 3, 3, 7, 2, 2, 2])
Program
def delete_nth(order, max_e):
# code here
check_list = [x for x in dict.fromkeys(order) if order.count(x) > 1]
print(check_list)
print(order)
for i in range(len(check_list)):
while(order.count(check_list[i]) > max_e):
order.remove(check_list[i])
#order.pop(index)
return order
Your assertions fails, because the order is not preserved. Here is a simple example of how this could be done without doing redundant internal loops to count the occurrences for each number:
def delete_nth(order, max_e):
# Get a new list that we will return
result = []
# Get a dictionary to count the occurences
occurrences = {}
# Loop through all provided numbers
for n in order:
# Get the count of the current number, or assign it to 0
count = occurrences.setdefault(n, 0)
# If we reached the max occurence for that number, skip it
if count >= max_e:
continue
# Add the current number to the list
result.append(n)
# Increase the
occurrences[n] += 1
# We are done, return the list
return result
assert delete_nth([20,37,20,21], 1) == [20, 37, 21]
assert delete_nth([1, 1, 1, 1], 2) == [1, 1]
assert delete_nth([1, 1, 3, 3, 7, 2, 2, 2, 2], 3) == [1, 1, 3, 3, 7, 2, 2, 2]
assert delete_nth([1, 1, 2, 2], 1) == [1, 2]
A version which maintains the order:
from collections import defaultdict
def delete_nth(order, max_e):
count = defaultdict(int)
delet = []
for i, v in enumerate(order):
count[v] += 1
if count[v] > max_e:
delet.append(i)
for i in reversed(delet): # start deleting from the end
order.pop(i)
return order
print(delete_nth([1,1,2,2], 1))
print(delete_nth([20,37,20,21], 1))
print(delete_nth([1,1,3,3,7,2,2,2,2], 3))
This should do the trick:
from itertools import groupby
import numpy as np
def delete_nth(order, max_e):
if(len(order)<=max_e):
return order
elif(max_e<=0):
return []
return np.array(
sorted(
np.concatenate(
[list(v)[:max_e]
for k,v in groupby(
sorted(
zip(order, list(range(len(order)))),
key=lambda k: k[0]),
key=lambda k: k[0])
]
),
key=lambda k: k[1])
)[:,0].tolist()
Outputs:
print(delete_nth([2,3,4,5,3,2,3,2,1], 2))
[2, 3, 4, 5, 3, 2, 1]
print(delete_nth([2,3,4,5,5,3,2,3,2,1], 1))
[2, 3, 4, 5, 1]
print(delete_nth([2,3,4,5,3,2,3,2,1], 3))
[2, 3, 4, 5, 3, 2, 3, 2, 1]
print(delete_nth([2,2,1,1], 1))
[2, 1]
Originally my answer only worked for one test case, this is quick (not the prettiest) but works for both:
def delete_nth(x, e):
x = x[::-1]
for i in x:
while x.count(i) > e:
x.remove(i)
return x[::-1]

Generate a Recursive Tribonacci Sequence in Python

I am supposed to create a function to generate a Tribonacci sequence in Python. This function must be RECURSIVE. Based on this, I have this code so far:
def TribRec(n) :
if (n == 0 or n == 1 or n == 2) :
return []
elif (n == 3) :
return [0,1][:n]
else :
sequence = TribRec(n-1)
sequence.append(sequence[len(sequence)-1] +
sequence[len(sequence)-2] + sequence[len(sequence)-3])
return sequence
def Trib(n) :
for i in range(1, n) :
print( TribRec(i) , " ", end = "")
# Driver code
n = 10
Trib(n)
When I run this code, I get the following output:
[] [] [0, 1] [0, 1, 2] [0, 1, 2, 3] [0, 1, 2, 3, 6] [0, 1, 2, 3, 6, 11] [0, 1,2, 3, 6, 11, 20] [0, 1, 2, 3, 6, 11, 20, 37]
Instead, I want the output of length = n which is 10 here in the form of:
[0, 1, 2, 3, 6, 11, 20, 37, 68, 125]
How do I fix my current code?
Your code can be written more succinctly as follows.
def TribRec(n) :
if n in {0, 1, 2}:
return n
else :
return TribRec(n-1) + TribRec(n-2) + TribRec(n-3)
def Trib(n) :
for i in range(0, n) :
yield TribRec(i)
res = list(Trib(10))
# [0, 1, 2, 3, 6, 11, 20, 37, 68, 125]
Explanation
As per #czr's solution, you can sum the last 3 calls to TribRec recursively.
Return n if it belongs to the set {0, 1, 2}.
For Trib(n), use a generator to remove boilerplate code (list instantiation, appending to list).
Instead of returning a list in every recursive call, return the nth element and aggregate the list on the Trib function:
def TribRec(n) :
if n == 0:
return 0
elif n == 1:
return 1
elif n == 2:
return 2
else :
return TribRec(n-1) + TribRec(n-2) + TribRec(n-3)
def Trib(n) :
l = []
for i in range(0, n) :
l.append(TribRec(i))
return l
# Driver code
n = 10
Trib(n)
This is much more simple:
def trib(n):
if n < 3:
return n
return trib(n-1) + trib(n-2) + trib(n-3)
def tri(n):
result = []
for i in range(0,n):
result.append(trib(n))
return result

Python: A program to find the LENGTH of the longest run in a given list?

Q: A run is a sequence of adjacent repeated values. Given a list, write a function to
determine the length of the longest run. For example, for the sequence [1, 2, 5, 5, 3, 1, 2, 4, 3, 2, 2, 2, 2, 3, 6, 5, 5, 6, 3, 1], the longest run is 4.
I am having trouble with this, I've written a code that finds the longest run consist of the number '2' but have yet to get the length of the run which is 4.
Here is my code so far (i've commented out a part that i was working on but don't pay attention to it):
# longestrun.py
# A function to determine the length of the longest run
# A run is a sequence of adjacent repeated values.
def longestrun(myList):
result = None
prev = None
size = 0
max_size = 0
for i in myList:
if i == prev:
size += 1
if size > max_size:
result = i
max_size = size
else:
size = 0
prev = i
return result
def main():
print("This program finds the length of the longest run within a given list.")
print("A run is a sequence of adjacent repeated values.")
myString = input("Please enter a list of objects (numbers, words, etc.) separated by
commas: ")
myList = myString.split(',')
longest_run = longestrun(myList)
print(">>>", longest_run, "<<<")
main()
Help please!!! :(((
You can do this in one line using itertools.groupby:
import itertools
max(sum(1 for _ in l) for n, l in itertools.groupby(lst))
This should work if you do not want to use itertools and imports.
a=[1, 2, 5, 5, 3, 1, 2, 4, 3, 2, 2, 2, 2, 3, 6, 5, 5, 6, 3, 1]
def longestrun(myList):
result = None
prev = None
size = 0
max_size = 0
for i in myList:
if i == prev:
print (i)
size += 1
if size > max_size:
print ('******* '+ str(max_size))
max_size = size
else:
size = 0
prev = i
print (max_size+1)
return max_size+1
longestrun(a)
Just another way of doing it:
def longestrun(myList):
sett = set()
size = 1
for ind, elm in enumerate(myList):
if ind > 0:
if elm == myList[ind - 1]:
size += 1
else:
sett.update([size])
size = 1
sett.update([size])
return max(sett)
myList = [1, 2, 5, 5, 3, 1, 2, 4, 3, 2, 2, 2, 2, 3, 6, 5, 5, 6, 3, 1]
print longestrun(myList)
def getSublists(L,n):
outL=[]
for i in range(0,len(L)-n+1):
outL.append(L[i:i+n])
return outL
def longestRun(L):
for n in range(len(L), 0, -1):
temp=getSublists(L,n)
for subL in temp:
if subL==sorted(subL):
return len(subL)
def longestrun(myList):
size = 1
max_size = 0
for i in range(len(myList)-1):
if myList[i+1] = myList[i]:
size += 1
else:
size = 1
if max_size<size:
max_size = size
return size
Remove the .split() from myList in main() and you're good to go with this.
As an update to David Robinson's answer, it is now (Python 3.4) possible to return 0 on an empty sequence (instead of raising ValueError):
import itertools
max((sum(1 for _ in l) for n, l in itertools.groupby(lst)), default=0)

Categories

Resources