web-dev-qa-db-ja.com

C ++ STL unordered_mapはどのように衝突を解決しますか?

C++ STL unordered_mapはどのように衝突を解決しますか?

http://www.cplusplus.com/reference/unordered_map/unordered_map/ を見ると、「一意のキーコンテナ内の2つの要素が同等のキーを持つことはできません」と表示されます。

これは、コンテナが実際に衝突を解決していることを意味するはずです。ただし、そのページでは、それがどのように実行されているかはわかりません。リンクリストやプローブを使用するなど、衝突を解決する方法をいくつか知っています。私が知りたいのは、c ++ STL unordered_mapがそれを解決する方法です。

51
whiteSkar

この規格では、ほとんどの人が理解していると思われるよりも、これについてもう少し多く定義しています。

具体的には、この規格には以下が必要です(§23.2.5/ 9):

順序付けられていない連想コンテナの要素は、バケットに編成されます。同じハッシュコードのキーは同じバケットに表示されます。

インターフェイスには、一定時間で実行される_bucket_count_が含まれます。 (表103)。また、バケットのサイズに比例して時間内に実行する必要がある_bucket_size_も含まれています。

基本的には、衝突連鎖を使用する実装について説明しています。コリジョンチェーンを使用する場合、すべての要件を満たすことは簡単なことと些細なことのどちらかです。 bucket_count()は、配列内の要素の数です。 bucket_size()は、衝突チェーン内の要素の数です。それらをそれぞれ一定時間と線形時間で取得するのは簡単で簡単です。

対照的に、リニアプローブやダブルハッシュなどを使用する場合、これらの要件を満たすことはほとんど不可能になります。具体的には、特定の値にハッシュされたすべてのアイテムは同じバケットに収まる必要があり、一定の時間でそれらのバケットをカウントできる必要があります。

ただし、リニアプローブやダブルハッシュなどを使用する場合、同じ値にハッシュされたすべてのアイテムを見つけるには、値をハッシュし、テーブル内の空でないアイテムの「チェーン」を調べていくつを見つける必要があることを意味します同じ値にハッシュされたものの。ただし、同じ値にハッシュされたアイテムの数では線形ではありません。同じ値にハッシュされたアイテムの数では線形ですまたは衝突する値です。

十分な追加作業といくつかの要件の意味をほぼブレークポイントまで拡張することで、衝突連鎖以外のものを使用してハッシュテーブルを作成することはほとんど不可能であり、それでも少なくとも要件を満たすことができます。 -しかし、私はそれが可能であるとは本当に確信していません、そしてそれは確かにかなり多くの余分な仕事を伴うでしょう。

要約:_std::unordered_set_(または_unordered_map_)のすべての実用的な実装は、間違いなく衝突連鎖を使用します。リニアプローブまたはダブルハッシュを使用して要件を満たすことは(わずかに)可能ですが、そのような実装では多くの損失が発生し、見返りがほとんどありません。

63
Jerry Coffin

私のタイプが衝突するタイミングを検出する方法を探しているこの答えを見つけたので、質問の目的である場合にこれを投稿します。

「コンテナ内の2つの要素が同等のキーを持つことはできない」という誤解があると思います。

以下のコードを見てください

//pseudocode
std::unordered_map<int, char> hashmap;
hashmap[5] = 'a';
hashmap[5] = 'b'; //replace 'a' with 'b', there is no collision being handled.

Jerryの答えは、キーを適切な配列インデックスに縮小するために使用する内部システムに関するものだと思います。

タイプ(バケットを使用)で衝突を処理する場合は、std::unordered_multimapそして繰り返し処理する必要があります

このコードが、生成したコンテキストなしで読み取れることを願っています。基本的には、ハッシュに関連付けられているバケット内の要素が探している要素であるかどうかを確認します。

//sp is std::shared_ptr
//memo is std::unordered_multimap< int, sp<AStarNode> >

//there's probably multiple issues with this code in terms of good design (like using int keys rather than unsigned)

bool AStar_Incremental::hasNodeBeenVisited(sp<AStarNode> node)
{
    using UMIter = std::unordered_multimap<int, sp<AStarNode> >::iterator;

    bool bAlreadyVisited = false;

    //get all values for key in O(1*)
    int hash = WorldGrid::hashGrid(node->location);
    std::pair<UMIter, UMIter> start_end = memo.equal_range(hash); //bucket range
    UMIter start = start_end.first;
    UMIter end = start_end.second;

    //hopefully this is implemented to be O(m) where m is the bucket size.
    for(UMIter bucketIter = start; bucketIter != end; ++bucketIter)
    {
        sp<AStarNode> previousNode = bucketIter->second;
        sf::Vector2i& previousVisit = previousNode->location;
        if (previousVisit == node->location)
        {
            bAlreadyVisited = true;
            break;
        }
    }

    return bAlreadyVisited;
}
0
Enigma22134