2016-11-02 4 views
1

exercises 중 하나 (즉, # 6)는 큐 구현의 성능 (목록 끝 부분의 머리 부분)을 비교하도록 요청합니다. 약간의 차이가있을 수있는 것처럼 들리므로, 나는 그것을 알아 내려고 노력했다. 여기파이썬에서 큐 구현 간의 차이점

import timeit 

class QueueStart(object): 
    '''Queue implementation with head in the beginning of a list'''  
    def __init__(self): 
     self.items = [] 

    def enqueue(self, i): 
     self.items.append(i) 

    def dequeue(self): 
     return self.items.pop(0) 

    def isEmpty(self): 
     return len(self.items) == 0 

    def size(self): 
     return len(self.items) 

class QueueEnd(object): 
    '''Queue implementation with head at the end of a list'''  
    def __init__(self): 
     self.items = [] 

    def enqueue(self, item): 
     self.items.insert(0, item) 

    def dequeue(self): 
     return self.items.pop() 

    def isEmpty(self): 
     return len(self.items) == 0 

    def size(self): 
     return len(self.items) 

# store results for further plotting 
start_add_list = [] # QueueStart.enqueue(item) runtimes for inputs of different sizes 
start_pop_list = [] # the same for QueueStart.dequeue(item) 
end_add_list = [] # the same for QueueEnd.enqueue(item) 
end_pop_list = [] # the same for QueueEnd.dequeue(item) 

for i in range(100000, 500000, 10000): 
    qs = QueueStart() 
    qs.items = list(range(i)) 
    qe = QueueEnd() 
    qe.items = list(range(i)) 
    start_add = timeit.Timer('qs.enqueue(1)', 'from __main__ import qs') 
    start_pop = timeit.Timer('qs.dequeue()', 'from __main__ import qs') 
    end_add = timeit.Timer('qe.enqueue(1)', 'from __main__ import qe') 
    end_pop = timeit.Timer('qe.dequeue()', 'from __main__ import qe') 

    start_add_list.append(start_add.timeit(number=1000)) 
    start_pop_list.append(start_pop.timeit(number=1000)) 
    end_add_list.append(end_add.timeit(number=1000)) 
    end_pop_list.append(end_pop.timeit(number=1000)) 

그리고 여기가 내 실험 enter image description here enter image description here

insertpop(index)는 O (n)이있는 것으로 알려져 결과를 반영 플롯입니다 내 코드입니다. 흥미로운 점은 그래프에서 볼 때 insert(0, item)pop(0)의 두 배가 걸리는 것을 볼 수 있습니다. 그 관찰은 저에게 이것이 왜 그런지 궁금합니다. 표면적으로 볼 때 두 가지 방법이 매우 유사하지만 외관상으로는 흥미로운 점이 있습니다. 그래서, 질문은 : 당신이 그것을 알아 내도록 도울 수 있습니까?

+0

이것은 기본 배열에 추가하고 삭제할 때 작업이 메모리를 관리하는 방법에 따라 구현에 따라 다를 수 있습니다. 예를 들어,'pop (0)'은 어떤 배열 요소가'[0]'인지를 바꿀 수있는 반면,'insert (0, ...)'는 한 슬롯 위로 모든 것을 계속해서 푸시해야 할 것입니다. 배열의 앞쪽에. – chepner

+0

@chepner : 운동에서 나오는 책의 제목은 : "pop이리스트의 끝에서 호출되면 그것은 O (1)을 취하지 만, pop이리스트의 첫 번째 요소 나 중간에있는 어떤 곳에서 호출 될 때 O (n)이 이유는 파이썬이리스트를 구현하는 방법에있다. **리스트 앞에서 항목을 가져올 때, 파이썬의 구현에서,리스트의 다른 모든 원소들은 한 위치에서 시작 부분으로 더 가깝게 이동한다. **. " 알다시피,'insert (0, ...)'와'pop (0)'모두 최악의 경우에 교대조를 만들지 만, 상수 요인의 차이가 어디에서 유래하는지는 분명하지 않다. –

답변

0

CPython과의 list 구현의 일부 독서는 : http://www.laurentluce.com/posts/python-list-implementation/ 기본적으로

, 목록은 그들이 어떤 주어진 시간에 필요로 두 배 정도 많은 메모리를 갖도록 설계되어, 그래서 그들은 메모리를 재 할당 할 필요없이 약간 길이를 변경할 수 있습니다. 목록에 더 많은 메모리가 필요할 때, 전체 목록을 충분한 공간이있는 메모리의 다른 위치로 이동해야하는 경우가 있습니다. 그들은 축소 할 때 목록 끝에 메모리를 확보 할 수 있습니다.