Вращение массива Python

Итак, я реализую алгоритм обмена блоками в python.

Алгоритм, которым я следую, таков:

Инициализировать A = arr[0..d-1] и B = arr[d..n-1] 1) Делайте следующее, пока размер A не станет равным размеру B

a) Если A короче, разделите B на Bl и Br так, чтобы Br был той же длины, что и A. Поменяйте местами A и Br, чтобы преобразовать ABlBr в BrBlA. Теперь A находится на своем последнем месте, поэтому повторяйте части B.

b) Если A длиннее, разделите A на Al и Ar так, чтобы Al был той же длины, что и B. Поменяйте местами Al и B, чтобы заменить AlArB на BArAl. Теперь B находится на своем последнем месте, поэтому повторяйте части A.

2) Наконец, когда A и B имеют одинаковый размер, поменяйте их местами.

Тот же алгоритм был реализован в C на этом веб-сайте — Вращение массива

Мой код Python для того же самого

a = [1,2,3,4,5,6,7,8]

x = 2

n = len(a)


def rotate(a,x):
    n = len(a)

    if x == 0 or x == n:
        return a

    if x == n -x:
        print(a)
        for i in range(x):
            a[i], a[(i-x+n) % n] = a[(i-x+n) % n], a[i]
        print(a)
        return a

    if x < n-x:
        print(a)
        for i in range(x):
            a[i], a[(i-x+n) % n] = a[(i-x+n) % n], a[i]
        print(a)
        rotate(a[:n-x],x)
    else:
        print(a)
        for i in range(n-x):
            a[i], a[(i-(n-x) + n) % n] = a[(i-(n-x) + n) % n] , a[i]
        print(a)
        rotate(a[n-x:], n-x)

rotate(a,x)
print(a)

Я получаю правильные значения на каждом этапе, но рекурсивный вызов функции не возвращает ожидаемого результата, и я не могу понять причину. Может кто-нибудь объяснить, что не так с моей рекурсией? и какая может быть возможная альтернатива.


person Samarth    schedule 27.06.2013    source источник


Ответы (7)


Вы можете вращать список на месте в Python, используя deque. :

>>> from collections import deque
>>> d=deque([1,2,3,4,5])
>>> d
deque([1, 2, 3, 4, 5])
>>> d.rotate(2)
>>> d
deque([4, 5, 1, 2, 3])
>>> d.rotate(-2)
>>> d
deque([1, 2, 3, 4, 5])

Или с кусочками списка:

>>> li=[1,2,3,4,5]
>>> li[2:]+li[:2]
[3, 4, 5, 1, 2]
>>> li[-2:]+li[:-2]
[4, 5, 1, 2, 3]

Обратите внимание, что соглашение о знаках противоположно deque.rotate vs slices.

Если вам нужна функция с тем же соглашением о знаках:

def rotate(l, y=1):
   if len(l) == 0:
      return l
   y = -y % len(l)     # flip rotation direction
   return l[y:] + l[:y]

>>> rotate([1,2,3,4,5],2)
[4, 5, 1, 2, 3]
>>> rotate([1,2,3,4,5],-22)
[3, 4, 5, 1, 2]
>>> rotate('abcdefg',3)
'efgabcd'

Для numpy просто используйте np.roll

>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> np.roll(a, 1)
array([9, 0, 1, 2, 3, 4, 5, 6, 7, 8])
>>> np.roll(a, -1)
array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])

Или вы можете использовать пустую версию того же rotate выше (снова отметив разницу в знаке и np.roll):

def rotate(a,n=1):
    if len(a) == 0:
        return a
    n = -n % len(a)     # flip rotation direction
    return np.concatenate((a[n:],a[:n]))  
person dawg    schedule 27.06.2013
comment
есть много Python, который я мог бы использовать, как вы предложили. Однако это было дано мне как вызов, который я должен был реализовать, не используя собственный метод. Если бы вы могли предложить модификацию в приведенном выше коде, было бы здорово - person Samarth; 27.06.2013
comment
@dawg Если li является numpy.array, добавление не объединяет оба массива, а вместо этого добавляет элемент за элементом. Как я могу выполнить операцию поворота с массивом numpy? - person SebMa; 11.07.2017
comment
@dawg Мне очень нравится твой ответ, я просто хотел бы увидеть этот ответ раньше, а пока я сделал это: np.concatenate((a[n:],a[:n])) - person SebMa; 12.07.2017
comment
@dawg Согласно IPython %timeit, np.concatenate( ( a[n:], a[:n] ) ) кажется в 10 раз быстрее :) - person SebMa; 12.07.2017
comment
@SebMa: np.concatenate( ( a[n:], a[:n] ) ) действительно быстрее. Это замедляет работу с большими массивами. Полагаю, похоже на ответ Python, который я дал. Спасибо! Я добавлю это. - person dawg; 12.07.2017

Простой и сокращенный синтаксис для поворота массива в Python:

arr = arr[numOfRotations:]+arr[:numOfRotations]

Пример:

arr = [1,2,3,4,5]
rotations = 4
then 

arr = arr[4:]+arr[:4]

дает нам

[5,1,2,3,4]

person Yashwanth Chowdary Kata    schedule 20.10.2017

Я обнаружил проблему, заключавшуюся в том, что мне нужны были повороты вправо и влево для больших значений k (где k — количество поворотов), поэтому я реализовал следующие функции для любого размера k.

Круговое вращение вправо (слева направо: 1234 -> 4123):

def right_rotation(a, k):
   # if the size of k > len(a), rotate only necessary with
   # module of the division
   rotations = k % len(a)
   return a[-rotations:] + a[:-rotations]

Круговое вращение влево (справа налево: 1234 -> 2341):

def left_rotation(a, k):
   # if the size of k > len(a), rotate only necessary with
   # module of the division
   rotations = k % len(a)
   return a[rotations:] + a[:rotations]

Источники:

person Ângelo Polotto    schedule 02.09.2018

Вам действительно нужно реализовать замену блоков или вы просто хотите повернуть массив? В python вы можете выполнять вращения CW и CWW, используя

zip(*arr[::-1])

а также

zip(*arr)[::-1]
person wflynny    schedule 27.06.2013
comment
Можете ли вы объяснить это подробнее? - person Long Hoang; 18.11.2016
comment
@LongHoang Этот ответ на самом деле не отвечает на вопрос, поскольку он вращает по часовой стрелке и против часовой стрелки на 2D-массивах. Я, вероятно, удалю этот ответ позже сегодня. - person wflynny; 18.11.2016

Я ожидаю, что когда вы передаете часть своего рекурсивного вызова, вы больше не передаете одну и ту же переменную. Попробуйте передать a полностью и верхнюю/нижнюю границы вашего среза в качестве дополнительных аргументов вашей функции.

Например, рассмотрим эту функцию:

def silly(z):
  z[0] = 2

Я только что попробовал следующее:

>>> z = [9,9,9,9,9,9]
>>> silly(z)
>>> z
[2, 9, 9, 9, 9, 9]
>>> silly(z[3:])
>>> z
[2, 9, 9, 9, 9, 9]

Где вы можете видеть, что изменение среза не было сохранено полным массивом

Из любопытства, какие результаты вы получаете и какие результаты вы ожидаете?

person user1245262    schedule 27.06.2013

вы можете использовать этот код для левого вращения в массиве python

import copy
def leftRotate(arr,n) :
    _arr = copy.deepcopy(arr)
    for i in range(len(arr)-n):
        arr[i] = arr[i+n]
    for j in range(n):
        arr[(len(arr)-n)+j] = _arr[j]

arr = [1, 2, 3, 4, 5, 6, 7] 
leftRotateby = 3
leftRotate(arr,leftRotateby)
print arr 
#output:: [4, 5, 6, 7, 1, 2, 3]
person a.patidar    schedule 22.05.2019
comment
Ответ @Angelo Polotto правильный, этот не работает в крайних случаях. - person Feanor; 29.03.2020

person    schedule
comment
Не могли бы вы объяснить, что было не так с исходным кодом? - person Run_Script; 15.10.2020
comment
В исходном коде нет ничего плохого. Я просто хотел поделиться другим подходом, в котором я использовал нарезку. - person Shubham Das Mohapatra; 17.10.2020