set ()은 어떻게 구현됩니까?


151

사람들은 set파이썬의 객체에 O (1) 멤버쉽 검사가 있다고 말합니다 . 이것을 허용하기 위해 내부적으로 어떻게 구현됩니까? 어떤 종류의 데이터 구조를 사용합니까? 그 구현에 어떤 다른 의미가 있습니까?

여기에있는 모든 대답은 실제로 깨달았지만 하나만 받아 들일 수 있으므로 원래 질문에 가장 가까운 대답으로 갈 것입니다. 정보 주셔서 감사합니다!

답변:


139

이 스레드 에 따르면 :

실제로 CPython의 세트는 더미 값 (키가 세트의 멤버 임)을 가진 사전과 같은 것으로 구현되며, 이러한 값 부족을 악용하는 최적화가 있습니다.

기본적으로 a set는 기본 데이터 구조로 해시 테이블을 사용합니다. 해시 테이블에서 항목을 찾는 것이 평균적으로 O (1) 작업이므로 O (1) 멤버쉽 확인에 대해 설명합니다.

Achim Domma 에 따르면 대부분 구현 에서 잘라내어 붙여 넣는 set에 대한 CPython 소스 코드를 찾아 볼 수도 있습니다 .dict


18
IIRC는, 원래의 set구현은 사실 이었다 dict 더미 값으로하고, 나중에 최적화되었다.
dan04

1
최악의 시나리오는 크지 않습니까? 시간이 O (n) 인 인스턴스를 찾을 수 있으면 O (n)입니다. 지금은 모든 튜토리얼에서 아무것도 이해하지 못합니다.
Claudiu Creanga

4
아니요, 평균 사례는 O (1)이지만 최악의 경우는 해시 테이블 조회의 경우 O (N)입니다.
Justin Ethier

4
@ClaudiuCreanga 이것은 오래된 의견이지만, 명확히하기 위해 : big-O 표기법은 사물의 성장률에 대한 상한을 알려주지 만 평균 사례 성능의 상한을 상한으로 할 수 있으며 최악의 경우의 성장을 별도로 상한으로 할 수 있습니다 공연.
커크 보이어

79

사람들이 세트에 O (1) 회원 확인이 있다고 말하면 평균 사례 에 대해 이야기하고 있습니다. 에서 최악의 경우 (모든 해시 값이 충돌 할 때) 멤버 검사가 O (N)이다. 시간 복잡성에 대한 파이썬 위키를 참조하십시오 .

위키 백과 문서는 말한다 최상의 경우 크기 조정하지 않는 해시 테이블에 대한 시간 복잡도를 O(1 + k/n). Python 세트는 크기가 조정되는 해시 테이블을 사용하므로이 결과는 Python 세트에 직접 적용되지 않습니다.

위키 피디 기사에있어서 약간은에 대해 말한다 평균 케이스와 시간 복잡도는 간단 균일 해싱 함수를 가정하고 O(1/(1-k/n)), k/n일정에 의해 제한 될 수 c<1.

Big-O는 점근 적 행동만을 n → ∞로 나타냅니다. k / n은 n과 관계 없이 상수 c <1에 의해 제한 될 수 있으므로 ,

O(1/(1-k/n))보다 더 큰 없습니다 O(1/(1-c))에 해당하는 O(constant)= O(1).

따라서 균일 한 간단한 해싱을 가정 할 때 평균적 으로 파이썬 세트에 대한 멤버십 확인은 O(1)입니다.


14

나는 일반적인 실수, set조회 (또는 그 문제에 대한 해시 테이블)가 O (1)이 아니라고 생각합니다 .
위키 백과에서

가장 간단한 모델에서 해시 함수는 완전히 지정되어 있지 않으며 테이블 크기는 조정되지 않습니다. 최적의 해시 함수 선택을 위해 열린 주소 지정을 사용하는 크기 n의 테이블은 충돌이없고 최대 n 개의 요소를 보유하며, 성공적인 조회를위한 단일 비교와 연결 및 k 키가있는 크기 n의 테이블은 최대 값이 최소입니다. 조회에 대한 (0, kn) 충돌 및 O (1 + k / n) 비교 해시 함수의 최악의 선택의 경우, 모든 삽입시 충돌이 발생하고 해시 테이블은 선형 검색으로 퇴보합니다. 삽입 당 Ω (k)의 상각 비교와 성공적인 조회를 위해 최대 k 개의 비교가 이루어집니다.

관련 : 자바 해시 맵은 O (1) 정말?


4
그러나 그들은 항목을 찾는 데 일정한 시간이 걸립니다 : python -m timeit -s "s = set (range (10))" "5 in s"10000000 루프, 루프 당 0.0642 usec 중 최대 <-> python- m timeit -s "s = set (range (10000000))" "5 in s"10000000 루프, 루프 당 3 : 634,000 usec 중 최고 ... MemoryErrors를 발생시키지 않는 가장 큰 세트
Jochen Ritzel

2
@ THC4k 당신이 증명 한 전부는 X를 찾는 것이 일정한 시간에 이루어 졌다는 것입니다. 그러나 X + Y를 찾는데 시간이 O (1)과 거의 같은 시간이 걸리는 것은 아닙니다.
Shay Erlichmen

3
@intuited : 그렇습니다. 그러나 위의 테스트 실행으로 "485398"을 찾거나 끔찍한 충돌 공간에있을 수있는 다른 숫자를 동시에 찾을 수 있음을 증명하지는 않습니다. 그것은 같은 시간에 다른 크기의 해시에서 동일한 요소를 찾는 것이 아니라 (실제로 전혀 필요하지는 않음) 현재 테이블에서 같은 시간에 각 항목에 액세스 할 수 있는지 여부입니다. 일반적으로 항상 충돌이 있기 때문에 해시 테이블에서는 불가능한 것입니다.
Nick Bastin

3
즉, 조회 시간은 저장된 값의 수에 따라 달라집니다. 충돌 가능성이 높아지기 때문입니다.
intuited

3
@intuited : 아니오, 맞지 않습니다. 저장된 값의 수가 증가하면 Python은 해시 테이블의 크기를 자동으로 증가시키고 충돌 속도는 거의 일정하게 유지됩니다. 균등하게 분산 된 O (1) 해시 알고리즘을 가정하면 해시 테이블 조회는 O (1)로 상각 됩니다. 비디오 프리젠 테이션 "Mighty Dictionary" python.mirocommunity.org/video/1591/…
Lie Ryan

13

우리 모두는 소스에 쉽게 접근 할 수 있으며 , 앞의 주석은 set_lookkey()다음과 같습니다.

/* set object implementation
 Written and maintained by Raymond D. Hettinger <python@rcn.com>
 Derived from Lib/sets.py and Objects/dictobject.c.
 The basic lookup function used by all operations.
 This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.
 The initial probe index is computed as hash mod the table size.
 Subsequent probe indices are computed as explained in Objects/dictobject.c.
 To improve cache locality, each probe inspects a series of consecutive
 nearby entries before moving on to probes elsewhere in memory.  This leaves
 us with a hybrid of linear probing and open addressing.  The linear probing
 reduces the cost of hash collisions because consecutive memory accesses
 tend to be much cheaper than scattered probes.  After LINEAR_PROBES steps,
 we then use open addressing with the upper bits from the hash value.  This
 helps break-up long chains of collisions.
 All arithmetic on hash should ignore overflow.
 Unlike the dictionary implementation, the lookkey function can return
 NULL if the rich comparison returns an error.
*/


...
#ifndef LINEAR_PROBES
#define LINEAR_PROBES 9
#endif

/* This must be >= 1 */
#define PERTURB_SHIFT 5

static setentry *
set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)  
{
...

2
이 답변은 C 구문 강조 에서 도움이 될 것 입니다. 주석의 파이썬 구문 강조는 정말 나빠 보입니다.
user202729

"이것은 우리에게 선형 프로빙과 개방형 어드레싱의 하이브리드를 남겨둔다"는 의견에 관해서는, en.wikipedia.org/wiki/Open_addressing ? 따라서 선형 프로빙은 개방형 주소 지정의 하위 유형이며 주석은 의미가 없습니다.
Alan Evangelista

2

조금을 사이에 많은 차이를 강조하기 위해 set's그리고 dict's여기 것은로부터 발췌 한 것입니다 setobject.cdicts에 대한 일련의의 주요 차이점을의 명확하게 코멘트 섹션.

세트의 사용 사례는 조회 키가 존재할 가능성이 높은 사전과 상당히 다릅니다. 반대로 세트는 주로 요소의 존재를 미리 알 수없는 멤버쉽 테스트에 관한 것입니다. 따라서 세트 구현은 발견 된 사례와 발견되지 않은 사례 모두에 대해 최적화해야합니다.

github의 소스

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