回答:
このスレッドによると:
実際、CPythonのセットはダミー値(キーはセットのメンバー)を持つ辞書のようなものとして実装されており、この値の欠如を悪用するいくつかの最適化があります。
したがって、基本的にset
は、基礎となるデータ構造としてハッシュテーブルを使用します。ハッシュテーブルで項目を検索することは、平均してO(1)操作であるため、これはO(1)メンバーシップチェックを説明します。
意欲がある場合は、セットのCPythonソースコードを参照することもできます。これは、Achim Dommaによると、ほとんどがdict
実装からのカットアンドペーストです。
セットにO(1)メンバーシップチェックがあると人々が言うとき、彼らは平均的なケースについて話している。で最悪の場合(すべてのハッシュ値が衝突したときに)メンバーシップチェックはO(N)です。時間の複雑さに関するPython wikiを参照してください。
Wikipediaの記事は述べている最良のケースのサイズ変更ではありません、ハッシュテーブルのための時間の複雑さをO(1 + k/n)
。Pythonセットはサイズ変更するハッシュテーブルを使用するため、この結果はPythonセットには直接適用されません。
ウィキペディアの記事の少し先に、平均的なケースでは、単純な均一ハッシュ関数を想定すると、時間の複雑さはO(1/(1-k/n))
でありk/n
、定数によって制限される可能性があると述べていc<1
ます。
Big-Oは、漸近的な振る舞いのみをn→∞と呼びます。k / nはnとは無関係に定数c <1でバインドできるため、
O(1/(1-k/n))
= O(1/(1-c))
と同等の大きさです。O(constant)
O(1)
したがって、均一な単純なハッシュを仮定すると、平均して、PythonセットのメンバーシップチェックはになりO(1)
ます。
私はそれをよくある間違いだと思います、set
ルックアップ(またはそのことについてはハッシュテーブル)はO(1)ではありません。
ウィキペディアから
最も単純なモデルでは、ハッシュ関数は完全に指定されておらず、テーブルのサイズは変更されません。ハッシュ関数の最良の選択のために、オープンアドレス指定のサイズnのテーブルは衝突がなく、ルックアップを成功させるための単一の比較で最大n個の要素を保持し、チェーンとkキーのサイズnのテーブルは最小の最大値を持ちますルックアップのための(0、kn)衝突とO(1 + k / n)比較。ハッシュ関数の最悪の選択では、挿入ごとに衝突が発生し、ハッシュテーブルは線形検索に縮退します。挿入ごとにΩ(k)の償却比較と、ルックアップを成功させるために最大k回の比較が行われます。
我々は、すべてに簡単にアクセスできていたソース前のコメントは、set_lookkey()
言うに:
/* set object implementation
Written and maintained by Raymond D. Hettinger <python@rcn.com>
Derived from Lib/sets.py and Objects/dictobject.c.
The basic lookup function used by all operations.
This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.
The initial probe index is computed as hash mod the table size.
Subsequent probe indices are computed as explained in Objects/dictobject.c.
To improve cache locality, each probe inspects a series of consecutive
nearby entries before moving on to probes elsewhere in memory. This leaves
us with a hybrid of linear probing and open addressing. The linear probing
reduces the cost of hash collisions because consecutive memory accesses
tend to be much cheaper than scattered probes. After LINEAR_PROBES steps,
we then use open addressing with the upper bits from the hash value. This
helps break-up long chains of collisions.
All arithmetic on hash should ignore overflow.
Unlike the dictionary implementation, the lookkey function can return
NULL if the rich comparison returns an error.
*/
...
#ifndef LINEAR_PROBES
#define LINEAR_PROBES 9
#endif
/* This must be >= 1 */
#define PERTURB_SHIFT 5
static setentry *
set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)
{
...
set's
との違いをもう少し強調するためにdict's
、setobject.c
コメントセクションからの抜粋を示します。これは、ディクショナリに対するセットの主な違いを明らかにしています。
セットの使用例は、検索されたキーが存在する可能性が高い辞書とはかなり異なります。対照的に、セットは、要素の存在が事前にわからないメンバーシップテストに関するものです。したがって、集合の実装は、見つかった場合と見つからなかった場合の両方に対して最適化する必要があります。
github上のソース
set
実装は実際にdict
はダミー値を使用しており、後で最適化されました。