Using recursion to determine the index path of a nested function - python

Im trying to make a function which finds a value from a list (xs) using another list (index_list) as an index path.
My function should work like this:
xs = [[[1, 2], 3], [4, 5, [6, 7]], 8, [9, 10, 11]]
>>> recursive_index(xs, [1, 2, 0])
6
So far I have:
def recursive_index(xs: List, index_path):
if not index_path:
return 0
return recursive_index(xs, index_path[1:])
This however just returns 0 for everything, but I don't know what else the base case should be.

You're quite close, but you forgot that at each recursion you actually need to index the list so that you get further in at each recursion. This way, by the time you get to the base case, the variable xs will store the correct result and you can just return it.
This is what the code would look like:
def recursive_index(xs: List, index_path):
if not index_path:
return xs
return recursive_index(xs[index_path[0]], index_path[1:])

You want this:
def recursive_index(xs, index_path):
if not index_path:
# if path is exhausted just return current element
return xs
# use first index on current list and recurse with the remaining path
return recursive_index(xs[index_path[0]], index_path[1:])

Your recursive function should keep extracting the value from xs at the first index of index_path until there is no more index in the rest of the path:
def recursive_index(xs, index_path):
index, *rest = index_path
value = xs[index]
return recursive_index(value, rest) if rest else value

The accepted answer already explains how to fix your recursive function.
Note that an iterative function works just as well:
def iterative_index(xs, index_path):
for idx in index_path:
xs = xs[idx]
return xs
Or using reduce:
from functools import reduce
def reduce_index(xs, index_path):
return reduce(list.__getitem__, index_path, xs)
Testing:
xs = [[[1, 2], 3], [4, 5, [6, 7]], 8, [9, 10, 11]]
index_path = (1, 2, 0)
print( iterative_index(xs, index_path) )
# 6
print( reduce_index(xs, index_path) )
# 6

Related

How to find subsets from a set that product equals the target?

Let us say we have a list and target of:
list: [1,2,3,4,5] and target: 20
and we want to find total combinations to reach this, with multiplication, which is:
[1,4,5], [4,5], [2,5,2], [1,2,2,5]
I did this code, but I can't seem to know how to remove the ones so I have them too, I mean that I receive:
[1,4,5], [1,2,2,5].
But without [1], I can't seem to get it, I tried to "cheat" somehow to get it, but I can't since my code doesn't fit for it...
def Coin(target, lst, temp=[], comb=[]):
if target == 1:
comb.append(temp)
return comb
if len(lst) == 0:
return []
if target >= lst[0]:
if lst[0] > 1:
take=Coin(target/lst[0],lst,temp+[lst[0]],comb)
dont_take=Coin(target,lst[1:],temp,comb)
else:
take_1 = Coin(target, lst[1:], temp + [1], comb)
return take_1
return take
return comb
print(Coin(20, [1,2,3,4,5], [], []))
[[1, 2, 2, 5], [1, 4, 5]]
How to add the parts without 1? I don't need a solution, since as said, not homework, but practice for exam. Just a clue will be enough, I want to find it myself, but I need a clue for it.
Maybe you should combine
if lst[0] > 1:
else:
together, that means for 1 we should also decide whether take it or not.
This is much easier to do with a generator using yield than a function using return.
The difference is that you can only return once, while you can yield any number of times (including 0 if there are no solutions).
If you wish to return a list, you still can, like this:
def coin (target, lst):
return list(_coin(target, lst, 0)
def _coin (target, lst, i):
...
If that doesn't matter, the generator saves memory by not having to generate the whole list at once. And you simply:
def coin (target, lst, i=0):
... # Use yield as often as you want, all will be returned.
Second, you are running into the most common gotcha in Python. You are using mutable objects as defaults. If you're going to continue with your current approach you need to:
def coin(target, lst, temp=None, comb=None):
if temp is None:
temp = []
if comb is None:
comb = []
...
And third, you should get in the habit of following standard style conventions. In many ways, what the convention is doesn't matter that much. But that everyone is on the same page, does. Therefore you should try to follow the most common Python convention. In which the function should be named coin instead of Coin.
Edit:
The rules for the question is:
Positive integers only ( 0 not allowed ).
Number can appear once only ( at input ).
can not repeat numbers on list.
EG: [1,2,3,4], n=12 >> [1,12], [12,1], [3,4], [2,6], [1,3,4], [1,2,6].
NOT: [1,2,2,3], [2,2,3]
That is all I guess.
def coin_change_MULTI(num, lst):
if num == 1 and 1 not in lst:
return []
return Coin_MULTI(num, sorted(lst), [], [])
def Coin_MULTI(target, lst, temp=[], comb=[]):
if target == 1:
if big_than_target(1, lst):
return [[1]]
comb.append(temp)
return comb
if len(lst) == 0: return []
if target >= lst[0]:
if lst[0] > 1:
take=Coin_MULTI(target/lst[0],lst[1:],temp+[lst[0]],comb)
dont_take=Coin_MULTI(target,lst[1:],temp,comb)
return comb
else:
take_1 = Coin_MULTI(target, lst[1:], temp + [1], comb)
dont_take_1 = Coin_MULTI(target, lst[1:], temp, comb)
return comb
return take + dont_take
return comb
print(coin_change_MULTI(12, [2,4,6,12,7,3, 1]))
print(coin_change_MULTI(1, [2,4,6,12,7,3,1]))
print(coin_change_MULTI(1, [2,4,6,12,7,3]))
print(coin_change_MULTI(100, [2,4,6,12,7,3,1]))
print(coin_change_MULTI(576, [2,4,6,12,7,3,1]))
print(coin_change_MULTI(12096, [2,4,6,12,7,3,1]))
print(coin_change_MULTI(0, [2,4,6,12,7,3,1]))
print((coin_change_MULTI(24, [2,4,6,12,7,3,1])))
[[1, 2, 6], [1, 3, 4], [1, 12], [2, 6], [3, 4], [12]]
[[1]]
[]
[]
[[1, 2, 4, 6, 12], [2, 4, 6, 12]]
[[1, 2, 3, 4, 6, 7, 12], [2, 3, 4, 6, 7, 12]]
[]
[[1, 2, 3, 4], [1, 2, 12], [1, 4, 6], [2, 3, 4], [2, 12], [4, 6]]
Process finished with exit code 0

Finding next max value from index of nested lists in python?

I'm trying to find the next maximum value of nested lists, I already have a nested list sorted by bubblesort, I need to take the largest element of each nested list and insert it into the solution vector, until the solution vector is sorted.
P.S: I can't delete the element from the initial nested list, only find the next maximum value.
See the image at the bottom as an example:
Nested_list = [[1, 7, 9], [4, 5, 6], [2, 3, 8], [0]]
The way I devised deleted the largest vector from the original list, which was quite time consuming, I believe that just moving the index to the next largest value will consume less time:
def bubbleSort(array):
n = len(array)-1
for i in range(n):
for j in range(0, n-i):
if array[j] > array[j+1]:
array[j], array[j+1] = array[j+1], array[j]
else:
continue
return array
def ordena_lista(output):
for sublista in output:
bubbleSort(sublista)
def maior_valor_lista(output):
return list(el[-1] for el in output)
def nested_remove(L, x):
if x in L:
L.remove(x)
else:
for element in L:
if type(element) is list:
nested_remove(element, x)
b = list(random.sample(range(10), 10))
n= m.floor(m.sqrt(len(b)))
output=list([b[i:i + n] for i in range(0, len(b), n)])
ordena_lista(b)
while output:
valores_maximo = maior_valor_lista(output)
var = max(valores_maximo, key=int)
final = [var] + final
nested_remove(output, var)
output = list(filter(None, output))
The simplest solution would be the following,
from functools import reduce
from operator import add
def sort_nested_list(nested_list):
return sorted(reduce(add, nested_list))
but, without knowing the exact implementation details of python's sorted, I can't tell you if it takes advantage of your pre-sorting.
If we know the sublists are sorted, and we are allowed to copy the list, and we know how many elements there are in total, we can write the following,
import math
from copy import deepcopy
def get_max_and_pop(nested_list):
""" find the maximum element of a sublist of nested_list, remove it from the sublist, and return it """
print(f"get_max_and_pop says: {nested_list}")
return max(nested_list, key=lambda x: x[-1:]).pop()
def sort_nested_list_whose_sublists_are_sorted(nested_list, n_elements):
nested_list_copy = deepcopy(nested_list)
return [get_max_and_pop(nested_list=nested_list_copy) for _ in range(n_elements)][::-1]
edit: without knowledge of the number of elements, we can write,
from copy import deepcopy
def sort_nested_list_whose_sublists_are_sorted_iter(nested_list):
nested_list_copy = deepcopy(nested_list)
while any(nested_list_copy):
yield max(nested_list_copy, key=lambda x: x[-1:]).pop()
This amounts to a bizarre, woefully inefficient and completely unnecessary sorting algorithm but here goes anyway:
Nested_list = [[9, 7, 1], [4, 5, 6], [2, 3, 8], [0]]
for e in Nested_list:
e.sort()
Output_list = []
Nested_list_copy = [[e_ for e_ in e] for e in Nested_list]
element_count = sum(len(e) for e in Nested_list)
for _ in range(element_count):
m = None
for i, e in enumerate(Nested_list_copy):
if e:
tm = e[-1]
if m is None or tm > m:
m = tm
k = i
Output_list.insert(0, Nested_list_copy[k].pop())
print(Nested_list)
print(Output_list)
Output:
[[1, 7, 9], [4, 5, 6], [2, 3, 8], [0]]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Recursively modify list with irregular shape

The aa list is my input, and the bb list my desired output. The operation is rather simple (change every element in aa to string appending some characters, while keeping the original shape), but the way I do it seems unnecessarily convoluted.
Is there a better (more Pythonic) way to do this?
aa = [0, 5, 6, [8], 3, [9, 2]]
bb = []
for e1 in aa:
if type(e1) == list:
bb2 = []
for e2 in e1:
bb2.append('sss' + str(e2))
bb.append(bb2)
else:
bb.append('sss' + str(e1))
print(bb)
['sss0', 'sss5', 'sss6', ['sss8'], 'sss3', ['sss9', 'sss2']]
You can use a recursive list comprehension to achieve this
def f(data):
return [f(i) if isinstance(i, list) else 'sss{}'.format(i) for i in data]
For example
>>> aa = [0, 5, 6, [8], 3, [9, 2]]
>>> f(aa)
['sss0', 'sss5', 'sss6', ['sss8'], 'sss3', ['sss9', 'sss2']]
You could do what your question title is already hinting at, i.e. use recursion:
def transform(ll):
if isinstance(ll, list):
return list(map(transform, ll))
else:
return 'sss%s' % ll
print(transform([0, 5, 6, [8], 3, [9, 2]]))
Well, one way to do this recursively that is a little more concise is
def addSSS(item):
if isinstance(item, list):
# map takes a function, and a list and returns a
# new list that is the result of applying the function
# to each element. list() converts the result to a list
return list(map(addSSS, item))
# if it's not a list simply append the string and return that value
return "sss" + str(item)
bb = list(map(addSSS, aa))
Maps are nice because they don't evaluate each element right away, only on demand, of course here it gets evaluated right away when you call list() to turn it back to a list
This has the additional advantage that it works when your list nests deeper like aa = [1, 2, [1, 2, [1, 2]]]

How to find all partial solutions of a list recursively

I have a box with a maximum weight capacity. I am given a list of weights of the items that I am supposed to fit in the box. I need all solutions that get to, or as close as possible to, the maximum capacity. What I mean is that I should not be able to add another item to any of the partial solutions without going over the maximum capacity.
If I am given the following list of weights:
[1,2,3,4,5]
If the maximum capacity is 9, the solutions should be (I may have missed one, but you get the point):
[[4,3,2], [5,3,1], [5,4], [3,2,1], [4,2,1], [4,3,1], [5,3]]
Here's my recursive algorithm, I think I am getting close but I can't figure out how to fix it.
def findSubset(alist, maxim):
if maxim <= 0:
return [[]]
if len(alist) == 0:
return []
alist2 = alist[1:]
include = findSubset(alist2, maxim-alist[0])
for s in include:
s.append(alist[0])
return include + findSubset(alist2, maxim)
Current output is:
[[4, 3, 2, 1], [5, 3, 2, 1], [5, 4, 2, 1], [5, 4, 3, 1], [5, 3, 1], [5, 4, 1], [4, 3, 2], [5, 3, 2], [5, 4, 2], [5, 4, 3], [5, 4]]
My advice would be to iterate on all element of the list, while building a list of possible solutions
if the element is < maxim, recurse on a sublist on following elements for maxim - elt, and for each element of the result, append the element to it and append all to the resulting list
if the element is == maxim, add the singleton list containing the element to the resulting list
Code is:
def findSubset(alist, maxim):
res = []
if maxim <= 0:
return [[]]
if len(alist) == 0:
return []
for i, elt in enumerate(alist):
if elt < maxim:
res.extend([ [elt] + l for l in findSubset(alist[i+1:], maxim-elt)])
elif elt == maxim:
res.append([elt])
return res
It gives
>>> findSubset(lst, 9)
[[1, 3, 5], [2, 3, 4], [4, 5]]
Above code only gives exact solutions. In case where there are no exact solution, it should be extended to give best approaching solutions:
def findApproachSubset(alist, maxim):
for i in range(maxim, 0, -1):
res = findSubset(alist, i)
if len(res) > 0:
return res
return []
For example:
>>> findSubset([1, 4, 5, 6], 8)
[]
>>> findApproachSubset([1, 4, 5, 6], 8)
[[1, 6]]
because here the best solution is for 7 instead of 8.
This is written using Haskell. As it seems that this is homework, there is no point giving you the complete answer.
We use three separate functions, the function f can be made recursive as it is just map
xs = [1, 2, 3, 4, 5]
perms [] = []
perms xs = xs : perms (tail xs)
All you need to know is that : means cons i.e 1:[2] = [1,2]
In python, we could write it as:
def perms(xs):
if xs:
return [xs] + perms(xs[1:])
else:
return []
so perms xs is just [[1,2,3,4,5],[2,3,4,5],[3,4,5],[4,5],[5]]
takeWhileLessThan _ [] = []
takeWhileLessThan n (x:xs) =
if n-x < 0 then [] else (x : takeWhileLessThan (n-x) xs)
and takeWhileLessThan uses recursion. It operates on a single list, keeping track of the current sum.
f n xs = map (takeWhileLessThan n) (perms xs)
> [[1,2,3],[2,3,4],[3,4],[4,5],[5]]
The final function maps the recursive function over the list of lists. If you then wanted all values and wanted it to be recursive, just write another function...
if you are allowed for loops, then this method would work well. The only recursion is the main function.
def f(n, xs, solutions):
if xs:
m = n
ys = []
for x in xs:
if (m-x) < 0:
break
else:
ys.append(x)
m = m-x
solutions.append(ys)
return f(n, xs[1:], solutions)
else:
return solutions
which gives:
>>> f(9, [1,2,3,4,5], [])
[[1, 2, 3], [2, 3, 4], [3, 4], [4, 5], [5]]
def recursive_function(current_node: int, nodes_left: list, maximum: int, trace: list) -> list:
sum_so_far = sum(trace)
if current_node is not None:
trace.append(current_node)
else:
current_node = 0
if sum_so_far + current_node > maximum:
# That means, that current trace is already to big
# no need to search further this path
return False
elif sum_so_far + current_node == maximum:
# Bingo! The perfect set of nodes!
# No need to look further this path
return {tuple(sorted(trace))}
else:
# We still haven't reached maximum value
# let's check if we can pack some more nodes to trace
results = set()
for node in nodes_left:
nodes_copy = nodes_left.copy()
nodes_copy.remove(node)
traces = recursive_function(node, nodes_copy, maximum, trace.copy())
if traces:
# This path gave us some legitimate results, we collect them
results.update(traces)
if results:
# At least one possible path gave us results, we pass them on
return results
# There was nothing left in nodes_left that we could add and not
# cross the maximum limit. The path leading to this moment fits
# our requirements.
return {tuple(sorted(trace))}
def findSubset(alist, maxim) -> list:
results = recursive_function(None, alist, maxim, [])
return results
Why do I do this {tuple(sorted(trace))}? So I don't keep different permutations of same results in my memory.
Time cost: n!
Memory cost: n

Using (trying to) recursion to reverse lists within a list

def is_list(p):
return isinstance(p, list)
def deep_reverse(p):
initial = []
for v, e in enumerate(p):
if is_list(e):
#print p[v][::-1]
initial.append(p[v][::-1])
deep_reverse(e)
return initial
p = [1, [2, 3, [4, [5, 6, [7, 8]]]]]
print deep_reverse(p)
I get [[[4, [5, 6, [7, 8]]], 3, 2]], expected at least (I haven't bothered to figure out how to not lose the very first list [1[...]] yet) [[[[6, 5, [8, 7]], 4], 3, 2]].
As you can see the code only reverses [ [2, 3]] --> [[3, 2]].
What did I do wrong? Haven't I though about?
This is how I would do it:
def deep_reverse(p):
return [deep_reverse(x) if isinstance(x, list) else x for x in p[::-1]]
p = [1, [2, 3, [4, [5, 6, [7, 8]]]]]
print deep_reverse(p) # [[[[[8, 7], 6, 5], 4], 3, 2], 1]
A more generic, Pythonic answer to this, based on Pavel Anossov's is as follows:
def deep_reversed(seq):
return [deep_reversed(x) if (isinstance(x, collections.Sequence) and
not isinstance(x, str)) else x
for x in reversed(seq)]
Note that this is for Python 3.x, in Python 2.x, you will want isinstance(x, basestring) instead to allow for Unicode strings.
This answer is a good one, as it will work correctly with any object that acts as a sequence - be it a list, a tuple, or a custom class. This means it's much more flexible.
Edit: If you wanted it to reverse strings internally:
def deep_reversed(seq):
for x in reversed(seq):
if isinstance(x, collections.Sequence):
if isinstance(x, str):
yield "".join(reversed(x))
else:
yield deep_reversed(x)
else:
yield x
Again, in 2.x, use isinstance(x, basestring).
There are already many nice solutions, but maybe this is the algorithm you are trying:
def is_list(p):
return isinstance(p, list)
def deep_reverse(p):
initial = p[::-1] # reverse this level
for v, e in enumerate(initial):
if is_list(e): # for all the sublist in this level
initial[v] = deep_reverse(e) # recursively call deep_reverse to reverse the sublist
return initial
p = [1, [2, 3, [4, [5, 6, [7, 8]]]]]
print deep_reverse(p)
In the recursive calls to deep_reverse(e) you are not using the returned value. It looks as though you are expecting it to modify the input list
You can change it to something like this:
def deep_reverse(p):
initial = []
for e in p[::-1]:
if is_list(e):
initial.append(deep_reverse(e)])
else:
initial.append(e)
return initial
This will solve your purpose:
import collections
def dr(p):
r=[]
for i in p:
if isinstance(i,collections.Iterable):
r.append(dr(i))
else:
r.append(i)
return r[::-1]

Categories