Recursively multiply all values in an array Python - python

I've had a look through the forums and can't find anything to do with multiplying all elements in an array recursively.
I've created the following code that almost does what I want. The goal is to use no loops and only recursion.
Here's the code:
def multAll(k,A):
multAllAux(k,A)
return A[:]
def multAllAux(k,A):
B = [0]
if A == []:
return 0
else:
B[0] = (A[0] * k)
B.append(multAllAux(k,A[1:]))
return B
print(multAllAux(10, [5,12,31,7,25] ))
The current output is:
[50, [120, [310, [70, [250, 0]]]]]
However, it should be:
[50,120,310,70,250]
I know I am close, but I am at a complete loss at this point. The restrictions of no loops and solely recursion has left me boggled!

Your multAllAux function returns a list. If you append a list to another list, you get this nested list kind of structure that you are getting right now.
If you instead use the "extend" function; it will work as expected.
>>> a = [1, 2, 3]
>>> a.extend([4, 5])
>>> a
[1, 2, 3, 4, 5]
extend takes the elements from a second list and adds them to the first list, instead of adding the second list itself which is what append does!

Your function also returns a zero at the end of the list, which you don't need. You can try this:
def mult(k, A: list) -> list:
return [k * A[0]] + mult(k, A[1:]) if A else []

The problem is here:
B.append(multAllAux(k,A[1:])))
What .append(..) does is it takes the argument, considers it as a single element and adds that element to the end of the list. What you want is to concatenate to the list (ie the item being added should be seen as a list of elements rather than one single element).
You can say something like: B = B + multAllAux(..) or just use +=
B += multAllAux(...)
BTW, if you wanted to multiply a single number to a list, there is a very similar construct: map(..). Note that this behaves slightly differently depending on whether you're using Py2 or Py3.
print(map(lambda x: x * 10, [5,12,31,7,25]))

Related

Python cross multiplication with an arbitrary number of lists

I'm not sure what the correct term is for the multiplication here but I need to multiply an element from List A for example by every element in List B and create a new list for the new elements, so that the total length of the new list is len(A)*len(B).
As an example
A = [1,3,5], B=[4,6,8]
I need to multiply the two together to get
C = [4,6,8,12,18,24,20,30,40]
I have researched this and I have found that itertools(product) have exactly what I needed, however it is for a specific number of lists and I need to generalise to any number of lists as requested by the user.
I don't have access to the full code right now but the code asks the user for some lists (can be any number of lists) and the lists can have any number of elements in the lists (but all lists contain the same number of elements). These lists are then stored in one big list.
For example (user input)
A = [2,5,8], B= [4,7,3]
The big list will be
C = [[2,5,8],[4,7,3]]
In this case there are two lists in the big list but in general it can be any number of lists.
Once the code has this I have
print([a*b for a,b in itertools.product(C[0],C[1])])
>> [8,14,6,20,35,15,32,56,24]
The output of this is exactly what I want, however in this case the code is written for exactly two lists and I need it generalised to n lists.
I've been thinking about creating a loop to somehow loop over it n times but so far I have not been successful in this. Since C could any of any length then the loop needs a way to know when it's reached the end of the list. I don't need it to compute the product with n lists at the same time
print([a0*a1*...*a(n-1) for a0,a1,...,a(n-1) in itertools.product(C[0],C[1],C[2],...C[n-1])])
The loop could multiply two lists at a time then use the result from that multiplication against the next list in C and so on until C[n-1].
I would appreciate any advice to see if I'm at least heading in the right direction.
p.s. I am using numpy and the lists are arrays.
You can pass variable number of arguments to itertools.product with *. * is the unpacking operator that unpacks the list and passes its values the values of list to the function as if they are separately passed.
import itertools
import math
A = [[1, 2], [3, 4], [5, 6]]
result = list(map(math.prod, itertools.product(*A)))
print(result)
Result:
[15, 18, 20, 24, 30, 36, 40, 48]
You can find many explanations on the internet about * operator. In short, if you call a function like f(*lst), it will be roughly equivalent to f(lst[0], lst[1], ..., lst[len(lst) - 1]). So, it will save you from the need to know the length of the list.
Edit: I just realized that math.prod is a 3.8+ feature. If you're running an older version of Python, you can replace it with its numpy equivalent, np.prod.
You could use a reduce function that is intended exactly for these types of operations, which is based on recursion and accumulation. I am providing you an example with a primitive function so you can better understand its functionality:
lists = [
[4, 6, 8],
[1, 3, 5]
]
def reduce(function, iterable, initializer=None):
it = iter(iterable)
if initializer is None:
value = next(it)
else:
value = initializer
for element in it:
value = function(value, element)
return value
def cmp(a, b):
for x in a:
for y in b:
yield x*y
summed = list(reduce(cmp, lists))
# OUTPUT
[4, 12, 20, 6, 18, 30, 8, 24, 40]
In case you need it sorted just make use of the sort() function.

Meaning of list[-1] in Python

I have a piece of code here that is supposed to return the least common element in a list of elements, ordered by commonality:
def getSingle(arr):
from collections import Counter
c = Counter(arr)
return c.most_common()[-1] # return the least common one -> (key,amounts) tuple
arr1 = [5, 3, 4, 3, 5, 5, 3]
counter = getSingle(arr1)
print (counter[0])
My question is in the significance of the -1 in return c.most_common()[-1]. Changing this value to any other breaks the code as the least common element is no longer returned. So, what does the -1 mean in this context?
One of the neat features of Python lists is that you can index from the end of the list. You can do this by passing a negative number to []. It essentially treats len(array) as the 0th index. So, if you wanted the last element in array, you would call array[-1].
All your return c.most_common()[-1] statement does is call c.most_common and return the last value in the resulting list, which would give you the least common item in that list. Essentially, this line is equivalent to:
temp = c.most_common()
return temp[len(temp) - 1]

In-place modification of Python lists

I am trying to perform in-place modification of a list of list on the level of the primary list. However, when I try to modify the iterating variable (row in the example below), it appears to create a new pointer to it rather than modifying it.
Smallest example of my problem.
c = [1,2,3]
for x in c:
x = x + 3
print(c) #returns [1,2,3], expected [4,5,6]
The above example is a trivial example of my problem. Is there a way to modify x elementwise, in-place and have the changes appear in C?
Less trivial example of my problem. I am switching all 0's to 1's and vice-versa.
A = [[1,1,0],
[1,0,1],
[0,0,0]]
for row in A:
row = list(map(lambda val: 1 - val, row))
print(A)
Expected
A = [[0,0,1],
[0,1,0],
[1,1,1]]
Returned
A = [[1,1,0],
[1,0,1],
[0,0,0]]
update:
Great answers so far. I am interested how the iterating variable (row in the second example) is linked to the iterable variable (A in the second example).
If I do the following, which reverses each sublist of A, it works perfectly.
Why does the following example, where I modify the iterating variable works but the above examples do not?
A = [[1,1,0],
[1,0,1],
[0,0,0]]
for row in A:
row.reverse()
print(A)
#returns, as expected
A = [[0, 1, 1],
[1, 0, 1],
[0, 0, 0]]
I found this in the docs: https://docs.python.org/3/tutorial/controlflow.html#for
Python’s for statement iterates over the items of any sequence (a list
or a string), in the order that they appear in the sequence.
If you need to modify the sequence you are iterating over while inside
the loop (for example to duplicate selected items), it is recommended
that you first make a copy. Iterating over a sequence does not
implicitly make a copy.
I was wrong in my first response, when iterating through a list it returns the actual items in that list. However, it seems they cannot be edited directly while they are being iterated through. This is why iterating through the integers the length of the list works.
As for why the .reverse() function works, I think it's because it is affecting a list instead of a value. I tried to use similar built in functions on nonlist datatypes like .replace() on strings and it had no effect.
All of the other list functions I tried worked: .append(), .remove(), and .reverse() as you showed. I'm not sure why this is, but I hope it clears up what you can do in for loops a bit more.
Answer to old question below:
The way you are using the for loops doesn't affect the actual list, just the temporary variable that is iterating through the list. There are a few ways you can fix this. Instead of iterating through each element you can can count up to the length of the list and modify the list directly.
c = [1,2,3]
for n in range(len(c)):
c[n] += 3
print(c)
You can also use the enumerate() function to iterate through both a counter and list items.
c = [1,2,3]
for n, x in enumerate(c):
c[n] = x + 3
print(c)
In this case, n is a counter and x is the item in the list.
Finally, you can use list comprehension to generate a new list with desired differences in one line.
c = [1, 2, 3]
d = [x + 3 for x in c]
print(d)
The usual way to poke values into an existing list in Python is to use enumerate which lets you iterate over both the indices and the values at once -- then use the indices to manipulate the list:
c = [1,2,3]
for index, value in enumerate(c):
c[index] = value + 3
For your second example you'd do almost the same:
A = [[1,1,0],
[1,0,1],
[0,0,0]]
for row in A:
for index, val in row:
row[index] = 0 if val > 0 else 1
In the second example the list objects in A become the loop variable row -- and since you're only mutating them (not assigning to them) you don't need enumerate and the index
If you want to keep it consice without creating an additional variable, you could also do:
c = [1,2,3]
print(id(c))
c[:] = [i+3 for i in c]
print(c, id(c))
Output:
2881750110600
[4, 5, 6] 2881750110600
Using list comprehension here also will work:
A = [[1,1,0],
[1,0,1],
[0,0,0]]
A = [[0 if x > 0 else 1 for x in row] for row in A]
print(A)
Output:
[[0, 0, 1],
[0, 1, 0],
[1, 1, 1]]

List of lists based on numbers in another list

Let's say I have an array
a = np.array[5, 3, 2]
and based on that array I want to return a new array in the form:
b = np.array[ [0, 1, 2, 3, 4], [0, 1, 2], [0, 1] ]
I've been trying:
for item in a:
b = np.hstack(np.arange(item))
print b
but this only gives me the elements without joining them into an array;
for item in a:
b = b.append((b[:], b[item]))
print b
but this approach blows up nicely with a:
AttributeError: 'numpy.ndarray' object has no attribute 'append'
I have tried other things, like:
b[item] = np.arange(item),
but that one complains about out-of-bounds indices.
And
b = np.zeros(len(a))
for item in np.arange(len(a)):
b[item] = np.arange(b[item])
print b
which complains with
ValueError: setting an array element with a sequence.
That last one is the one that looks more promising and, searching for some questions on this site (https://stackoverflow.com/a/13311979/531687) I know that the problem is that I am trying to fit a sequence where a value is expected, but I can't figure out my way around it.
How can I go about this?
This should work
b = [range(x) for x in a]
update
The brackets [...] here create a list and inside an iterator can be used to define the items in the list. In this case the items of of type range(x) for each item in a.
Note that there is a difference in implementation between python2 and python3 here. In python2 this actually generates a list of lists. In python3 however this generates a lists of generators (the python2 equivalent would be xrange), which is typically more efficient.

Basic python: how to increase value of item in list [duplicate]

This question already has answers here:
Why does this iterative list-growing code give IndexError: list assignment index out of range? How can I repeatedly add (append) elements to a list?
(9 answers)
Closed 4 months ago.
This is such a simple issue that I don't know what I'm doing wrong. Basically I want to iterate through the items in an empty list and increase each one according to some criteria. This is an example of what I'm trying to do:
list1 = []
for i in range(5):
list1[i] = list1[i] + 2*i
This fails with an list index out of range error and I'm stuck. The expected result (what I'm aiming at) would be a list with values:
[0, 2, 4, 6, 8]
Just to be more clear: I'm not after producing that particular list. The question is about how can I modify items of an empty list in a recursive way. As gnibbler showed below, initializing the list was the answer. Cheers.
Ruby (for example) lets you assign items beyond the end of the list. Python doesn't - you would have to initialise list1 like this
list1 = [0] * 5
So when doing this you are actually using i so you can just do your math to i and just set it to do that. there is no need to try and do the math to what is going to be in the list when you already have i. So just do list comprehension:
list1 = [2*i for i in range(5)]
Since you say that it is more complex, just don't use list comprehension, edit your for loop as such:
for i in range(5):
x = 2*i
list1[i] = x
This way you can keep doing things until you finally have the outcome you want, store it in a variable, and set it accordingly! You could also do list1.append(x), which I actually prefer because it will work with any list even if it's not in order like a list made with range
Edit: Since you want to be able to manipulate the array like you do, I would suggest using numpy! There is this great thing called vectorize so you can actually apply a function to a 1D array:
import numpy as np
list1 = range(5)
def my_func(x):
y = x * 2
vfunc = np.vectorize(my_func)
vfunc(list1)
>>> array([0, 2, 4, 6, 8])
I would advise only using this for more complex functions, because you can use numpy broadcasting for easy things like multiplying by two.
Your list is empty, so when you try to read an element of the list (right hand side of this line)
list1[i] = list1[i] + 2*i
it doesn't exist, so you get the error message.
You may also wish to consider using numpy. The multiplication operation is overloaded to be performed on each element of the array. Depending on the size of your list and the operations you plan to perform on it, using numpy very well may be the most efficient approach.
Example:
>>> import numpy
>>> 2 * numpy.arange(5)
array([0, 2, 4, 6, 8])
I would instead write
for i in range(5):
list1.append(2*i)
Yet another way to do this is to use the append method on your list. The reason you're getting an out of range error is because you're saying:
list1 = []
list1.__getitem__(0)
and then manipulate this item, BUT that item does not exist since your made an empty list.
Proof of concept:
list1 = []
list1[1]
IndexError: list index out of range
We can, however, append new stuff to this list like so:
list1 = []
for i in range(5):
list1.append(i * 2)

Categories

Resources