Reputation: 1339
I'm trying to write code for a modulus 11 but I'm failing to make it more python-like.
As of now I'm using a weight and increasing it, checking when it reaches a number and then set it to it's original value.
Let's say I have a list of numbers
1..20
and I'd like to multiply them by 2,3,4,5,6,7,8,9,2,3,4,etc
so that each index would be multiplied by an increasing number.
1x2, 2x3, 3x4, 4x5, 5x6, 6x7, 7x8, 8x9, 9x2, 10x3, etc..
Is there an elegant way to do this?
Unelegant way:
def mod11(list, max_weight=9):
sum = 0
weight = 2
for item in reversed(list):
sum += item * weight
weight += 1
if weight > max_weight:
weight = 2
mod = 11 - sum % 11
if mod > 9:
return 0
else:
return mod
Upvotes: 4
Views: 2054
Reputation: 103774
You can use starmap and cycle from itertools.
First create a list of tuples to multiply together:
>>> from itertools import starmap, cycle
>>> li=zip(range(1,21), cycle(range(2,10)))
>>> li
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 2), (10, 3), (11, 4), (12, 5), (13, 6), (14, 7), (15, 8), (16, 9), (17, 2), (18, 3), (19, 4), (20, 5)]
Then use starmap
and mul
(from operator) to multiply:
>>> from operator import mul
>>> list(starmap(mul, li))
[2, 6, 12, 20, 30, 42, 56, 72, 18, 30, 44, 60, 78, 98, 120, 144, 34, 54, 76, 100]
If you wanted a straight comprehension, you can do:
>>> [x*y for x, y in ((e+1, (e%8)+2) for e in range(20))]
[2, 6, 12, 20, 30, 42, 56, 72, 18, 30, 44, 60, 78, 98, 120, 144, 34, 54, 76, 100]
Upvotes: 1
Reputation: 52133
You don't have to worry about resetting the multiplier if you use itertools.cycle
:
>>> itertools.cycle(range(2, 10))
2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, ...
so you can simplify your function like below:
from itertools import cycle
def mod11(lst, max_weight=9):
multipliers = cycle(range(2, max_weight + 1))
zipped = zip(lst, multipliers))
summed = sum(a * b for a, b in zipped)
mod = 11 - (summed % 11)
return (0 if mod > 9 else mod)
Upvotes: 1
Reputation: 20553
A lot of good answers here already, and in case you would like to use numpy
(usually good for this sort of thing), here's an alternative:
In [1]: import numpy as np
In [2]: a = np.arange(1, 21) # like range in numpy array
In [3]: a - 1 # calculation performs per element wise... so will a * n
Out[3]:
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19])
In [4]: a * ((a - 1) % 8 + 2) # directly translate the calculation
Out[4]:
array([ 2, 6, 12, 20, 30, 42, 56, 72, 18, 30, 44, 60, 78,
98, 120, 144, 34, 54, 76, 100])
Upvotes: 1
Reputation: 473833
You can use the current index to determine the multiplier:
>>> [(index % 8 + 2) * item for index, item in enumerate(range(1, 21))]
[2, 6, 12, 20, 30, 42, 56, 72, 18, 30, 44, 60, 78, 98, 120, 144, 34, 54, 76, 100]
#^ resets ^ resets ^
Or, you can "cycle" the sequence of multipliers and zip with the input sequence:
>>> from itertools import cycle
>>> [x * y for x, y in zip(range(1, 21), cycle(range(2, 10)))]
[2, 6, 12, 20, 30, 42, 56, 72, 18, 30, 44, 60, 78, 98, 120, 144, 34, 54, 76, 100]
Upvotes: 2
Reputation: 440
Ok, I am not quite sure if I understand your question correctly, since you talk about modulus 11, but also seem to use numbers from 2 up to and including 9.
But let's first start with writing the 1..20 as a list, that would be possible as follows:
list(range(1,21)) (This is if you want to include the 20)
Next, to turn some integer into a number between 2 and 9 you could do the following:
x -> x % 8 + 2
Combining these two, you could make a list comprehension:
[((x-1) % 8 + 2) * x for x in xrange(1,21)]
(the -1 is added to start the first number from 2 instead of from 3)
Upvotes: 1
Reputation: 2144
maybe oneliner :
s = map(lambda x : x*(x+1),list(range(1,20)))
basically map is core function used to make same operation on every element of table. first parameter is function which will be applied to every element. And If You want to remember generate another values used to multiplication maybe try some generator
Upvotes: 0