9

I'm new to coding and am trying to write a simple code that will take a list, say [1,2,3] and cycle the elements n number of times. So if n=1, I should get A=[3,1,2]. If n=2, I should get A=[2,3,1].The code I have written is:

n=1
j=0
A = [1,2,3]
B = [None]*len(A)

while j<=n:
     for i in range(0,len(A)):
         B[i] = A[-1+i]
     j=j+1
print(B)

The problem is that no matter what the value of n is I get the same answer which is only cycled once. I think the problem is that the loop is cycling through the same B every time, so I need to store the new B as something else and then repeat the loop with new B. But I can't figure out how to do that. Any tips would be appreciated

martineau
  • 119,623
  • 25
  • 170
  • 301
DeathbyGreen
  • 337
  • 2
  • 17

6 Answers6

7

I think you're overcomplicating it. Consider changing it to something like the following:

n = 1
A = [1,2,3]
B = A.copy()

for _ in range(n):
    # Cycle through by concatenating the last element and all other elements together 
    B = [B[-1]]+B[0:-1]

print(B)

In case of n=1, you get [3, 1, 2], and n=2 gives you [2, 3, 1]

Note that what you are trying to do is implemented in numpy.roll (I suppose you're asking about the process, not the result, but just in case)

import numpy as np

>>> np.roll(A,1)
array([3, 1, 2])
>>> np.roll(A,2)
array([2, 3, 1])
sacuL
  • 49,704
  • 8
  • 81
  • 106
  • 1
    The relationship between the two list's indices is `j = (j-n)%k` where `j` is the cycled list index and `k` is the list length. Using arrays this would be very efficient. Indexing the list directly would still be acceptable (in python thinking). However it baffles me that for such a common problem one has to create, join and destroy lists all over the place. – Margaret Bloom Oct 06 '18 at 10:14
  • You can further simplify `B[0:-1]` to `B[:-1]` – rocksportrocker Oct 07 '18 at 11:27
5

A simpler function for this is:

def roll(L, n):
    n %= len(L)
    return L[-n:] + L[:-n]

A = [1,2,3]
roll(A, 1)   # [3, 1, 2]
roll(A, 2)   # [2, 3, 1]
roll(A, 3)   # [1, 2, 3]
roll(A, 4)   # [3, 1, 2]

Taking the modulus (n %= len(L)) avoids the need to keep cycling through. We then just concatenate an appropriate-sized slice off the end of the list to the beginning of it.

Stuart
  • 9,597
  • 1
  • 21
  • 30
2

See @sacul's answer for the problem with your code. But list is not the most appropriate structure for such a requirement as each shift has O(n) complexity.

deque ("double-ended queue") from the collections module provides this feature via its rotate method. This method works in-place and has O(k) complexity, where k is argument representing the number of rotations. Here's an example:

from collections import deque

d = deque([1,2,3])
d.rotate(2)

print(d)

deque([2, 3, 1])
jpp
  • 159,742
  • 34
  • 281
  • 339
1

@sacul's answer works, but you were close! You missed updating A for the next iteration of the while loop after you created your new B.

n=1
j=0
A = [1,2,3]
B = [None]*len(A)

while j<=n:
    for i in range(0,len(A)):
        B[i] = A[-1+i]
    A = B[:] # update A so that next time B works on the new A 
    print('A is now ', A) # to debug
    j=j+1

print(B)

This results in the following print statements:

A is now  [3, 1, 2]                                                                                                                   
A is now  [2, 3, 1]                                                                                                                   
[2, 3, 1] # this is what finally B is
slider
  • 12,810
  • 1
  • 26
  • 42
  • But this is the wrong outcome for n = 1... set `while j – Stuart Oct 06 '18 at 01:55
  • @Stuart What do you mean "wrong"? `j = 1` can mean 2 iterations; it's just a matter of definition. This is OP's original code only modified slightly to explain where they went wrong. – slider Oct 06 '18 at 02:07
  • Wrong in the sense that it does not provide the output specified by the OP for the given `A` and `n`. – Stuart Oct 06 '18 at 02:10
  • Okay I see now how to update B like I was talking about originally. I had tried to say B=A, but I see if I swap the order I get what one of my issues was – DeathbyGreen Oct 07 '18 at 17:49
0

An itertools based solution

from itertools import cycle, islice

def roll(x, n):
    start = len(x) - n
    return islice(cycle(x), start, start + len(x))

If we cycle through the values in x (we'd actually only do this at most twice). Then slice it so that it begins at the element we want and includes the correct number of elements.

It's probably a little more esoteric than some of the other solutions, but worth throwing into the bag, given there are so many alternatives.

itertools.islice returns a generator. If you wanted to print the result you'd need to convert it to list.

Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
-1

Not going to in depth since someone already answered your question.. But an important property to note here is also that

A = [1, 2, 3] #Assuming that the length of the matrix you want to rotate is of length 3

If you want to cycle/rotate by 3, then you want to actually cycle/rotate by `len(A)%3` = 0
If you want to cycle/rotate by 6, then you want to actually cycle/rotate by `len(A)%6` = 0
If you want to cycle/rotate by 4, then you want to actually cycle/rotate by `len(A)%3` = 1
If you want to cycle/rotate by a number lesser than the length itself.. then just rotate it by that number!

so if you want to cycle by 2, then well.. rotate it by two.. 
ababuji
  • 1,683
  • 2
  • 14
  • 39