2016-11-02 7 views
1

exercises(つまり#6)の1つは、キュー実装のパフォーマンスを比較するように要求しています。それはいくつかの違いがあるように聞こえるので、私はそれを把握しようとしました。ここに私のコードはPythonでのキュー実装の違い

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)の2倍の時間がかかります。その観察は私にはなぜ、これが当てはまるのか不思議に思った。表面上、2つの方法は非常によく似ていますが、明らかにボンネットの下には何か面白いことがあります。それで、問題は、あなたが私にそれを理解させる助けになるか?

+0

これはおそらく実装に依存します。これは、基になる配列を追加したり削除したりするときに、操作がメモリをどのように管理するかに基づいています。例えば、 'pop(0)'は未使用メモリを犠牲にしてどの配列要素を '[0]'に変更することができるのに対し、 'insert(0、...)アレイの前面にあります。 – chepner

+0

@chepner:エクササイズの本はこう書いています: "リストの最後にpopが呼び出されると、O(1)が必要ですが、リストの最初の要素か中間のどこかでpopが呼び出されると、Oその理由は、Pythonがリストを実装する方法にあります**アイテムがリストの先頭から取り出されるとき、Pythonの実装では、リスト内の他のすべての要素が、最初の位置に近い位置にシフトされます**。 "私が理解しているように、 'insert(0、...)'と 'pop(0)'の両方が最悪の場合にシフトを起こす可能性があります。 –

答えて

0

はCPythonのlist実装で読んでいくつか:http://www.laurentluce.com/posts/python-list-implementation/

は、基本的には、リストは、彼らは任意の時点で必要があるとして、約2倍の量のメモリを持つように設計されているので、彼らは、メモリを再割り当てする必要がなく、わずかに長さを変更することができます。リストがより多くのメモリを必要とする場合、リスト全体を十分なスペースを持つメモリ内の別の場所に移動する必要があることがあります。彼らは縮小すると、リストの最後にメモリを解放するだけです。

関連する問題