Какой самый эффективный способ повернуть список в Python? Прямо сейчас у меня есть что-то вроде этого:
>>> def rotate(l, n):
... return l[n:] + l[:n]
...
>>> l = [1,2,3,4]
>>> rotate(l,1)
[2, 3, 4, 1]
>>> rotate(l,2)
[3, 4, 1, 2]
>>> rotate(l,0)
[1, 2, 3, 4]
>>> rotate(l,-1)
[4, 1, 2, 3]
Есть ли способ лучше?
rotate
это правильное слово, а неshift
.Ответы:
А
collections.deque
оптимизирован для тяги и толкания на обоих концах. У них даже есть специальныйrotate()
метод.источник
collections.deque rotate()
это быстрее, чем нарезка в соответствии с wiki.python.org/moin/TimeComplexitydeque.rotate
в виду , использование требует сначала преобразования типа вdeque
объект, что медленнее, чемl.append(l.pop(0))
. Так что если у вас есть объект deque для начала, убедитесь, что он самый быстрый. В противном случае используйтеl.append(l.pop(0))
.deque.rotate
это O (k), но преобразование типа из списка в deque является O (n) . Поэтому, если вы начнете со списка, использование deque.rotate будет O (n) + O (k) = O (n).l.append(l.pop(0))
с другой стороны, O (1).Как насчет просто использовать
pop(0)
?источник
l.append(l.pop(0)
. Что, если я не ошибаюсь, является O (1).Numpy может сделать это с помощью
roll
команды:источник
Это зависит от того, что вы хотите, чтобы произошло, когда вы делаете это:
Вы можете изменить свой:
чтобы:
источник
Самый простой способ, которым я могу придумать:
источник
collections.deque
быстрее, но для большинства распространенных случаев длины списка в одной итерации или в любом случае нескольких итерацийa.append(a.pop(0))
будет быстрее, чем преобразование типов в dequeЕсли вы просто хотите перебирать эти наборы элементов, а не создавать отдельную структуру данных, рассмотрите возможность использования итераторов для создания выражения генератора:
источник
Это также зависит от того, хотите ли вы сместить список на месте (изменить его) или хотите, чтобы функция возвращала новый список. Потому что, согласно моим тестам, что-то вроде этого как минимум в двадцать раз быстрее, чем ваша реализация, которая добавляет два списка:
На самом деле, даже добавление a
l = l[:]
к вершине этого для работы с копией переданного списка все равно в два раза быстрее.Различные реализации с некоторым временем на http://gist.github.com/288272
источник
l[:n] = []
чтобы пойти наdel l[:n]
. Просто альтернатива.del
это все еще заявление в Py3. Однакоx.__delitem__(y) <==> del x[y]
, если вы предпочитаете использовать методы,l.__delitem__(slice(n))
это также эквивалентно и работает в 2 и 3.Несколько замечаний по срокам:
Если вы начинаете со списка,
l.append(l.pop(0))
это самый быстрый способ, который вы можете использовать. Это может быть показано только со сложностью времени:Так что, если вы начинаете с
deque
объектов, вы можетеdeque.rotate()
за счет O (k). Но, если отправной точкой является список, временная сложность использованияdeque.rotate()
составляет O (n).l.append(l.pop(0)
быстрее в O (1).Просто для иллюстрации, вот несколько примеров времени на 1M итераций:
Методы, которые требуют преобразования типов:
deque.rotate
с объектом deque: 0.12380790710449219 секунд (самый быстрый)deque.rotate
с преобразованием типов: 6,853878974914551 секундnp.roll
с nparray: 6.0491721630096436 секундnp.roll
с преобразованием типов: 27,558452129364014 секундПеречислите методы, упомянутые здесь:
l.append(l.pop(0))
: 0,32483696937561035 секунд (самый быстрый)shiftInPlace
": 4,819645881652832 секундВременной код используется ниже.
collections.deque
Показывает, что создание заявок из списков - это O (n):
Если вам нужно создать объекты deque:
1M итераций @ 6,853878974914551 секунд
Если у вас уже есть объекты deque:
1M итераций @ 0.12380790710449219 секунд
np.roll
Если вам нужно создать nparrays
1M итераций @ 27,558452129364014 секунд
Если у вас уже есть nparrays:
1M итераций @ 6.0491721630096436 секунд
«Сдвиг на место»
Не требует преобразования типа
1M итераций @ 4.819645881652832 секунд
l.append (l.pop (0))
Не требует преобразования типа
1M итераций @ 0.32483696937561035
источник
l = [random.random() for i in range(100000)]
l.append(l.pop(0))
лучше всего сдвигать короткие списки (около 7 элементов) на один?l.append(l.pop(0))
ответа: этот вопрос закрыт как дубликат. Может быть, вы проголосуете за его открытие?Я также заинтересовался этим и сравнил некоторые из предложенных решений с perfplot ( мой маленький проект).
Оказывается, что
является на сегодняшний день самым быстрым способом для малых сдвигов
n
.Для больших
n
,не плохо
По сути, perfplot выполняет сдвиг для увеличения больших массивов и измеряет время. Вот результаты:
shift = 1
:shift = 100
:Код для воспроизведения сюжета:
источник
l.append(l.pop(0))
ответа: Этот вопрос закрыт как дубликат. Может быть, вы проголосуете за его открытие?Возможно, кольцевой буфер больше подходит. Это не список, хотя вполне вероятно, что он может вести себя достаточно как список для ваших целей.
Проблема в том, что эффективность изменения списка составляет O (n), что становится значительным для достаточно больших списков.
Сдвиг в кольцевом буфере - это просто обновление местоположения головы, которое равно O (1)
источник
Для неизменной реализации вы можете использовать что-то вроде этого:
источник
Если ваша цель - эффективность (циклы? Память?), Вам может быть лучше взглянуть на модуль массива: http://docs.python.org/library/array.html
Массивы не имеют накладных расходов списков.
Что касается чистых списков, то, что у вас есть, настолько хорошо, насколько вы можете надеяться сделать.
источник
Я думаю, что вы ищете это:
источник
Другая альтернатива:
источник
Я принимаю эту модель стоимости в качестве ссылки:
http://scripts.mit.edu/~6.006/fall07/wiki/index.php?title=Python_Cost_Model
Ваш метод нарезки списка и объединения двух подсписков - это операции с линейным временем. Я бы предложил использовать pop, который является операцией постоянного времени, например:
источник
collections.dequeue
pop и appendleft, которые оба являются O (1) ops. В моем первом ответе выше, вставить это O (n).collections.deque
Я не знаю, является ли это «эффективным», но это также работает:
РЕДАКТИРОВАТЬ: Привет еще раз, я только что нашел большую проблему с этим решением! Рассмотрим следующий код:
Метод shift_classlist () выполняет тот же код, что и мой x.insert (0, x.pop ()) - решение, otherlist - это список, независимый от класса. После передачи содержимого otherlist в список MyClass.classlist вызов функции shift_classlist () также изменяет список других списков:
КОНСОЛЬНЫЙ ВЫХОД:
Я использую Python 2.7. Я не знаю, является ли это ошибкой, но я думаю, что более вероятно, что я что-то не так понял здесь.
Кто-нибудь из вас знает, почему это происходит?
источник
x.classlist = otherlist
заставляетx.classlist
обращаться к тому же списку, чтоotherlist
и тогда, когда вы вызываетеx.shift_classlist()
его, изменяет список и потому что оба имени ссылаются на один и тот же объект списка. Кажется, что оба имени меняются, потому что это просто псевдонимы для одного и того же объекта Используйтеx.classlist = otherlist[:]
вместо этого, чтобы назначить копию списка.Следующий метод - это O (n) с постоянной вспомогательной памятью:
Обратите внимание, что в python этот подход ужасно неэффективен по сравнению с другими, поскольку он не может использовать преимущества собственных реализаций какой-либо части.
источник
У меня есть похожая вещь. Например, сдвинуться на два ...
источник
Я думаю, у вас есть самый эффективный способ
источник
Какой вариант использования? Часто нам фактически не нужен полностью сдвинутый массив - нам просто нужно получить доступ к нескольким элементам в сдвинутом массиве.
Получение срезов Python - это время выполнения O (k), где k - это срез, поэтому ротация срезов - это время выполнения N. Команда вращения deque также O (k). Можем ли мы сделать лучше?
Рассмотрим массив, который очень большой (скажем, настолько большой, что его нарезка будет вычислительно медленной). Альтернативным решением было бы оставить исходный массив в покое и просто вычислить индекс элемента, который существовал бы в нашем желаемом индексе после какого-либо сдвига.
Таким образом, доступ к смещенному элементу становится O (1).
источник
Следующая функция копирует отправленный список во временный список, чтобы функция pop не влияла на исходный список:
Тестирование:
Вывод:
источник
Джон Бентли из « Программирование жемчужин» (столбец 2) описывает элегантный и эффективный алгоритм поворота
n
вектора -элемента,x
оставленногоi
позициями:Это может быть переведено на Python следующим образом:
Демо-версия:
источник
Для списка
X = ['a', 'b', 'c', 'd', 'e', 'f']
и желаемого значения сдвига,shift
меньшего длины списка , мы можем определить функцию,list_shift()
как показано нижеПримеры,
list_shift(X,1)
возвращается['b', 'c', 'd', 'e', 'f', 'a']
list_shift(X,3)
возвращается['d', 'e', 'f', 'a', 'b', 'c']
источник
list_shift
в вашем ответе идентична функцииshift
в исходном вопросе, так что это не ответ на реальный вопрос: «Есть ли лучший способ?»Например, учитывая
функция должна вернуться
[9, 7, 6, 3, 8]
. Три поворота были сделаны:Для другого примера, приведенного
функция должна вернуться
[0, 0, 0]
Дано
функция должна вернуться
[1, 2, 3, 4]
источник
Я искал на месте решение этой проблемы. Это решает цель в O (k).
источник
для аналогичной функциональности, как сдвиг в других языках:
источник
L.pop(0)