왜 누군가가 unorder_set 대신 set을 사용합니까?


145

C ++ 0x는 다른 곳에서도 unordered_set사용할 수있는 것을 소개 boost합니다. 내가 이해하는 unordered_set것은 O(1)조회 복잡도 가있는 해시 테이블입니다 . 반면에 조회 복잡성이 set있는 나무 log(n)일뿐입니다. 왜 지구상에서 누군가 set대신에 사용 unordered_set하겠습니까? 즉 set더 이상 필요 합니까?


22
귀하의 질문은 근본적으로 더 이상 나무가 필요한지 묻고 있습니다.
Vinko Vrsalovic

2
나는 첫 번째 줄에 분명히 언급했다고 생각합니다. 이것은 어리석은 질문입니다. 나는 무언가를 놓치고 있었고 지금 나는 대답을 얻었다 :)
AraK

2
실제 이유는 물건이 흑백으로 보이지 않기 때문입니다. 그 사이에는 많은 회색과 다른 색상이 있습니다. 이 컨테이너는 도구라는 것을 기억해야합니다. 때로는 성능이 중요하지 않으며 편의성이 훨씬 더 중요합니다. 사람들이 가장 효율적인 솔루션을 찾고 있다면 우리는 "파이썬은 말할 것도없이 C ++을 절대 사용하지 않고 기계 언어로 코드를 지속적으로 작성하고 최적화 할 것입니다.
AturSams

(왜 지구상에서 누군가가 그 이름으로 암시 된 것 이상의 약속을 가지고 구현 / 인터페이스에 일반적인 이름을 사용하여없는 사람들에게는 어색한 상황을 만드는 이유는 무엇입니까?)
greybeard

답변:


219

세트의 항목을 반복하려는 사람의 경우 순서가 중요합니다.


삽입 순서 또는 연산자를 사용한 실제 비교에 따라 정렬 < >됩니까?
SomethingSothinging

2
기본적으로 std :: less를 사용하여 주문합니다. 이를 무시하고 고유 한 비교 연산자를 제공 할 수 있습니다. cplusplus.com/reference/set/set
moonshadow

또는 순서가 중요하지 않더라도 반복하고 싶을 때가 있습니다.
mfnx

319

정렬되지 않은 세트는 몇 가지 방법으로 O (1) 평균 액세스 시간을 지불해야합니다.

  • set사용 적은 메모리 보다 unordered_set같은 수의 요소를 저장하도록한다.
  • A의 원소의 소수 , A의 조회를 set할 수있는 빠른 에서 조회보다 unordered_set.
  • 많은 작업이 빠르게에서 비록 평균 경우unordered_set, 그들은 종종이 보장되는 더 나은 최악의 복잡성을 위해 set(예를 들어 insert).
  • 그건 set 종류의 요소는 당신이 순서에 액세스 그들에게 원하는 경우 유용합니다.
  • 당신은 할 수 사전 식 비교 다른 set과들 <, <=, >>=. unordered_set이러한 작업을 지원할 필요는 없습니다.


9
+1, 모든 우수한 점수. 사람들은 해시 테이블의 평균 액세스 시간 이 O (1) 인 사실을 간과하는 경향이 있습니다. 즉, 때때로 큰 지연이있을 수 있습니다. 실시간 시스템의 경우 구별이 중요 할 수 있습니다.
j_random_hacker

그러나 좋은 점 은 ( en.cppreference.com/w/cpp/container/unordered_set/operator_cmp ) unordered_sets를 비교할 수 있다고 언급되어 있습니다.
Michiel 님이 Broek 님에게

5
"소수의 요소"를 정의하십시오
Sunjay Varma

4
@SunjayVarma 일반적으로 100 개의 요소는 두 요소 사이의 좋은 차단입니다. 확실하지 않은 경우 특정 사용 사례에서 두 가지 테스트 성능을 대체 할 수있는 것은 없습니다.
Nate

3
@MichieluithetBroek 순서 ( <)가 아닌 평등 비교 만 표시 됩니다.
lisyarus

26

해시 테이블보다 트리를 선호 할 때마다.

예를 들어, 해시 테이블은 최악의 경우 "O (n)"입니다. O (1)이 평균 사례입니다. 나무는 최악의 경우 "O ( log n)"입니다.


18
/ Balanced / 트리는 최악의 경우 O (ln n)입니다. O (n) 트리 (필수적으로 링크 된 목록)로 끝날 수 있습니다.
strager

5
합리적으로 지능적인 해시 함수를 작성할 수 있다면 거의 항상 해시 테이블에서 O (1) 성능을 얻을 수 있습니다. 세트를 "순서대로"반복해야하는 해시 함수를 작성할 수 없으면 트리를 사용해야합니다. 그러나 "O (n) 최악의 성능"이 두렵기 때문에 트리를 사용해서는 안됩니다.
Justin L.

6
스 태퍼 : 욕설이 되겠군요. 그러나 우리는 일반적으로 균형 이진 검색 트리 로 구현되는 C ++ 세트에 대해 이야기하고 있습니다. 복잡성에 대해 이야기 할 실제 작업을 지정해야합니다. 이러한 맥락에서 우리는 조회에 대해 이야기하고 있음이 분명합니다.
Mehrdad Afshari

1
Justin L : 나무를 선호하는 이유 중 하나 입니다. 내 대답의 핵심은 첫 번째 줄입니다. 해시 테이블보다 트리 데이터 구조를 선호 할 때마다 . 테이블을 해시하는 데 트리가 선호되는 경우가 많이 있습니다. 해시 테이블은 특히 "거리 교차점"과 같은 것을 빨아들입니다.
Mehrdad Afshari

2
stl 트리는 거의 보편적으로 구현 된 고급 블랙 밸런스 트리 인 고급 자체 균형 트리입니다. 최악의 경우 O (n) 조회가 허용되지 않는 경우가 실제로 있습니다. 악의적 인 사용자가 특수하게 조작 된 값을 저장하여 DoS를 효과적으로 만들 수 있으므로 사용자 값을 저장하고 인터페이스를 제공하는 웹 서비스는 해시 맵을 사용하지 않아야합니다. 중요하고 시간에 민감한 시스템은 O (n) 조회, 항공 교통 관제 등을 허용하지 않을 수도 있습니다. 일반적으로 옳습니다. 기본적으로 해시 맵을 사용하고 실제로 필요한 경우에만 트리 버전을 전환하십시오.
deft_code

14

다음과 같은 경우에 세트를 사용하십시오.

  1. 우리는 순서가 지정된 데이터 (다른 요소)가 필요합니다.
  2. 데이터를 인쇄 / 액세스해야합니다 (정렬 된 순서로).
  3. 우리는 요소의 선임자 / 후임자가 필요합니다.

다음과 같은 경우 unorder_set을 사용하십시오.

  1. 고유 한 요소 집합을 유지해야하며 순서가 필요하지 않습니다.
  2. 단일 요소 액세스가 필요합니다. 즉 순회가 없습니다.

예 :

세트:

입력 : 1, 8, 2, 5, 3, 9

출력 : 1, 2, 3, 5, 8, 9

정렬되지 않은 _ 세트 :

입력 : 1, 8, 2, 5, 3, 9

출력 : 9 3 1 8 2 5 (이 순서는 해시 함수의 영향을 받음)

주로 차이점 :

여기에 이미지 설명을 입력하십시오

참고 : set예를 vector들어 키로 사용 하는 경우 (더 편리한 경우 )

set<vector<int>> s;
s.insert({1, 2});
s.insert({1, 3});
s.insert({1, 2});

for(const auto& vec:s)
    cout<<vec<<endl;   // I have override << for vector
// 1 2
// 1 3 

그 이유는 이유 vector<int>의 핵심으로 될 수 set있기 때문에 vector무시 operator<.

그러나 벡터에 해시 함수가 없기 때문에 unordered_set<vector<int>>를 사용하는 경우에 대한 해시 함수를 만들어야 vector<int>하므로 다음과 같이 정의해야합니다.

struct VectorHash {
    size_t operator()(const std::vector<int>& v) const {
        std::hash<int> hasher;
        size_t seed = 0;
        for (int i : v) {
            seed ^= hasher(i) + 0x9e3779b9 + (seed<<6) + (seed>>2);
        }
        return seed;
    }
};

vector<vector<int>> two(){
    //unordered_set<vector<int>> s; // error vector<int> doesn't  have hash function
    unordered_set<vector<int>, VectorHash> s;
    s.insert({1, 2});
    s.insert({1, 3});
    s.insert({1, 2});

    for(const auto& vec:s)
        cout<<vec<<endl;
    // 1 2
    // 1 3
}

어떤 경우 unordered_set에는 더 복잡 하다는 것을 알 수 있습니다 .

주로 인용 : https://www.geeksforgeeks.org/set-vs-unordered_set-c-stl/ https://stackoverflow.com/a/29855973/6329006


6

std :: set은 Standard C ++의 일부이고 unorder_set은 그렇지 않기 때문입니다. C ++ 0x는 표준이 아니며 Boost도 아닙니다. 우리 중 많은 사람들에게 이식성은 필수적이며 이는 표준을 고수한다는 의미입니다.


2
내가 그를 올바르게 이해한다면, 그는 사람들이 현재 아직도 세트를 사용하는 이유를 묻지 않습니다. 그는 C ++ 0x에 대해 스스로에게 알리고있다.
Johannes Schaub-litb

2
아마도. 나는 모든 사람들이 해시 테이블과 나무가 다른 문제를 해결한다는 것을 알고 있다고 생각했습니다.

21
글쎄요, 지금 은 표준입니다 (몇 년이 걸렸습니다)
Clayton Hughes

6

스위프 라인 알고리즘을 고려하십시오. 이 알고리즘은 해시 테이블에서 완전히 실패하지만 균형 트리에서 아름답게 작동합니다. 스위프 라인 알고리즘의 구체적인 예를 제공하려면 fortune의 알고리즘을 고려하십시오. http://en.wikipedia.org/wiki/Fortune%27s_algorithm


1
나는 그러한 언급이 질문에 비해 너무 복잡하다고 생각합니다. (내가 그것을 찾아야했다)
hectorpal

3

다른 사람들이 이미 언급 한 것 외에 한 가지 더. unordered_set에 요소를 삽입하기위한 예상 상각 복잡도 O 동안 (1), 때때로 그것은 것이다 해시 테이블 요구가 재구성 될 수 있기 때문에 (변경 버킷 요구의 수) O (N)를 취 - 짝수로 '좋은'해시 함수. 벡터에 요소를 삽입하는 것처럼 O (n)이 걸리는 것처럼 기본 배열을 재 할당해야하기 때문입니다.

세트에 삽입하는 데는 항상 최대 O (log n)가 걸립니다. 이것은 일부 응용 프로그램에서 바람직 할 수 있습니다.


3

분류 된 재산에 대해 주목할 가치가있는 또 하나의 사실을 용서하십시오.

컨테이너 에 데이터 범위 를 원하는 경우 ( 예 : set 에 시간을 저장 했으며 2013-01-01에서 2014-01-01까지의 시간을 원함)

들어 unordered_set 는 불가능하다.

물론이 예제는 mapunordered_map 사이의 사용 사례에 대해 더 설득력이 있습니다 .


3

g++ 6.4 stdlibc ++ 주문과 비 정렬 세트 벤치 마크

이 주요 Linux C ++ 구현을 벤치마킹하여 차이점을 확인했습니다.

여기에 이미지 설명을 입력하십시오

전체 벤치 마크 세부 사항 및 분석은 다음과 같습니다. C ++에서 STL 세트의 기본 데이터 구조는 무엇입니까? 그리고 나는 그것들을 여기서 반복하지 않을 것입니다.

"BST"는 "테스트 된"을 의미 std::set하고 "해시 맵"은 "테스트 된"을 의미 std::unordered_set합니다. "힙"은 std::priority_queue내가 분석 한 : 이진 검색 트리 (BST)입니다.

빠른 요약으로 :

  • 그래프는 이러한 조건에서 해시 맵 삽입이 100k 개가 넘는 항목이있을 때 항상 훨씬 빠르며 항목 수가 증가함에 따라 차이가 커짐을 분명히 보여줍니다.

    이 속도 향상의 비용은 효율적으로 순회 할 수 없다는 것입니다.

  • 곡선은 순서 std::set가 BST 기반이며 std::unordered_set해시 맵 기반 임을 분명히 나타냅니다 . 참조 답변에서 GDB 단계를 통해 코드를 디버깅한다는 것을 추가로 확인했습니다.

mapvs에 대한 비슷한 질문 unordered_map: 사소한 키의 경우 unorder_map보다 map을 사용하면 어떤 이점이 있습니까?


1

다른 형식으로 변환하려는 경우 관계를 맺는 것이 편리하다고 말하고 싶습니다.

액세스하는 것이 더 빠르지 만 인덱스를 작성하거나 액세스 할 때 사용되는 메모리 또는 인덱스를 빌드하는 시간이 더 클 수도 있습니다.


+1, Big Oh 표기법은 상수 요인을 숨기고 일반적인 문제 크기의 경우 종종 가장 중요한 상수 요인입니다.
j_random_hacker

1

항목을 정렬하려면 unordered_set 대신 set을 사용하십시오. unorder_set은 저장된 순서가 중요하지 않은 경우 set을 초과하여 사용됩니다.


1

이 답변은 10 년이 늦었지만 std::unordered_set보안상의 단점도 있습니다.

해시 함수를 예측할 수있는 경우 (임의로 무작위 소금과 같은 대응책을 적용하지 않는 한 일반적으로 그렇습니다) 공격자는 해시 충돌을 일으켜 모든 삽입 및 조회에 O (n) 시간이 걸리는 데이터를 수작업으로 만들 수 있습니다. .

이는 매우 효율적이고 우아한 서비스 거부 공격에 사용될 수 있습니다.

내부적으로 해시 맵을 사용하는 많은 (대부분?) 언어 구현은 다음과 같습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.