Alternate between add and subtract on a list - python

Does anybody here know how to sum elements from a list alternating the sign? I tried this:
i = 40
suma = 0
sign = 1
while i <= 100:
suma = suma + sign * i
sign = sign * -1
i = i + 1
print(suma)
but I don't know how to improve it into a function and add a list. Please help.

def add_alternate_sign(a_list):
total = 0
for i, number in enumerate(a_list):
total += number * (-1) ** i
print(total)
numbers = [1,2,3,4,5]
add_alternate_sign(numbers)

You can use numpy for faster vectorized operation. Using your example:
import numpy as np
x = np.arange(1, 101)
np.sum(x*(-1)**x)
For any arbitrary list:
x = [5,10,20,100,2, 1]
y = np.array(x)
np.sum(y*(-1)**np.arange(len(x)))

You can use itertools.cycle to loop continuously through 1, -1 for the multiplier. For example:
import itertools
def alternate_sum(a):
"""
sums elements of a list with alternating multiplier +1, -1, +1, -1, ...
"""
return sum(x * s for x, s in zip(a, itertools.cycle([1, -1])))
print(alternate_sum([10,11,12,14,15]))
This approach is not limited to special case of +1 and -1; it could be used for any sequence of multipliers (of any length).

Another solution:
def alternate_sum(a):
"""
return sum of the elements of a with alternating multiplier 1, -1, ...
"""
return sum(x * (-1)**i for i, x in enumerate(a))
Here i is the index number 0, 1, 2, ...., so (-1)**i is the sequence 1, -1, 1, ...

You can use slices to sum alternate entries in a list:
def altsum(v):
return sum(v[0::2]) - sum(v[1::2])
The only downside is that the slice creates two new lists, one with the even indexes and the other with the odd indexes. If your original list is big, that's a lot of wasted storage (and some time).
So you could use itertools.islice to avoid creating the lists, although it's unlikely to turn out to be faster since islice is not optimised for lists, as far as I know.
from itertools import islice
def altsum(v):
return sum(islice(v, 0, None, 2)) - sum(islice(v, 1, None, 2))
But that won't work for arbitrary iterables because it relies on being able to iterate the argument twice. To construct the alternating sum of an arbitrary iterable, you can use itertools.zip_longest (or itertools.izip_longest in Python 2) along with functools.reduce (or reduce in Python 2):
from functools import reduce
from itertools import zip_longest
def altsum(v):
it = iter(v)
return reduce(lambda accum, pn: accum + pn[0] - pn[1],
zip_longest(it, it, fillvalue=0), 0)

Related

Finding two integers that multiply to 20. Can I make this code more "pythonic"?

I did this code that finds two integers in a said list (in this case [2,4,5,1,6,40,-1]) that multiply to twenty. I got a little stuck in the beginning, but adding a function to it solved my problems. I showed this code to a friend of mine who's a programmer and he said I could make this code more "pythonic", but I have no clue how.
Here's the code:
num_list = [2,4,5,1,6,40,-1]
def get_mult_num(given_list):
for i in given_list:
for j in range(i+1, len(given_list)): #for j not to be == i and to be in the list
mult_two_numbers = i * j
if mult_two_numbers == 20:
return i,j
print(get_mult_num(num_list))
I don't necessarily think it is 'unpythonic', you are using standard Python idioms to loop over your data and produce a single result or None. The term Pythonic is nebulous, a subject marred in "I know it when I see it" parameters.
Not that you produced a correct implementation. While i loops over given_numbers, j loops over an integer from i + 2 through to len(given_numbers), mixing values from given_list with indices? For your sample input, you are taking j from the half-open ranges [4, 7), [6, 7), [7, 7) (empty), [3, 7), [8, 7) (empty), [42, 7) (empty) and [1, 7), respectively. That it produces the correct answer at all is luck, not due to correctness; if you give your function the list [2, 10], it'll not find a solution! You want to loop over given_numbers again, limited with slicing, or generate indices starting at the current index of i, but then your outer loop needs to add a enumerate() call too:
for ii, i in enumerate(given_numbers):
for j in given_numbers[ii + 1:]:
# ...
or
for ii, i in enumerate(given_numbers):
for jj in range(ii + 1, len(given_numbers)):
j = given_numbers[jj]
# ...
All this is not nearly as efficient as it can be; the Python standard library offers you the tools to generate your i, j pairs without a nested for loop or slicing or other forms of filtering.
Your double loop should generate combinations of the integer inputs, so use the itertools.combinations() object to generate unique i, j pairs:
from itertools import combinations
def get_mult_num(given_list):
return [(i, j) for i, j in combinations(given_list, 2) if i * j == 20]
This assumes there can be zero or more such solutions, not just a single solution.
If you only ever need the first result or None, you can use the next() function:
def get_mult_num(given_list):
multiplies_to_20 = (
(i, j) for i, j in combinations(given_list, 2)
if i * j == 20)
return next(multiplies_to_20, None)
Next, rather than produce all possible combinations, you may want to invert the problem. If you turn given_list into a set, you can trivially check if the target number 20 can be divided cleanly without remainder by any of your given numbers and where the result of the division is larger and is also an integer in the set of numbers. That gives you an answer in linear time.
You can further limit the search by dividing with numbers smaller than the square root of the target value, because you won't find a larger value to match in your input numbers (given a number n and it's square root s, by definition s * (s + 1) is going to be larger than n).
If we add an argument for the target number to the function and make it a generator function, then you get:
def gen_factors_for(target, numbers):
possible_j = set(numbers)
limit = abs(target) ** 0.5
for i in numbers:
if abs(i) < limit and target % i == 0:
j = target // i
if j in possible_j and abs(j) > abs(i):
yield i, j
This approach is a lot faster than testing all permutations, especially if you need to find all possible factors. Note that I made both functions generators here to even out the comparisons:
>>> import random, operator
>>> from timeit import Timer
>>> def gen_factors_for_division(target, numbers):
... possible_j = set(numbers)
... limit = abs(target) ** 0.5
... for i in numbers:
... if abs(i) < limit and target % i == 0:
... j = target // i
... if j in possible_j and abs(j) > abs(i):
... yield i, j
...
>>> def gen_factors_for_combinations(target, given_list):
... return ((i, j) for i, j in combinations(given_list, 2) if i * j == target)
...
>>> numbers = [random.randint(-10000, 10000) for _ in range(100)]
>>> targets = [operator.mul(*random.sample(set(numbers), 2)) for _ in range(5)]
>>> targets += [t + random.randint(1, 100) for t in targets] # add likely-to-be-unsolvable numbers
>>> for (label, t) in (('first match:', 'next({}, None)'), ('all matches:', 'list({})')):
... print(label)
... for f in (gen_factors_for_division, gen_factors_for_combinations):
... test = t.format('f(t, n)')
... timer = Timer(
... f"[{test} for t in ts]",
... 'from __main__ import targets as ts, numbers as n, f')
... count, total = timer.autorange()
... print(f"{f.__name__:>30}: {total / count * 1000:8.3f}ms")
...
first match:
gen_factors_for_division: 0.219ms
gen_factors_for_combinations: 4.664ms
all matches:
gen_factors_for_division: 0.259ms
gen_factors_for_combinations: 3.326ms
Note that I generate 10 different random targets, to try to avoid a lucky best-case-scenario hit for either approach.
[(i,j) for i in num_list for j in num_list if i<j and i*j==20]
This is my take on it, which uses enumerate:
def get_mult_num(given_list):
return [
item1, item2
for i, item1 in enumerate(given_list)
for item2 in given_list[:i]
if item1*item2 == 20
]
I think your friend may be hinting towards using comprehensions when it makes the code cleaner (sometimes it doesn't).
I can think of using list-comprehension. This also helps to find multiple such-pairs if they exist in the given list.
num_list = [2,4,5,1,6,40,-1]
mult_num = [(num_list[i],num_list[j]) for i in range(len(num_list)) for j in range(i+1, len(num_list)) if num_list[i]*num_list[j] == 20]
print mult_num
Output:
[(4, 5)]
I came up with this. It reverses the approach a little bit, in that it searches in num_list for the required pair partner that the iteration value val would multiply to 20 with. This makes the code easier and needs no imports, even if it's not the most efficient way.
for val in num_list:
if 20 / val in num_list:
print(val, int(20/val))
You could make it more pythonic by using itertools.combinations, instead of nested loops, to find all pairs of numbers. Not always, but often iterating over indices as in for i in range(len(L)): is less pythonic than directly iterating over values as in for v in L:.
Python also allows you to make your function into a generator via the yield keyword so that instead of just returning the first pair that multiplies to 20, you get every pair that does by iterating over the function call.
import itertools
def factors(x, numbers):
""" Generate all pairs in list of numbers that multiply to x.
"""
for a, b in itertools.combinations(numbers, 2):
if a * b == x:
yield (a, b)
numbers = [2, 4, 5, 1, 6, 40, -1]
for pair in factors(20, numbers):
print(pair)

sum of a List Function difference

This function works only when I call with:
def prodo(L):
sum = 1
for i in range(0, len(L)):
sum = L[i] * sum
return sum
these lines:
d = prodo([3, -1, 4, -1, -5])
print(d)
But the following function using L returns 375 and not -60
def prod(L):
sum = 1
for i in L:
if i>=len(L):
break
sum = L[i] * sum
return sum
Question: What am I doing wrong and why?
You can do it like this:
from functools import reduce
product = reduce((lambda x, y: x * y), [3, -1, 4, -1, -5])
print(product)
This outputs -60
Optionally, you can declare any list
l = [3, -1, 4, -1, -5]
and then call
product = reduce((lambda x, y: x * y), l)
This function doesn't work because you are not iterating over the array like this:
for i in range(len(L)):
// now `i` will have values 0,1,2,3,...,len(L)-1
instead you are iterating like this:
d = prodo([3, -1, 4, -1, -5])
for i in L:
// now i will take values 3,-1,4,-1,-5
With this method you cannot use the if i == len(L): to determine if you are at the end of the array, because i is not a number index but rather the value at a certain index.
So the break will execute any time a number from the array is higher or equal to the length of the array.
def prod(L):
sum = 1
for i in L:
if i>=len(L):
break
sum = L[i] * sum
return sum
In the def prod() you are using the i as value of the elements of the list.
So when you do:
def prod(L)
for i in L: # i = 3, -1, 4, -1, -5
When you write:
for i in range(0,L): # i =0, 1, 2, 3, 4
These are the places you need to change the code.
As #Skam proposes in comments, you will know the difference if you try to print i in your second function. When you use the below syntax:
for i in L
i does not represent a numeric value that starts from 0 and goes to the length of your list. But it represents the item itself. As an example, consider the below code:
L = [1,3,4,5,6]
for i in L:
print i
The output of this code is:
1
3
4
5
6
So in your second function when you are trying to do L[i], you are not actually getting the current iteration number and trying to access it in your list, but you are trying to access the value that has the index of the current value of i , considering the mentioned code, if I try to do L[i] the first iteration will print 3 since it has the index of 1 which is the first element.

Looping through all item except itself

I am trying to find the item in a list with highest number of occurrence.
For this, I am trying to compare every item in list with all other items in list and increasing count's value by 1 each time a match is found.
def findInt(array):
count = []
count = [1 for i in range(0,len(array))]
for i,item in enumerate(array):
if (array[i] == array[i:]): #How can I compare it with all items except itself?
count[i]+=1
return max(count), count.index(max(count))
findInt(array=[1,2,3])
My question is "how do I compare the item with all other items except itself"?
use collections.Counter which has a most_common function.
import collections
def findInt(array):
c = collections.Counter(array)
return c.most_common(1)
DEMO
>>> import collections
>>> array=[1,2,3,1,2,3,2]
>>> c = collections.Counter(array)
>>> c.most_common(1)
[(2, 3)]
DOC
class collections.Counter([iterable-or-mapping])
A Counter is a dict subclass for counting hashable objects. It is an unordered collection where elements are stored as dictionary keys and their counts are stored as dictionary values. Counts are allowed to be any integer value including zero or negative counts. The Counter class is similar to bags or multisets in other languages.
most_common([n])
Return a list of the n most common elements and their counts from the most common to the least. If n is omitted or None, most_common() returns all elements in the counter. Elements with equal counts are ordered arbitrarily:
Whilst there exist many better ways of solving this problem, for instance as indicated in #zwer's comment to your question, here's how I would solve exactly what you're asking:
# O(n ** 2)
def find_int(array):
n = len(array)
count = [1 for i in range(n)]
for i in range(n):
for j in range(n):
if i == j: continue
if array[i] == array[j]:
count[i] += 1
return max(count), count.index(max(count))
# Worse than O(n ** 2)
def find_int_using_slice(array):
n = len(array)
count = [1 for i in range(n)]
for i in range(n):
for a_j in array[0:i] + array[i+1:]:
if array[i] == a_j:
count[i] += 1
return max(count), count.index(max(count))
print(find_int_using_slice([1,2,3,1,2,3,2]))
We're using a nested for-loop here and using continue to skip the iteration when the two indexes are the same.
Unless specifically for the purpose of learning, please consider using built-ins for common tasks this, as they are well implemented, tested, optimised, etc.
There are many potential solutions, but here are the two I'd recommend, depending on your application's requirements: 1) sort and count in a single pass from left to right: O(n * log(n)) and losing the original ordering, or 2) use a dictionary to maintain the counts, requiring only a single pass from left to right: O(n) but using more memory. Of course the better decision would be to use in-built methods which are highly optimised, but that's your call
Updated answer to reflect OP not wanting to use collections.Counter
Using setdefault to prime the counter for first occurrences, then increment the counter. Then you can use max with a key to find the most common item.
def most_common(ar):
y = {}
for item in ar:
y.setdefault(item, 0)
y[item] += 1
return max(y.items(), key=lambda x: x[1])
array = [1, 2, 1, 1, 2, 1, 3, 3, 1]
most_common(array)
(1, 5) # (Most common item, occurrences of item)
def findInt(array):
count = []
for i in range(len(array)):
count.append(array.count(array[i]))
return max(count), count.index(max(count))
print(findInt(array=[1,2,3,1,2,3,2]))
Fine, I'll bite - given that memory is cheap, hashing is preferred over looping. I'd reckon one of the most performant ways would be to use a temporary registry:
def findInt(array):
occurrences = dict.fromkeys(array, 0)
for element in array:
occurrences[element] += 1
items = occurrences.values()
max_occurences = max(items)
return occurrences.keys()[items.index(max_occurences)], max_occurences
Returns a tuple of the element that occurs the most, and the number of times it occurs.
Actually, let's optimize it even more - here's a pure O(N) solution with no extra list building and searching:
def findInt(array):
occurrences = dict.fromkeys(array, 0)
candidate = array[0]
occurs = 0
for element in array:
value = occurrences[element] + 1
occurrences[element] = value
if value > occurs:
candidate = element
occurs = value
return candidate, occurs
Counter is ideal for counting frequencies of items in an iterable. Alternatively, you can loop once with a defaultdict.
import operator as op
import collections as ct
def findInt(array):
dd = ct.defaultdict(int)
for item in array:
dd[item] += 1
return dd
# Frequencies
array = [1, 2, 1, 1, 2, 1, 3, 3, 1]
freq = findInt(array)
freq
# Out: defaultdict(int, {1: 5, 2: 2, 3: 2})
# Maximum key-value pair (2 options)
{k:v for k,v in freq.items() if k == max(freq, key=lambda x: freq[x])}
# Out: {1: 5}
max({k:v for k,v in freq.items()}.items(), key=op.itemgetter(-1))
# Out: (1: 5)

Aesthetic way of appending to a list in Python?

When appending longer statements to a list, I feel append becomes awkward to read. I would like a method that would work for dynamic list creation (i.e. don't need to initialize with zeros first, etc.), but I cannot seem to come up with another way of doing what I want.
Example:
import math
mylist = list()
phi = [1,2,3,4] # lets pretend this is of unknown/varying lengths
i, num, radius = 0, 4, 6
while i < num:
mylist.append(2*math.pi*radius*math.cos(phi[i]))
i = i + 1
Though append works just fine, I feel it is less clear than:
mylist[i] = 2*math.pi*radius*math.cos(phi[i])
But this does not work, as that element does not exist in the list yet, yielding:
IndexError: list assignment index out of range
I could just assign the resulting value to temporary variable, and append that, but that seems ugly and inefficient.
You don;t need an existing list and append to it later. Just use list comprehension
List comprehension,
is fast,
easy to comprehend,
and can easily be ported as a generator expression
>>> import math
>>> phi = [1,2,3,4]
>>> i, num, radius = 0, 4, 6
>>> circum = 2*math.pi*radius
>>> mylist = [circum * math.cos(p) for p in phi]
Reviewing your code, here are some generic suggestions
Do not compute a known constant in an iteration
while i < num:
mylist.append(2*math.pi*radius*math.cos(phi[i]))
i = i + 1
should be written as
circum = 2*math.pi
while i < num:
mylist.append(circum*math.cos(phi[i]))
i = i + 1
Instead of while use for-each construct
for p in phi:
mylist.append(circum*math.cos(p))
If an expression is not readable, break it into multiple statements, after all readability counts in Python.
In this particular case you could use a list comprehension:
mylist = [2*math.pi*radius*math.cos(phi[i]) for i in range(num)]
Or, if you're doing this sort of computations a lot, you could move away from using lists and use NumPy instead:
In [78]: import numpy as np
In [79]: phi = np.array([1, 2, 3, 4])
In [80]: radius = 6
In [81]: 2 * np.pi * radius * np.cos(phi)
Out[81]: array([ 20.36891706, -15.68836613, -37.32183785, -24.64178397])
I find this last version to be the most aesthetically pleasing of all. For longer phi it will also be more performant than using lists.
mylist += [2*math.pi*radius*math.cos(phi[i])]
you can use list concatenation, but append is twice as fast according to this:
import math
mylist = list()
phi = [1,2,3,4] # lets pretend this is of unknown/varying lengths
i, num, radius = 0, 4, 6
while i < num:
mylist += [(2*math.pi*radius*math.cos(phi[i]))]
i = i + 1

How does reduce function work?

As far as I understand, the reduce function takes a list l and a function f. Then, it calls the function f on first two elements of the list and then repeatedly calls the function f with the next list element and the previous result.
So, I define the following functions:
The following function computes the factorial.
def fact(n):
if n == 0 or n == 1:
return 1
return fact(n-1) * n
def reduce_func(x,y):
return fact(x) * fact(y)
lst = [1, 3, 1]
print reduce(reduce_func, lst)
Now, shouldn't this give me ((1! * 3!) * 1!) = 6? But, instead it gives 720. Why 720? It seems to take the factorial of 6 too. But, I need to understand why.
Can someone explains why this happens and a work-around?
I basically want to compute the product of factorials of all the entries in the list.
The backup plan is to run a loop and compute it. But, I would prefer using reduce.
The other answers are great. I'll simply add an illustrated example that I find pretty good to understand reduce():
>>> reduce(lambda x,y: x+y, [47,11,42,13])
113
will be computed as follows:
(Source) (mirror)
The easiest way to understand reduce() is to look at its pure Python equivalent code:
def myreduce(func, iterable, start=None):
it = iter(iterable)
if start is None:
try:
start = next(it)
except StopIteration:
raise TypeError('reduce() of empty sequence with no initial value')
accum_value = start
for x in iterable:
accum_value = func(accum_value, x)
return accum_value
You can see that it only makes sense for your reduce_func() to apply the factorial to the rightmost argument:
def fact(n):
if n == 0 or n == 1:
return 1
return fact(n-1) * n
def reduce_func(x,y):
return x * fact(y)
lst = [1, 3, 1]
print reduce(reduce_func, lst)
With that small revision, the code produces 6 as you expected :-)
Your function calls fact() on both arguments. You are calculating ((1! * 3!)! * 1!). The workaround is to only call it on only the second argument, and pass reduce() an initial value of 1.
From the Python reduce documentation,
reduce(function, sequence) returns a single value constructed by calling the (binary) function on the first two items of the sequence, then on the result and the next item, and so on.
So, stepping through. It computes reduce_func of the first two elements, reduce_func(1, 3) = 1! * 3! = 6. Then, it computes reduce_func of the result and the next item: reduce_func(6, 1) = 6! * 1! = 720.
You missed that, when the result of the first reduce_func call is passed as input to the second, it's factorialized before the multiplication.
Ok, got it:
I need to map the numbers to their factorials first and then call reduce with multiply operator.
So, this would work:
lst_fact = map(fact, lst)
reduce(operator.mul, lst_fact)
You could also implement factorial using reduce.
def factorial(n):
return(reduce(lambda x,y:x*y,range(n+1)[1:]))
Beyond the trivial examples, here is one where I find reduce to be actually quite useful:
Imagine an iterable of ordered int values, often with some runs of contiguous values, and that we'd like to "summarize" it as a list of tuples representing ranges. (Note also that this iterable could be a generator of a very long sequence --another reason to use reduce and not some operation on an in-memory collection).
from functools import reduce
def rle(a, b):
if a and a[-1][1] == b:
return a[:-1] + [(a[-1][0], b + 1)]
return a + [(b, b + 1)]
reduce(rle, [0, 1, 2, 5, 8, 9], [])
# [(0, 3), (5, 6), (8, 10)]
Notice the use of a proper initial value ([] here) for reduce.
Corner cases handled as well:
reduce(rle, [], [])
# []
reduce(rle, [0], [])
# [(0, 1)]
Well, first of all, your reduce_func doesn't have the structure of a fold; it doesn't match your description of a fold (which is correct).
The structure of a fold is: def foldl(func, start, iter): return func(start, foldl(func, next(iter), iter)
Now, your fact function doesn't operate on two elements - it just calculates factorial.
So, in sum, you're not using a fold, and with that definition of factorial, you don't need to.
If you do want to play around with factorial, check out the y-combinator: http://mvanier.livejournal.com/2897.html
If you want to learn about folds, look at my answer to this question, which demonstrates its use to calculate cumulative fractions: creating cumulative percentage from a dictionary of data
Reduce executes the function in parameter#1 successively through the values provided by the iterator in parameter#2
print '-------------- Example: Reduce(x + y) --------------'
def add(x,y): return x+y
x = 5
y = 10
import functools
tot = functools.reduce(add, range(5, 10))
print 'reduce('+str(x)+','+str(y)+')=' ,tot
def myreduce(a,b):
tot = 0
for i in range(a,b):
tot = tot+i
print i,tot
print 'myreduce('+str(a)+','+str(b)+')=' ,tot
myreduce(x,y)
print '-------------- Example: Reduce(x * y) --------------'
def add(x,y): return x*y
x = 5
y = 10
import functools
tot = functools.reduce(add, range(5, 10))
print 'reduce('+str(x)+','+str(y)+')=' ,tot
def myreduce(a,b):
tot = 1
for i in range(a,b):
tot = tot * i
print i,tot
print 'myreduce('+str(a)+','+str(b)+')=' ,tot
myreduce(x,y)

Categories

Resources