파이썬에서 목록을 회전시키는 효율적인 방법
파이썬에서 목록을 회전시키는 가장 효율적인 방법은 무엇입니까? 지금 나는 이와 같은 것을 가지고있다 :
>>> 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]
더 좋은 방법이 있습니까?
A collections.deque
는 양쪽 끝을 당기고 밀도록 최적화되어 있습니다. 그들은 심지어 전용 rotate()
방법이 있습니다.
from collections import deque
items = deque([1, 2])
items.append(3) # deque == [1, 2, 3]
items.rotate(1) # The deque is now: [3, 1, 2]
items.rotate(-1) # Returns deque to original state: [1, 2, 3]
item = items.popleft() # deque == [2, 3]
그냥 사용하는 것은 pop(0)
어떻습니까?
list.pop([i])
목록에서 지정된 위치에있는 항목을 제거하고 반환하십시오. 인덱스를 지정하지 않으면
a.pop()
목록의 마지막 항목을 제거하고 반환합니다. (i
메소드 서명에서 대괄호 는 매개 변수가 선택적이며 해당 위치에 대괄호를 입력하지 않아야 함을 나타냅니다.이 표기법은 Python Library Reference에 자주 표시됩니다.)
Numpy는 다음 roll
명령을 사용하여이를 수행 할 수 있습니다 .
>>> import numpy
>>> a=numpy.arange(1,10) #Generate some data
>>> numpy.roll(a,1)
array([9, 1, 2, 3, 4, 5, 6, 7, 8])
>>> numpy.roll(a,-1)
array([2, 3, 4, 5, 6, 7, 8, 9, 1])
>>> numpy.roll(a,5)
array([5, 6, 7, 8, 9, 1, 2, 3, 4])
>>> numpy.roll(a,9)
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
이 작업을 수행 할 때 수행하려는 작업에 따라 다릅니다.
>>> shift([1,2,3], 14)
다음을 변경하고 싶을 수도 있습니다.
def shift(seq, n):
return seq[n:]+seq[:n]
에:
def shift(seq, n):
n = n % len(seq)
return seq[n:] + seq[:n]
별도의 데이터 구조를 구성하는 대신 이러한 요소 세트를 반복하려는 경우 반복자를 사용하여 생성기 표현식을 구성하십시오.
def shift(l,n):
return itertools.islice(itertools.cycle(l),n,n+len(l))
>>> list(shift([1,2,3],1))
[2, 3, 1]
내가 생각할 수있는 가장 간단한 방법 :
a.append(a.pop(0))
또한 목록을 제자리로 이동 (변경)하거나 함수가 새 목록을 반환하도록할지 여부에 따라 다릅니다. 내 테스트에 따르면 다음과 같은 것이 두 가지 목록을 추가하는 구현보다 20 배 이상 빠르기 때문입니다.
def shiftInPlace(l, n):
n = n % len(l)
head = l[:n]
l[:n] = []
l.extend(head)
return l
실제로, l = l[:]
전달 된 목록의 사본을 조작하기 위해 맨 위에 a 를 추가해도 여전히 두 배 빠릅니다.
http://gist.github.com/288272 에서 일부 타이밍을 사용한 다양한 구현
타이밍에 대한 몇 가지 참고 사항 :
목록으로 시작하는 l.append(l.pop(0))
경우 가장 빠른 방법입니다. 시간 복잡성만으로도이를 보여줄 수 있습니다.
- deque.rotate는 O (k)입니다 (k = 요소 수).
- 변환을 해제하는 목록은 O (n)입니다.
- list.append와 list.pop은 모두 O (1)입니다.
따라서 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
목록에서 deques를 만드는 것은 O (n)임을 보여줍니다.
from collections import deque
import big_o
def create_deque_from_list(l):
return deque(l)
best, others = big_o.big_o(create_deque_from_list, lambda n: big_o.datagen.integers(n, -100, 100))
print best
# --> Linear: time = -2.6E-05 + 1.8E-08*n
deque 객체를 만들어야하는 경우 :
1M 반복 @ 6.853878974914551 초
setup_deque_rotate_with_create_deque = """
from collections import deque
import random
l = [random.random() for i in range(1000)]
"""
test_deque_rotate_with_create_deque = """
dl = deque(l)
dl.rotate(-1)
"""
timeit.timeit(test_deque_rotate_with_create_deque, setup_deque_rotate_with_create_deque)
이미 deque 객체가있는 경우 :
0.12380790710449219 초에서 1M 반복
setup_deque_rotate_alone = """
from collections import deque
import random
l = [random.random() for i in range(1000)]
dl = deque(l)
"""
test_deque_rotate_alone= """
dl.rotate(-1)
"""
timeit.timeit(test_deque_rotate_alone, setup_deque_rotate_alone)
np.roll
nparray를 만들어야하는 경우
1M 반복 @ 27.558452129364014 초
setup_np_roll_with_create_npa = """
import numpy as np
import random
l = [random.random() for i in range(1000)]
"""
test_np_roll_with_create_npa = """
np.roll(l,-1) # implicit conversion of l to np.nparray
"""
이미 nparray가있는 경우 :
6.0491721630096436 초에서 1M 반복
setup_np_roll_alone = """
import numpy as np
import random
l = [random.random() for i in range(1000)]
npa = np.array(l)
"""
test_roll_alone = """
np.roll(npa,-1)
"""
timeit.timeit(test_roll_alone, setup_np_roll_alone)
"제자리에 이동"
타입 변환이 필요하지 않습니다
4.819645881652832 초에서 1M 반복
setup_shift_in_place="""
import random
l = [random.random() for i in range(1000)]
def shiftInPlace(l, n):
n = n % len(l)
head = l[:n]
l[:n] = []
l.extend(head)
return l
"""
test_shift_in_place="""
shiftInPlace(l,-1)
"""
timeit.timeit(test_shift_in_place, setup_shift_in_place)
l. 첨부 (l.pop (0))
타입 변환이 필요하지 않습니다
1M 반복 @ 0.32483696937561035
setup_append_pop="""
import random
l = [random.random() for i in range(1000)]
"""
test_append_pop="""
l.append(l.pop(0))
"""
timeit.timeit(test_append_pop, setup_append_pop)
나는 이것에 관심이 있었고 제안 된 솔루션 중 일부를 perfplot (작은 프로젝트) 과 비교했습니다 .
그것은 밝혀졌다
for _ in range(n):
data.append(data.pop(0))
이다 훨씬 작은 변화를위한 가장 빠른 방법 n
.
큰 들어 n
,
data[n:] + data[:n]
나쁘지 않습니다.
본질적으로 perfplot은 큰 배열을 늘리기 위해 이동을 수행하고 시간을 측정합니다. 결과는 다음과 같습니다.
shift = 1
:
shift = 100
:
줄거리를 재현하는 코드 :
import numpy
import perfplot
import collections
shift = 100
def list_append(data):
return data[shift:] + data[:shift]
def shift_concatenate(data):
return numpy.concatenate([data[shift:], data[:shift]])
def roll(data):
return numpy.roll(data, -shift)
def collections_deque(data):
items = collections.deque(data)
items.rotate(-shift)
return items
def pop_append(data):
for _ in range(shift):
data.append(data.pop(0))
return data
perfplot.save(
"shift100.png",
setup=lambda n: numpy.random.rand(n).tolist(),
kernels=[list_append, roll, shift_concatenate, collections_deque, pop_append],
n_range=[2 ** k for k in range(7, 20)],
logx=True,
logy=True,
xlabel="len(data)",
)
아마도 링 버퍼가 더 적합 할 것입니다. 목록은 아니지만 목적에 따라 목록처럼 충분히 작동 할 수 있습니다.
문제는리스트의 시프트 효율이 O (n)이며, 이는 충분히 큰리스트에 대해 중요해진다는 것입니다.
링 버퍼에서 쉬프트는 단순히 O (1) 인 헤드 위치를 업데이트하는 것입니다.
변경 불가능한 구현의 경우 다음과 같이 사용할 수 있습니다.
def shift(seq, n):
shifted_seq = []
for i in range(len(seq)):
shifted_seq.append(seq[(i-n) % len(seq)])
return shifted_seq
print shift([1, 2, 3, 4], 1)
효율성이 목표라면 (주기? 메모리?) 배열 모듈을 보는 것이 좋습니다. http://docs.python.org/library/array.html
배열에는 목록의 오버 헤드가 없습니다.
그러나 순수한 목록에 관한 한, 당신이 원하는 것은 당신이 할 수있는만큼 좋은 것입니다.
나는 당신이 이것을 찾고 있다고 생각합니다 :
a.insert(0, x)
다른 대안 :
def move(arr, n):
return [arr[(idx-n) % len(arr)] for idx,_ in enumerate(arr)]
이 비용 모델을 참조로 사용합니다.
http://scripts.mit.edu/~6.006/fall07/wiki/index.php?title=Python_Cost_Model
목록을 분할하고 두 개의 하위 목록을 연결하는 방법은 선형 시간 작업입니다. 상수 시간 작업 인 pop을 사용하는 것이 좋습니다.
def shift(list, n):
for i in range(n)
temp = list.pop()
list.insert(0, temp)
이것이 '효율적인지'모르지만 작동합니다.
x = [1,2,3,4]
x.insert(0,x.pop())
편집 : 안녕하세요 다시, 나는이 솔루션에 큰 문제를 발견했습니다! 다음 코드를 고려하십시오.
class MyClass():
def __init__(self):
self.classlist = []
def shift_classlist(self): # right-shift-operation
self.classlist.insert(0, self.classlist.pop())
if __name__ == '__main__':
otherlist = [1,2,3]
x = MyClass()
# this is where kind of a magic link is created...
x.classlist = otherlist
for ii in xrange(2): # just to do it 2 times
print '\n\n\nbefore shift:'
print ' x.classlist =', x.classlist
print ' otherlist =', otherlist
x.shift_classlist()
print 'after shift:'
print ' x.classlist =', x.classlist
print ' otherlist =', otherlist, '<-- SHOULD NOT HAVE BIN CHANGED!'
shift_classlist () 메서드는 내 x.insert (0, x.pop ()) 솔루션과 동일한 코드를 실행하고 otherlist는 클래스와 독립적 인 목록입니다. otherlist의 내용을 MyClass.classlist 목록으로 전달한 후 shift_classlist ()를 호출하면 otherlist 목록도 변경됩니다.
콘솔 출력 :
before shift:
x.classlist = [1, 2, 3]
otherlist = [1, 2, 3]
after shift:
x.classlist = [3, 1, 2]
otherlist = [3, 1, 2] <-- SHOULD NOT HAVE BIN CHANGED!
before shift:
x.classlist = [3, 1, 2]
otherlist = [3, 1, 2]
after shift:
x.classlist = [2, 3, 1]
otherlist = [2, 3, 1] <-- SHOULD NOT HAVE BIN CHANGED!
파이썬 2.7을 사용합니다. 그게 버그인지는 모르겠지만 여기서 뭔가를 잘못 이해했을 가능성이 높습니다.
왜 이런 일이 일어나는지 아십니까?
다음 방법은 보조 메모리가 일정한 O (n)입니다.
def rotate(arr, shift):
pivot = shift % len(arr)
dst = 0
src = pivot
while (dst != src):
arr[dst], arr[src] = arr[src], arr[dst]
dst += 1
src += 1
if src == len(arr):
src = pivot
elif dst == pivot:
pivot = src
파이썬 에서이 접근법은 다른 조각에 비해 끔찍하게 비효율적입니다. 어떤 조각의 기본 구현을 활용할 수 없기 때문입니다.
나는 비슷한 것을 가지고 있습니다. 예를 들어, 2만큼 이동하려면 ...
def Shift(*args):
return args[len(args)-2:]+args[:len(args)-2]
가장 효율적인 방법이 있다고 생각합니다
def shift(l,n):
n = n % len(l)
return l[-U:] + l[:-U]
리스트 X = ['a', 'b', 'c', 'd', 'e', 'f']
와 shift
리스트 길이보다 작은 원하는 시프트 값을 위해, 함수 list_shift()
를 아래와 같이 정의 할 수 있습니다
def list_shift(my_list, shift):
assert shift < len(my_list)
return my_list[shift:] + my_list[:shift]
예
list_shift(X,1)
반품 ['b', 'c', 'd', 'e', 'f', 'a']
list_shift(X,3)
반품['d', 'e', 'f', 'a', 'b', 'c']
사용 사례는 무엇입니까? 실제로 완전히 시프트 된 어레이가 필요하지 않은 경우가 있습니다. 시프트 된 어레이의 소수의 요소에만 액세스하면됩니다.
파이썬 슬라이스를 얻는 것은 런타임 O (k)입니다. 여기서 k는 슬라이스이므로 슬라이스 회전은 런타임 N입니다. deque rotation 명령도 O (k)입니다. 더 잘할 수 있을까요?
매우 큰 배열을 가정 해 봅시다 (예를 들어, 계산하기가 느리면 슬라이스 속도가 느립니다). 다른 해결책은 원래 배열을 그대로두고 어떤 종류의 이동 후 원하는 색인에 존재했을 항목의 색인을 간단히 계산하는 것입니다.
시프트 된 요소에 액세스하면 O (1)이됩니다.
def get_shifted_element(original_list, shift_to_left, index_in_shifted):
# back calculate the original index by reversing the left shift
idx_original = (index_in_shifted + shift_to_left) % len(original_list)
return original_list[idx_original]
my_list = [1, 2, 3, 4, 5]
print get_shifted_element(my_list, 1, 2) ----> outputs 4
print get_shifted_element(my_list, -2, 3) -----> outputs 2
다음 함수는 전송 된 목록을 임시 목록에 복사하므로 pop 함수는 원래 목록에 영향을 미치지 않습니다.
def shift(lst, n, toreverse=False):
templist = []
for i in lst: templist.append(i)
if toreverse:
for i in range(n): templist = [templist.pop()]+templist
else:
for i in range(n): templist = templist+[templist.pop(0)]
return templist
테스트 :
lst = [1,2,3,4,5]
print("lst=", lst)
print("shift by 1:", shift(lst,1))
print("lst=", lst)
print("shift by 7:", shift(lst,7))
print("lst=", lst)
print("shift by 1 reverse:", shift(lst,1, True))
print("lst=", lst)
print("shift by 7 reverse:", shift(lst,7, True))
print("lst=", lst)
산출:
lst= [1, 2, 3, 4, 5]
shift by 1: [2, 3, 4, 5, 1]
lst= [1, 2, 3, 4, 5]
shift by 7: [3, 4, 5, 1, 2]
lst= [1, 2, 3, 4, 5]
shift by 1 reverse: [5, 1, 2, 3, 4]
lst= [1, 2, 3, 4, 5]
shift by 7 reverse: [4, 5, 1, 2, 3]
lst= [1, 2, 3, 4, 5]
Programming Pearls (Column 2)의 Jon Bentley 는 n
요소 벡터 x
를 i
위치 별로 왼쪽으로 회전시키는 우아하고 효율적인 알고리즘을 설명합니다 .
배열
ab
을 배열 로 변환하는 것으로 문제를ba
보자. 그러나 배열 의 지정된 부분에서 요소를 뒤집는 함수가 있다고 가정하자. 시작ab
, 우리는 반대로a
얻기 위해 역 얻기 위해 얻을 수있는 모든 일을 반대로 다음과 정확히 일치하는 . 결과적으로 다음과 같은 회전 코드가 생성됩니다.arb
b
arbr
(arbr)r
ba
reverse(0, i-1) reverse(i, n-1) reverse(0, n-1)
이것은 다음과 같이 파이썬으로 번역 될 수 있습니다 :
def rotate(x, i):
i %= len(x)
x[:i] = reversed(x[:i])
x[i:] = reversed(x[i:])
x[:] = reversed(x)
return x
데모:
>>> def rotate(x, i):
... i %= len(x)
... x[:i] = reversed(x[:i])
... x[i:] = reversed(x[i:])
... x[:] = reversed(x)
... return x
...
>>> rotate(list('abcdefgh'), 1)
['b', 'c', 'd', 'e', 'f', 'g', 'h', 'a']
>>> rotate(list('abcdefgh'), 3)
['d', 'e', 'f', 'g', 'h', 'a', 'b', 'c']
>>> rotate(list('abcdefgh'), 8)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
>>> rotate(list('abcdefgh'), 9)
['b', 'c', 'd', 'e', 'f', 'g', 'h', 'a']
다른 언어의 시프트와 유사한 기능 :
def shift(l):
x = l[0]
del(l[0])
return x
참고 URL : https://stackoverflow.com/questions/2150108/efficient-way-to-rotate-a-list-in-python
'Programing' 카테고리의 다른 글
Java 시스템 특성 및 환경 변수 (0) | 2020.04.09 |
---|---|
생성자 초기화 목록 평가 순서 (0) | 2020.04.09 |
Java 8 메소드 참조 : 매개 변수화 된 결과를 제공 할 수있는 공급 업체 제공 (0) | 2020.04.09 |
Java에서 XML을 파일 대신 문자열로 구문 분석하는 방법은 무엇입니까? (0) | 2020.04.09 |
'자산'폴더에서 SD 카드로 파일을 복사하는 방법은 무엇입니까? (0) | 2020.04.09 |