Pythonがハッシュテーブルを使用してdictを実装しますが、Red-Black Treeは使用しないのはなぜですか?[閉まっている]


11

Pythonがハッシュテーブルを使用してdictを実装しますが、Red-Black Treeは使用しないのはなぜですか?

キーは何ですか?パフォーマンス?


2
あなたの研究を共有することは皆を助けます。あなたが何を試みたのか、なぜあなたのニーズに合わなかったのか教えてください。これは、あなたが時間をかけて自分自身を助けようとしていること、明白な答えを繰り返すことから私たちを救うこと、そして何よりもあなたがより具体的で関連性のある答えを得るのを助けることを示しています。参照してください掲載する方法
GNAT

回答:


16

これは、Python固有ではない一般的な回答です。

アルゴリズムの複雑さの比較

       | Hash Table  |   Red-Black Tree    |
-------+-------------+---------------------+
Space  | O(n) : O(n) | O(n)     : O(n)     |
Insert | O(1) : O(n) | O(log n) : O(log n) |
Fetch  | O(1) : O(n) | O(log n) : O(log n) |
Delete | O(1) : O(n) | O(log n) : O(log n) |
       | avg  :worst | average  : worst    |

ハッシュテーブルの問題は、ハッシュが衝突する可能性があることです。衝突を解決するためのさまざまなメカニズムがあります。たとえば、オープンアドレス指定や個別の連鎖などです。絶対的な最悪のケースは、すべてのキーが同じハッシュコードを持っていることです。この場合、ハッシュテーブルはリンクリストに劣化します。

他のすべての場合、ハッシュテーブルは実装が簡単で優れたパフォーマンスを提供する優れたデータ構造です。欠点は、テーブルをすばやく成長させてエントリを再配布できる実装は、実際に使用されているメモリとほぼ同じ量のメモリを浪費する可能性が高いことです。

RBツリーは自己均衡型であり、最悪の場合にアルゴリズムの複雑さを変更しません。ただし、実装はより困難です。また、それらの平均的な複雑さは、ハッシュテーブルの複雑さよりも悪いです。

キーの制限

ハッシュテーブル内のすべてのキーはハッシュ可能で、相互の同等性が比較可能でなければなりません。これは、文字列または整数では特に簡単ですが、ユーザー定義型に拡張することもかなり簡単です。Javaのような一部の言語では、これらのプロパティは定義により保証されています。

RB-Treeのキーには完全な順序が必要です。各キーは他のキーと比較可能でなければならず、2つのキーはより小さい、より大きい、または等しいかを比較する必要があります。この順序付けの等価性は、セマンティックの等価性と同等でなければなりません。これは整数や他の数値については簡単で、文字列についても非常に簡単です(順序は一貫している必要があり、外部からは観察できないため、順序はロケールを考慮する必要はありません[1])。 。それらの間の何らかの比較が可能でない限り、異なるタイプのキーを持つことは絶対に不可能です。

[1]:実際、ここは間違っています。2つの文字列はバイトが等しくない場合がありますが、一部の言語の規則に従っては同等です。たとえば、2つの等しい文字列のエンコード方法が異なる1つの例については、Unicode正規化を参照してください。Unicode文字の構成がハッシュキーにとって重要であるかどうかは、ハッシュテーブルの実装が知ることができないものです。

RBツリーキーの安価な解決策は、最初に同等性をテストし、次に同一性を比較する(つまり、ポインターを比較する)と考えられるかもしれません。ただし、この順序は推移的ではありません。if a == bおよびの場合id(a) > id(c)、それに続く必要がありますがid(b) > id(c)、ここでは保証されません。そのため、代わりに、キーのハッシュコードをルックアップキーとして使用できます。ここでは、順序付けは正しく機能しますが、RBツリー内の同じノードに割り当てられる、同じハッシュコードを持つ複数の個別のキーになる可能性があります。これらのハッシュの衝突を解決するために、ハッシュテーブルと同様に個別のチェーンを使用できますが、これはハッシュテーブルの最悪の場合の動作、つまり両方の最悪の動作も継承します。

その他の側面

  • ハッシュテーブルは本質的に単なる配列であるため、ハッシュテーブルはツリーよりもメモリの局所性が優れていると考えています。

  • 両方のデータ構造のエントリには、かなり高いオーバーヘッドがあります。

    • ハッシュテーブル:キー、値、および個別のチェーンの場合の次のエントリポインター。また、ハッシュコードを保存すると、サイズ変更を高速化できます。
    • RBツリー:キー、値、色、左の子ポインター、右の子ポインター。色は1ビットですが、アライメントの問題により、2の累乗のサイズのメモリブロックしか割り当てられない場合、ほぼすべてのポインター、またはほぼ4つのポインターに十分なスペースを無駄に使用することになります。いずれにしても、RBツリーエントリは、ハッシュテーブルエントリよりも多くのメモリを消費します。
  • RBツリーでの挿入と削除には、ツリーの回転が含まれます。これらは実際には高価ではありませんが、オーバーヘッドが発生します。ハッシュでは、挿入と削除は単純なアクセスよりも高価ではありません(ただし、挿入時にハッシュテーブルのサイズを変更するのはO(n)努力です)。

  • ハッシュテーブルは本質的に可変ですが、RBツリーは不変の方法で実装することもできます。ただし、これはほとんど役に立ちません。


ハッシュを衝突させるための小さなRBツリーを持つハッシュテーブルを作成できますか?
aragaer

@aragaerは一般的ではありませんが、特定のケースでは可能です。ただし、衝突は通常、リンクリストによって処理されます。通常、衝突はほとんどないため、実装がはるかに簡単で、オーバーヘッドがはるかに少なく、パフォーマンスが向上します。多くの衝突が予想される場合、ハッシュ関数を変更するか、より単純なBツリーを使用できます。RBツリーのような自己分散ツリーは素晴らしいですが、多くの場合、単純に価値を追加しません。
アモン14

ツリーには、「<」をサポートするオブジェクトが必要です。ハッシュテーブルには、ハッシュ+ "="をサポートするオブジェクトが必要です。そのため、RBツリーは不可能な場合があります。しかし、実際にハッシュテーブルに大量の衝突がある場合は、キーを衝突させるための代替アルゴリズムではなく、新しいハッシュ関数が必要です。
gnasher729

1

真実である可能性のあるさまざまな理由がありますが、主な理由は次のとおりです。

  • ハッシュテーブルはツリーよりも実装が簡単です。どちらも完全に簡単ではありませんが、ハッシュテーブルは少し簡単です。また、ハッシュ関数と等式関数が必要なだけなので、法的キーのドメインへの影響はそれほど厳しくありません。ツリーは全順序関数を必要とし、それを書くのははるかに困難です。
  • ハッシュテーブルは、小さいサイズでパフォーマンスが向上する場合があります。これは非常に重要です。なぜなら、作業の大部分は理論的には大規模なデータセットのみを扱うからです。実際には、実際には、数百ではなく、数十または数百のキーでのみ機能します。小規模なパフォーマンスは重要であり、漸近分析を使用してそこに何が最適かを判断することはできません。実際に実装して測定する必要があります。

記述/保守が簡単で、一般的な使用例でパフォーマンスが勝者ですか?サインアップしてください!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.