69

最初に空の辞書を初期化し、for ループ (約 110,000 キー、各キーの値はリストであり、ループでも増加) で要素を辞書に追加すると、次のように速度が低下することがわかりました。 forループが行きます。

問題は、ディクショナリが初期化時にキーの数を認識しておらず、非常にスマートな処理を行っていないため、おそらくストレージの衝突が頻繁に発生し、速度が低下することです。

キーの数とそれらのキーが正確に何であるかがわかっている場合、Pythonで辞書(またはハッシュテーブル)をより効率的に機能させる方法はありますか? キーを知っていれば、ハッシュ関数をスマートに設計し(完全なハッシュ?)、事前にスペースを割り当てることができることを漠然と覚えています。

4

1 に答える 1

144

キーの数とそれらのキーが正確に何であるかがわかっている場合、Pythonで辞書(またはハッシュテーブル)をより効率的に機能させる方法はありますか? キーを知っていれば、ハッシュ関数をスマートに設計し(完全なハッシュ?)、事前にスペースを割り当てることができることを漠然と覚えています。

Python は、ディクショナリの「成長フェーズ」を高速化する事前サイズ設定オプションを公開していません。また、ディクショナリ内の「配置」を直接制御する機能も提供していません。

つまり、キーが常に事前にわかっている場合は、キーをセットに保存し、dict.fromkeys ()を使用してセットから辞書を作成できます。そのクラスメソッドは、設定されたサイズに基づいて辞書のサイズをあらかじめ設定するように最適化されており、 __hash__() を新たに呼び出すことなく辞書を作成できます。

>>> keys = {'red', 'green', 'blue', 'yellow', 'orange', 'pink', 'black'}
>>> d = dict.fromkeys(keys)  # dict is pre-sized to 32 empty slots

衝突を減らすことが目標である場合は、ディクショナリの挿入順序で実験を実行して、パイルアップを最小限に抑えることができます。(これがどのように行われるかについては、Knuth の TAOCP の アルゴリズム D に関する Brent のバリエーションを参照してください)。

辞書用の純粋な Python モデル (このようなもの) をインストルメント化することにより、別の挿入順序のプローブの加重平均数をカウントできます。たとえば、dict.fromkeys([11100, 22200, 44400, 33300])検索ごとに平均 1.75 個のプローブを挿入します。これは、 のルックアップあたりの平均プローブ数 2.25 を上回りますdict.fromkeys([33300, 22200, 11100, 44400])

別の「トリック」は、新しいキーを追加せずにサイズを大きくするように騙すことで、完全に入力された辞書のスペアを増やすことです。

 d = dict.fromkeys(['red', 'green', 'blue', 'yellow', 'orange'])
 d.update(dict(d))     # This makes room for additional keys
                       # and makes the set collision-free.

最後に、すべての衝突を排除する目的で、キーに独自のカスタム __hash__() を導入できます (おそらくgperfなどの完全なハッシュ ジェネレーターを使用します)。

于 2013-04-27T21:56:13.097 に答える