2016-08-23 4 views
3

세 번째 매개 변수 KeyEqual의 목적은 std::unordered_set에 무엇입니까? 해시 고유성이 충분하지 않습니까?std :: unordered_set의 KeyEqual은 무엇입니까?

template< 
    class Key, 
    class Hash = std::hash<Key>, 
    class KeyEqual = std::equal_to<Key>, 
    class Allocator = std::allocator<Key> 
> class unordered_set; 

이 질문이 순진하게 들리면 죄송합니다. 파이썬/PHP에서 C++로 옮기기 :)

현재로서는 KeyEqual의 구현은 항상 Hash impl을 복제합니다. 그래서 제대로했는지 궁금합니다.

+3

해시 충돌에 대해 들어 본 적이 없습니까? 두 객체가 동일한 해시를 생성하면 동등성 술어가 동등성을 비교하는지 여부를 결정하는 데 사용됩니다. – Praetorian

+1

해시 독창성은 충분합니까? 당신의 키가 int이고 해쉬 함수가'[] (int i) {return i % 10; }'? –

+0

['unordered_set'] (http://www.cplusplus.com/reference/unordered_set/unordered_set/) 문서의 문제점은 무엇입니까? 이전 코멘트의 해시 충돌은 이유 N 1이지만 거의 모든 컨테이너가 비교 작업의 사용자 정의를 허용합니다. yu가 키를 비교할 수있는 방법이 필요하거나 비교 연산자가 없으면 키 유형을 사용하면 어떻습니까? – mvidelgauz

답변

3

하지만 해시 충돌이 발생하면 어떻게 될까요?

enter image description here

사진은 동일한 해시 값을 갖는 상이한 두 요소가 발생하는 경우를 보여준다. 결과적으로, 해시 값은 이 아닌 일 수 있습니다. 해시는 고유해야합니다.


에 따옴표 refstd::unordered_set의 :

는 내부적으로 unordered_set의 요소는 빠르게 액세스 할 수 있도록 모든 특정 순서로 정렬하지만, 자신의 해시에 값을 따라 버킷으로 구성되지 않습니다 직접적으로 평균 시간 복잡도가 일정한 에 의해 개별 요소에 직접 전달됩니다.

그래서 버킷에는 하나 이상의 요소가있을 수 있습니다! 이 두 요소는 동일한 해시 값을 가지며 고유 한 것으로 보장되지 않습니다!


독특한이 보장되는 유일한 것은 입니다!

+0

충돌을 이해합니다. 하지만 충돌에 신경 쓰지 않는다면 어떨까요? 단순히 "해시가 100 % 독특합니다"라고 말하면 어떨까요? 같은 요소를 설정하려고하면 예외를 주거나이 요소를 바꿉니 까? Mybe 다른 질문을 위해 ..하지만 .. – spajak

+0

예 @spajak 다른 질문입니다. 컨테이너는 상자에서 꺼내서 그렇게하지 않습니다. 왜냐하면 그렇게 설계되지 않았기 때문입니다. 나는 그것이 사용하는 해싱 정책이 충돌을 허용한다는 것을 의미합니다. 그것이 평등을 결정하는 데 '키'가 필요한 이유입니다. 자신 만의 컨테이너를 만들어야한다. (STL로부터 상속 받는다.) 원한다면 예외를 던질 것이다. 나는 당신의 질문을 편집했다, 당신이 꺼리지 않기를 바란다. :) – gsamaras

1

간단히 말해 집합은 두 개의 키가 같은지 여부를 알아야합니다. KeyEqual은이를 수행하기위한 메커니즘입니다.

동일한 값을 비교하지 않는 두 개의 키는 같은 값으로 해시 할 수 있으며 그 값을 확인해야합니다.

1

다른 값이 반드시 다른 해시를 가질 필요는 없습니다. 예를 들어 std::string 개의 개체가 실제로 무한하지만 실제로는 2^N std::size_t 개의 개체 만 있으므로이라는 결과가 나오므로 해시 충돌은 피할 수없는 경우가 있습니다.

따라서 std::unordered_setstd::unordered_map은 해시 값이 동일해도 요소가 동일하거나 동일하지 않은지 테스트하는 방법이 필요합니다.

1

그냥 간단 모드 % 작업을 수행하는 해시 함수, 예를 들면 int의 세트를 보자

struct IntMod { 
    constexpr std::size_t operator()(int i) const { return i % 10; } 
}; 

std::unordered_set<int, IntMod> s; 

이 쉽게 해시 충돌이 발생할 수 있습니다, 그것은 당신이 할 필요가 발생했을 때 키가 이미 존재 하는지를 알기 위해 키를 비교할 수 있습니다.

s.insert(25); // hash == 5 
s.insert(35); // hash == 5 
assert(*s.find(25) == 25); // both 25 and 35 are present despite the same hash 
assert(*s.find(35) == 35); 

우리는 단지뿐만 아니라 해시 함수를 사용 (당신이 기본적으로 할 제안처럼)는 제 2 삽입에 나누기 KeyEqual를 추가하는 경우.

struct IntEq { 
    constexpr bool operator()(int a, int b) const { 
    return IntMod{}(a) == IntMod{}(b); 
    } 
}; 

std::unordered_set<int, IntMod, IntEq> s; 
s.insert(25); // hash == 5 
s.insert(35); // hash == 5 
assert(*s.find(25) == 25); 
assert(*s.find(35) == 35); // now this fails. s.find(35) returns iterator to 25 
+0

's.insert (35)'가 실패하거나 이전 요소를 대체해야합니다. 하지만 그것은 내가 생각하고 conatainer 다를 수 있습니다 :) – spajak

+0

@ spajak 그냥 '35'이미 존재한다고 생각합니다.어떻게해야 실패 할 것이라고 생각합니까? –

+0

실패에 의해 나는 예외적 인 예외를 의미했다. – spajak