I was hoping for an elegant or effective way to multiply sequences of integers (or floats).
My first thought was to try (1, 2, 3) * (1, 2, 2) would result (1, 4, 6), the products of the individual multiplications.
Though python isn't preset to do that for sequences. Which is fine, I wouldn't really expect it to. So what's the pythonic way to multiply (or possibly other arithmetic operations as well) each item in two series with and to their respective indices?
A second example (0.6, 3.5) * (4, 4) = (2.4, 14)
The simplest way is to use zip function, with a generator expression, like this
tuple(l * r for l, r in zip(left, right))
For example,
>>> tuple(l * r for l, r in zip((1, 2, 3), (1, 2, 3)))
(1, 4, 9)
>>> tuple(l * r for l, r in zip((0.6, 3.5), (4, 4)))
(2.4, 14.0)
In Python 2.x, zip returns a list of tuples. If you want to avoid creating the temporary list, you can use itertools.izip, like this
>>> from itertools import izip
>>> tuple(l * r for l, r in izip((1, 2, 3), (1, 2, 3)))
(1, 4, 9)
>>> tuple(l * r for l, r in izip((0.6, 3.5), (4, 4)))
(2.4, 14.0)
You can read more about the differences between zip and itertools.izip in this question.
A simpler way would be:
from operator import mul
In [19]: tuple(map(mul, [0, 1, 2, 3], [10, 20, 30, 40]))
Out[19]: (0, 20, 60, 120)
If you are interested in element-wise multiplication, you'll probably find that many other element-wise mathematical operations are also useful. If that is the case, consider using the numpy library.
For example:
>>> import numpy as np
>>> x = np.array([1, 2, 3])
>>> y = np.array([1, 2, 2])
>>> x * y
array([1, 4, 6])
>>> x + y
array([2, 4, 5])
With list comprehensions the operation could be completed like
def seqMul(left, right):
return tuple([value*right[idx] for idx, value in enumerate(left)])
seqMul((0.6, 3.5), (4, 4))
A = (1, 2, 3)
B = (4, 5, 6)
AB = [a * b for a, b in zip(A, B)]
use itertools.izip instead of zip for larger inputs.
Related
How to go about finding the number of non-coprimes in a given array?
Suppose
a = [2, 5, 6, 7]
b = [4, 9, 10, 12]
Then the number of non-coprimes will be 3, since You can remove:
(2, 4)
(5, 10)
(6, 9)
n = int(input())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
count = 0
len_a = len(a)
len_b = len(b)
for i in range(len_a):
for j in range(len_b):
x = a[i]
y = b[j]
if(math.gcd(x,y) != 1):
count += 1
print(count)
This is in reference to :https://www.hackerrank.com/challenges/computer-game/problem
I am receiving 8 as output.
Why do you expect the answer to be 3?
You're pairing 5 and 10, so you're obviously looking at pairs of elements from a and b disregarding their position.
Just print out the pairs and you'll see why you're getting 8...
import math
from itertools import product
a=[2, 5, 6, 7]
b=[4, 9, 10, 12]
print(sum([math.gcd(x, y) != 1 for x, y in product(a, b)])) # 8
print([(x, y) for x, y in product(a, b) if math.gcd(x, y) != 1]) # the pairs
Update: After reading the problem the OP is trying to handle, it's worth pointing out that the expect output (3) is the answer to a different question!
Not how many pairs of elements are not coprime, but rather how many non-coprime pairs can be removed without returning them into the arrays.
This question is actually an order of magnitude more difficult, and is not a matter of fixing one's code, but rather about giving the actual problem a lot of mathematical and algorithmic thought.
See some discussion here
Last edit, a sort-of solution, albeit an extremely inefficient one. The only point is to suggest some code that can help the OP understand the point of the original question, by seeing some form of solution, however low-quality or bad-runtime it is.
import math
from itertools import product, permutations
n = 4
def get_pairs_list_not_coprime_count(pairs_list):
x, y = zip(*pairs_list)
return min(i for i in range(n) if math.gcd(x[i], y[i]) == 1) # number of pairs before hitting a coprime pair
a = [2, 5, 6, 7]
b = [4, 9, 10, 12]
a_perms = permutations(a) # so that the pairing product with b includes all pairing options
b_perms = permutations(b) # so that the pairing product with a includes all pairing options
pairing_options = product(a_perms, b_perms) # pairs-off different orderings of a and b
actual_pairs = [zip(*p) for p in pairing_options] # turn a pair of a&b orderings into number-pairs (for each of the orderings possible as realized by the product)
print(max(get_pairs_list_not_coprime_count(pairs_list) for pairs_list in actual_pairs)) # The most pairings managed over all possible options: 3 for this example
I believe the answer should be 8 itself. Out of the 4*4 possible combinations of numbers that you are comparing, there are 8 coprimes and 8 non-coprimes.
Here is an implementation of the code with the gcd function without using math and broadcasting to avoid multiple loops.
import numpy
a = '2 5 6 7'
b = '4 9 10 12'
a = np.array(list(map(int,a.split())))
b = np.array(list(map(int,b.split())))
def gcd(p,q):
while q != 0:
p, q = q, p%q
return p
def is_coprime(x, y):
return gcd(x, y) == 1
is_coprime_v = np.vectorize(is_coprime)
compare = is_coprime_v(a[:, None], b[None, :])
noncoprime_pairs = [(a[i],b[j]) for i,j in np.argwhere(~compare)]
coprime_pairs = [(a[i],b[j]) for i,j in np.argwhere(compare)]
print('non-coprime',noncoprime_pairs)
print('coprime',coprime_pairs)
non-coprime [(2, 4), (2, 10), (2, 12), (5, 10), (6, 4), (6, 9), (6, 10), (6, 12)]
coprime [(2, 9), (5, 4), (5, 9), (5, 12), (7, 4), (7, 9), (7, 10), (7, 12)]
Same solution but using the math.gcd() -
import math
import numpy
a = '2 5 6 7'
b = '4 9 10 12'
a = np.array(list(map(int,a.split())))
b = np.array(list(map(int,b.split())))
def f(x,y):
return math.gcd(x, y) == 1
fv = np.vectorize(f)
compare = fv(a[:, None], b[None, :])
noncoprime_pairs = [(a[i],b[j]) for i,j in np.argwhere(~compare)]
print(noncoprime_pairs)
[(2, 4), (2, 10), (2, 12), (5, 10), (6, 4), (6, 9), (6, 10), (6, 12)]
If you are looking for the answer to be 3 in your example, I would assume you are counting the number of values in a that have at least one non-coprime in b.
If that is the case you could do it like this:
from math import gcd
def nonCoprimes(A,B):
return sum(any(gcd(a,b)>1 for b in B) for a in A)
print(nonCoprimes([2,5,6,7],[4,9,10,12])) # 3
So, for each value in a check if there are any values of b that don't have a gcd of 1 with the value in a
Is there a way to return various sums of a list of integers? Pythonic or otherwise.
For e.g. various sum totals from [1, 2, 3, 4] would produce 1+2=3, 1+3=4, 1+4=5, 2+3=5, 2+4=6, 3+4=7. Integers to be summed could by default be stuck to two integers only or more I guess.
Can't seem to wrap my head around how to tackle this and can't seem to find an example or explanation on the internet as they all lead to "Sum even/odd numbers in list" and other different problems.
You can use itertools.combinations and sum:
from itertools import combinations
li = [1, 2, 3, 4]
# assuming we don't need to sum the entire list or single numbers,
# and that x + y is the same as y + x
for sum_size in range(2, len(li)):
for comb in combinations(li, sum_size):
print(comb, sum(comb))
outputs
(1, 2) 3
(1, 3) 4
(1, 4) 5
(2, 3) 5
(2, 4) 6
(3, 4) 7
(1, 2, 3) 6
(1, 2, 4) 7
(1, 3, 4) 8
(2, 3, 4) 9
is this what you are looking for ?
A=[1,2,3,4] for i in A: for j in A: if i!=j: print(i+j)
how can I run the for loop for "n" number of times
c=[1, 2, 5, 10, 25, 50]
m=50
n=3
count=0
for i in c:
for ii in c:
for iii in c:
if (iii*ii*i)==m:
count+=1
print(count)
Count =18
So in the above example, I'm running it 3 times manually. I want to run it for 3 times automatically using any loop function.
Here's a solution using itertools.product and functools.reduce:
import itertools, functools
c=[1, 2, 5, 10, 25, 50]
m=50
l = [c] * 3
for i in itertools.product(*l):
if functools.reduce(lambda x,y:x*y, i) == m:
print(i)
The output is:
(1, 1, 50)
(1, 2, 25)
(1, 5, 10)
(1, 10, 5)
(1, 25, 2)
(1, 50, 1)
(2, 1, 25)
...
How does it work?
itertools.product: takes a list of iterables (lists in this case), and produces a cross product of all these lists. In our case, we're creating all the combinations of
[1, 2, 5, ...] x [1, 2, 5, ...] x [1, 2, 5 ..]
The result is (1, 1, 1), (1, 1, 2), (1, 1, 5), etc. Iterating over these combinations is equivalent to a loop within a loop within a loop.
Then, for every combination, we use reduce to product the multiplication of these three numbers. Reduce takes a list and a function, and applies this function to all the items in the list - for example 1 * 2 * 5.
Say I have an array of numbers
np.array(([1, 4, 2, 1, 2, 5]))
And I want to compute the sum over a list of slices
((0, 3), (2, 4), (2, 6))
Giving
[(1 + 4 + 2), (2 + 1), (2 + 1 + 2 + 5)]
Is there a nice way to do this in numpy?
Looking for something equivalent to
def reduce(a, ranges):
np.array(list(np.sum(a[low:high]) for (low, high) in ranges))
Seems like there is probably some fancy numpy way to do this though. Anyone know?
One way is to use np.add.reduceat. If a is the array of values [1, 4, 2, 1, 2, 5]:
>>> np.add.reduceat(a, [0,3, 2,4, 2])[::2]
array([ 7, 3, 10], dtype=int32)
Here the slice indexes are passed in a list and are summed to return [ 7, 1, 3, 2, 10] (i.e. the sums of a[0:3], a[3:], a[2:4], a[4:], a[2:]). We only want every other element from this array.
Longer alternative approach...
The fact that the slices are of different lengths makes this slightly trickier to vectorise in NumPy, but here is one way you approach the problem.
Given an array of values and an array of slices to make...
a = np.array(([1, 4, 2, 1, 2, 5]))
slices = np.array([(0, 3), (2, 4), (2, 6)])
...create a mask-like array z that, for each slice, will be used to "zero-out" the values from a we don't want to sum:
z = np.zeros((3, 6))
s1 = np.arange(6) >= s[:, 0][:,None]
s2 = np.arange(6) < s[:, 1][:,None]
z[s1 & s2] = 1
Then you can do:
>>> (z * a).sum(axis=1)
array([ 7., 3., 10.])
A quick %timeit shows this is slightly faster than the list comprehension, even though we had to construct z and z * a. If slices is made to be of length 3000, this method is around 40 times quicker.
However note that the array z will be of shape (len(slices), len(a)) which may not be as practical if a or slices are both very long - an iterative approach might be preferred to avoid large temporary arrays in memory.
For example
X=[5,6,2,3,1]
Y=[7,2,3,4,6]
I sort X:
X=[1,2,3,5,6]
But I want the same relative sort applied to Y so the numbers stay in the same positions relative to each other as before:
Y=[6,3,4,7,2]
I hope this makes sense!
Usually, you do a zip-sort-unzip for this
>>> X = [5,6,2,3,1]
>>> Y = [7,2,3,4,6]
Now sort them together:
>>> sorted(zip(X,Y))
[(1, 6), (2, 3), (3, 4), (5, 7), (6, 2)]
Pair that with a "unzip" (zip(*...))
>>> zip(*sorted(zip(X,Y)))
[(1, 2, 3, 5, 6), (6, 3, 4, 7, 2)]
which you could unpack:
>>> X,Y = zip(*sorted(zip(X,Y)))
>>> X
(1, 2, 3, 5, 6)
>>> Y
(6, 3, 4, 7, 2)
Now you have tuple instead of list objects, but if you really need to, you can convert it back.
As pointed out in the comments, this does introduce a very slight dependence on the second list in the sort: Consider the lists:
X = [1,1,5,7] #sorted already
Y = [2,1,4,6] #Not already sorted.
With my "recipe" above, at the end of the day, you'll get:
X = (1,1,5,7)
Y = (1,2,4,6)
which might be unexpected. To fix that, you could pass a key argument to sorted:
from operator import itemgetter
X,Y = zip(*sorted(zip(X,Y),key=itemgetter(0)))
Demo:
>>> X
[1, 1, 5, 7]
>>> Y
[2, 1, 4, 6]
>>> XX,YY = zip(*sorted(zip(X,Y)))
>>> XX
(1, 1, 5, 7)
>>> YY
(1, 2, 4, 6)
>>> from operator import itemgetter
>>> XX,YY = zip(*sorted(zip(X,Y),key=itemgetter(0)))
>>> XX
(1, 1, 5, 7)
>>> YY
(2, 1, 4, 6)
Another idea:
>>> d = dict(zip(Y, X))
>>> sorted(Y, key=d.get)
[6, 3, 4, 7, 2]
You just use corresponding values in X as keys while sorting Y.
Here is a way to preserve order even if there are duplicate items:
def argsort(seq):
'''
>>> seq = [1,3,0,4,2]
>>> index = argsort(seq)
[2, 0, 4, 1, 3]
Given seq and the index, you can construct the sorted seq:
>>> sorted_seq = [seq[x] for x in index]
>>> assert sorted_seq == sorted(seq)
Given the sorted seq and the index, you can reconstruct seq:
>>> assert [sorted_seq[x] for x in argsort(index)] == seq
'''
return sorted(range(len(seq)), key=seq.__getitem__)
X = (1,1,5,7)
Y = (1,2,4,6)
index = argsort(X)
print([Y[i] for i in index])
yields
[1, 2, 4, 6]
Regarding speed, using_argsort appears to be faster than using_zip or using_dict:
def using_argsort():
index = argsort(X)
return [X[i] for i in index], [Y[i] for i in index]
def using_zip():
return zip(*sorted(zip(X,Y), key = operator.itemgetter(0)))
def using_dict():
d = dict(zip(Y,X))
return sorted(X), sorted(Y, key = d.get)
X = [5,6,2,3,1]*1000
Y = [7,2,3,4,6]*1000
In [18]: %timeit using_argsort()
1000 loops, best of 3: 1.55 ms per loop
In [19]: %timeit using_zip()
1000 loops, best of 3: 1.65 ms per loop
In [21]: %timeit using_dict()
100 loops, best of 3: 2 ms per loop
I'm assuming x and y have the same number of elements. when sorting x, everytime you swap two value, say, x.swap(i,j), you do y.swap(i,j) as well. By the way, I don't know python so this is not python syntax.