How do I use modular arithmetic on indices?

Question:

I am trying to create a function that, for each member of a list, returns the value of that member and the number of values either side of it. The only trick is that it has to “wrap around” when it is at the start or end of the list

For example:

a = [0,1,2,3,4,5,6,7,8,9]  
myfunc(a,2) # 2 indicates 2 either side

[8,9,0,1,2]
[9,0,1,2,3]
[0,1,2,3,4]
...
...
[6,7,8,9,0]
[7,8,9,0,1]

I can work out how to do it from index 2 until 7:

def myfunc(vals, rnge):
    for i in range(0+rnge, len(vals)-rnge):
        print vals[i-rnge:i+rnge+1]

But I can’t work out how to handle when it needs to wrap around.

Asked By: guskenny83

||

Answers:

you could try this (extend vals in both directions first). there may be something in collections that allows for this to be done more efficiently:

def myfunc(vals, rnge):
    vals_ext = vals[-rnge:] + vals + vals[:rnge]
    for i in range(len(vals)):
        print( vals_ext[i:i+2*rnge+1] )

output:

[8, 9, 0, 1, 2]
[9, 0, 1, 2, 3]
[0, 1, 2, 3, 4]
[1, 2, 3, 4, 5]
[2, 3, 4, 5, 6]
[3, 4, 5, 6, 7]
[4, 5, 6, 7, 8]
[5, 6, 7, 8, 9]
[6, 7, 8, 9, 0]
[7, 8, 9, 0, 1]
Answered By: hiro protagonist

How about:

def myfunc(vals, rnge):
    valsX3 = vals*3;
    for i in range(len(vals)):
        print valsX3[i:i+2*rnge+1]

You could use something like this to avoid the duplication of arrays:
wrapping around slices in Python / numpy

Not sure how it does it internally though.

Answered By: Nitesh Patel

Here is the solution :

def defunc(vals, start, rnge):
    n = len(vals)
    new = []

    for i in xrange((start - rnge), (start + rnge)+ 1):
        new.append(vals[i%n])
    return new

def myfunc(vals, rnge):
    n = len(vals)
    for i in range(n):
        print defunc(vals,i,rnge)

I am not slicing the list to make the logic clearer.
I hope this works

Answered By: Kshitij Saraogi

Here is an alternative approach you might also find useful:

def myfunc(alist, offset):
    adeque = collections.deque(a)       
    adeque.rotate(offset)
    for i in xrange(len(alist)):
        print list(itertools.islice(adeque, 0, 2*offset+1))
        adeque.rotate(-1)

a = [0,1,2,3,4,5,6,7,8,9]  
myfunc(a,2) # 2 indicates 2 either side

It makes use of the deque collections object which has an efficient rotate function.

Answered By: Martin Evans

Generate your lists using the numpy element-wise modulo of an (unshifted) array.

You want 5 elements that wrap around 10, so use modulo 10. For example if the list starts with 8:

np.mod(np.arange(8,8+5,1),10)  returns [8, 9, 0, 1, 2]

To get all 10 possible lists, evaluate list(np.mod(np.arange(s,s+5,1),10))for each start s=1,2,..,10

Or, in a single list comprehension without resorting to numpy,

[[t%10 for t in range(s,s+5)] for s in range(10)]

returns

[[0, 1, 2, 3, 4],
 [1, 2, 3, 4, 5],
 [2, 3, 4, 5, 6],
 [3, 4, 5, 6, 7],
 [4, 5, 6, 7, 8],
 [5, 6, 7, 8, 9],
 [6, 7, 8, 9, 0],
 [7, 8, 9, 0, 1],
 [8, 9, 0, 1, 2],
 [9, 0, 1, 2, 3]] 
Answered By: Mark Greenwood
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.