私はオイラー問題をコーディングしていました、そして、私の好奇心を刺激した疑問に出くわしました。 2つのコードスニペットがあります。 1つはリストを使用する方法、もう1つは辞書を使用する方法です。
リストを使用:
n=100000
num=[]
suma=0
for i in range(n,1,-1):
tmp=Tuple(set([n for n in factors(i)]))
if len(tmp) != 2: continue
if tmp not in num:
num.append(tmp)
suma+=i
辞書を使用:
n=100000
num={}
suma=0
for i in range(n,1,-1):
tmp=Tuple(set([n for n in factors(i)]))
if len(tmp) != 2: continue
if tmp not in num:
num[tmp]=i
suma+=i
私はパフォーマンスだけに関心があります。辞書を使用した2番目の例が、リストを使用した最初の例よりも非常に高速で実行されるのはなぜですか。辞書を使用した例は、約30倍速く実行されます!
私はこれらの2つのコードをn = 1000000を使用してテストしました。最初のコードは1032秒で実行され、2番目のコードは3.3秒で実行されます。
Pythonでは、辞書キールックアップの平均時間の複雑さはO(1)です。これは、ハッシュテーブルとして実装されているためです。リストのルックアップの時間の複雑さは、平均でO(n))です。コードでは、これによりif tmp not in num:
、リストの場合、Pythonはメンバーシップを検出するためにリスト全体を検索する必要があります。一方、dictの場合は、絶対的な最悪の場合を除きます。
詳しくは、 TimeComplexity をご覧ください。
速度が問題になる場合は、リストを作成しないでください。
n = 100000
factors = ((frozenset(factors(i)), i) for i in range(2, n+1))
num = {k:v for k,v in factors if len(k)==2}
suma = sum(num.values())
リストでは、コードif tmp not in num:
はO(n)、それがO(lgn)辞書で。
編集:dictはハッシュに基づいているため、ライナーリスト検索よりもはるかに高速です。これを指摘してくれた@ user2357112に感謝します。
辞書を使用する「マジックソース」が、辞書がキーと値のペアで構成されているという事実にあることは、ほぼ確実です。
リストでは、配列を扱います。つまり、forループは、すべてのレコードをループするために、リスト内のインデックス0から開始する必要があります。
ディクショナリは、最初の「ゴーラウンド」で問題のキーと値のペアを見つけてそれを返す必要があるため、速度が...
基本的に、キーと値のペアのセットのメンバーシップをテストする方が、リスト全体で値を検索するよりもはるかに高速です。リストが大きくなるほど遅くなります...しかし、常にそうであるとは限りません。リストが速くなるシナリオもあります...しかし、これがあなたの探している答えになると思います