なぜstd :: mapはツリーベースであるのに対し、pythonのdictはハッシュテーブルとして実装されていますか?

なぜ1つの言語がツリーを使用し、別の言語が一見類似したデータ構造のためにハッシュテーブルを使用するのか?

C ++のマップとPythonのdict

関連する質問は、ハッシュテーブルのパフォーマンスに関するものです。
下記のハッシュテーブルの理解に感謝してください。

ツリーにはO(log n)があることが保証されています。
ハッシュテーブルは、衝突の可能性があるため入力が以前に分かっていない限り保証されません。 問題サイズが大きくなるにつれて、ハッシュテーブルのパフォーマンスはO(n)に近づくと思う傾向があります。
なぜなら、問題サイズが大きくなるにつれてテーブルサイズを動的に調整するハッシュ関数について聞いたことがないからです。

したがって、ハッシュテーブルは特定の範囲の問題サイズにのみ役立ちます。そのため、ほとんどのDBはハッシュテーブルよりもツリーを使用しています。

10
@ジョイ:したがって、ハッシュテーブルは、サイズが大きいときにはメモリで動作するのがむしろ困難です。一方、その点では自然に木が適しています。あなたを正しく理解していますか?
追加された 著者 eugene,
通常、使用するハッシュはテーブルのサイズより何倍も大きいので、テーブルのサイズを変更して別のモジュラスを使用することができます。データベースがツリー(異なるツリーも)を使用する理由は、ディスクへのアクセスを最小限に抑え、メモリ内の実際の構造を大量に保持しているため、無駄な頭の動き(SSDではなく、アクセスは大きな要因です)。
追加された 著者 Joey,
サイズのバランスのとれたバイナリツリーに基づいたHaskell Data.Map
追加された 著者 joaquin,
不均衡な木がリストになる特別な場合には、木はO(n)で動作することができます。バランスのとれたツリーを作るためには、バランスをとるために時間を費やさなければなりません。
追加された 著者 fogbit,

4 答え

新しいC ++標準には、 std :: unordered_map 型のがあります。ハッシュテーブル。 IIRCはこれまでの標準にも入りたいと思っていましたが、議論の中で十分な時間がなかったので省略されました。しかし、ほとんどの一般的なコンパイラは何年も何らかの形でそれを提供していました。

言い換えれば、あまり心配しないでください。手元のタスクに適切なデータ構造を使用してください。


ハッシュテーブルについての理解は、不正確です。

テーブルを動的に調整するハッシュ関数について聞いたことがありません   問題のサイズが大きくなるにつれてサイズが大きくなる

すべての深刻なハッシュテーブルの実装は、より大きな配列を割り当て、すべてのキーを再ハッシュすることによって、入力を増やすために動的に調整します。この操作は高価ですが、(まれにしか実行されないように)正しく設計されていれば、パフォーマンスはまだ償却されます(O)。

18
追加された

ハッシュテーブル(およびそれらを使用する人)に対するあなたの理解には欠陥があります。

問題は、ハッシュテーブルはむしろあいまいであるということです。フードの下には多くの実装がありますが、まずBST(バイナリ検索ツリー)の使用について話しましょう。


なぜC ++はバイナリ検索ツリーを使用していますか?

C ++は、commiteeによって設計されていますが、BST(Red-Black TreeとAVL Tree)の最も普及した実装はほぼ同じ特性を持っていますが、幅広い特性につながるハッシュテーブルの実装が数多くあります。したがって、彼らはハッシュテーブルを完全に拒否せず、選択する特性とユーザーに公開する詳細を決めることができませんでした。

James Kanzeのコメントを参照してください。提案が遅すぎました。JamesはStepanovが最初に提案しなかった理由について興味深い質問をします。私はまだ選択肢の数が犯人であると思われます。

データベースで検索ツリーを使用する理由

まず第一に、データベースソフトウェアで解決しよう。私はOracleデータベースを広く採用しており、SQLデータベースの典型的なものであるため、Oracleを採用する予定です。オラクル社は、ビットマップと検索ツリーの2種類の索引を提供しています。

注:バイナリ検索ツリーは使用しませんが、代わりにIOとキャッシュの利便性が高いB +ツリーを使用します

ハッシュテーブルと検索ツリーには基本的な違いがあります。後者はソートされています。多くのデータベース操作は並べ替えを意味します。

  • n番目の要素を取得する
  • 上位n個の要素を取得する
  • [a、b]の要素を取得する

これらのケースでは、ハッシュテーブルは役に立たない。

さらに、データベースは巨大なデータセット(一般的には)を扱う必要があります。つまり、IO(ディスクの読み書き)を最小限に抑えるためにデータを整理する必要があります。ここで、検索ツリーのソートされた性質は、一緒にアクセスされる可能性が高い(索引内の)要素が、ディスクの四隅に散在する代わりに一緒にグループ化されることを意味します。

最後に、内部的に Oracleは、実行計画にハッシュ・テーブルを使用することがあります。 2組の行の交差を必要とする操作を実行すると、最適化エンジンはハッシュテーブルに(一時的な)セットを格納することが最速の方法であると判断することがあります。


さて、パフォーマンスに関して。

確かに、Search Treesのパフォーマンスは一般的によく知られており、わかりやすくO(log N)はきれいで整っています。

一方、私が言ったように、成長と収縮の両方を処理する戦略だけでなく、多くの異なるハッシュテーブルの実装が可能です...間違いなくもっと複雑です。

構造の簡単な例では、ハッシュテーブルは以下を使用することができます:

  • 公開アドレス指定:ハッシュテーブルは要素の配列であり、ハッシュは要素を配置する配列のスロットを示し、スロットがいっぱいの場合は別のスロットの配置方法があります。同じ戦略が検索に使用されます。
  • バケット:ハッシュテーブルはバケットへのポインタの配列で、ハッシュは要素を配置するバケットのスロットを示します。バケットは無限に成長することができると想定されています。

これらの2つの戦略は非常に異なる特性を持ち、後者の特性はバケット実装にも依存します(簡単な実装は簡単なリンクリストを使用することです)。

しかし、インプリメンテーションを選択しても、そのパフォーマンスはハッシュ関数の分布に基づいています。ハッシュ関数の分布は、入力シーケンス自体によって異なります。


私の個人的なアドバイス? C ++で unordered_map map の間を選択するには、ソートされた要素が必要かどうかについて質問します。ソートする必要がある場合は、 map を使用します。それ以外の場合は、 unordered_map を使用します。ほとんどの場合、パフォーマンスはまあまあまあ良いので、単なるセマンティクスです。

12
追加された
"n番目の要素を取得し、上位n個の要素を取得し、[a、b]で要素を取得する" +1 newbを教育するために+1!
追加された 著者 aitchnyu,
歴史のいくつかのポイント:C ++のほとんどは委員会によって設計されていませんでした。委員会が既存のプラクティスを標準化した大部分(例外があります)。ライブラリの多くはAlexander StepanovのSTLから来ているので、Stepanovがハッシュテーブルではなくバイナリツリーを選択したのは疑問です。なぜ、提案が提示されたときに、委員会はそれを非常に好意的に見ていたが、新しいものをそのプロセスに組み込むプロセスが遅すぎたために拒否した標準。
追加された 著者 James Kanze,
@EliBendersky:初心者の多くはパフォーマンスに慣れているので、10要素のシーケンスを使用している間は大きなOに集中します。パフォーマンスが必要な場合は、(プロファイル)を測定します。手元にあるタスクに必要なセマンティクス。
追加された 著者 Matthieu M.,
@ JamesKanze:それは私の疑惑だった私の答えを読んでそれは不明です。私はすべてそれを打ち、あなたのコメントにリダイレクトしました。
追加された 著者 Matthieu M.,
素敵な答えですが、なぜ最後の文ですか?大規模なデータセットで、典型的なルックアップの必要性(ソート不要)のために、 unordered_map map
追加された 著者 Eli Bendersky,
私はSOの答えが一般的な意味で正確になされるべきであり、ユーザーの特定の部分(パフォーマンスや大きなことを理解していない初心者)を対象にしてはならないと思います。あなたがセグメントでそれを目指すならば、少なくともそれを括弧や脚注に明示的に書き留めておいてください。さもなければ、その文脈から抜け出して、答えはちょうど正しく表示されない
追加された 著者 Eli Bendersky,

It's a more or less arbitrary choice of the language designers. In the case of C++, I suspect (but don't know for sure) that the motivation was the desire to define strict upper limits to complexity: designing a good hash function isn't trivial, and a hash table with a poor hash function performs very poorly. Another issue that might have been considered is the fact that there is an established operator for ordering (<); there is nothing similar for hashing.

Python(および他の多くの言語)の場合、多くの時間、 キーは str std :: string のように組み込みタイプになります STLが定義されているときに利用可能)、適切な ハッシュ関数。そして、すべてがオブジェクトであり、 共通の基本クラスを使用すると、 ユニバーサルベースクラスに(仮想)関数を定義することで、 hash

Finally, the C++ solution depends on a single function/operator; a hash table requires two (the hash function and equality), which must compatible, which is more error prone. A common error in Java is to define equals, but not to define hashCode; I suspect that there are Python classes which make the same mistake (defining __cmp__ or __eq__, but not __hash__). Of course, seeing the number of times people mess up the < operator in C++, I'm not sure that it's that safe either:-).

5
追加された

Pythonのハッシュテーブルは決して2/3を超えていません。サイズが大きくなるにつれてサイズが変更されます(サイズ8から始まり、サイズが50000まで4倍になり、その後倍増します)。これにより、O(1)の挿入、削除、ルックアップを償却します。余分な衝突は可能ですが、まれです。

4
追加された