파이썬 : 룩업 테이블에 대한 목록 대 Dict


169

나는 어떤 종류의 룩업 테이블에 넣어야 할 약 1 천만 개의 값을 가지고 있으므로 어느 것이 더 효율적인 목록 이나 딕트 인지 궁금합니다 .

나는 당신이 둘 다 이와 같은 것을 할 수 있다는 것을 안다.

if something in dict_of_stuff:
    pass

if something in list_of_stuff:
    pass

내 생각은 dict이 더 빠르고 효율적일 것입니다.

당신의 도움을 주셔서 감사합니다.

편집 1
내가하려는 일에 대한 조금 더 많은 정보. 오일러 문제 92 . 계산 된 값이 모두 준비되었는지 확인하기 위해 룩업 테이블을 만들고 있습니다.

편집 2
효율성을 검색하십시오.

편집 3
값과 관련된 값이 없습니다 ... 그래서 세트 가 더 좋습니까?


1
무엇 측면에서 효율성? 끼워 넣다? 조회? 메모리 소비? 순수한 가치의 존재 여부를 확인하고 있습니까, 아니면 관련 메타 데이터가 있습니까?
truppo

참고로, 특정 문제에 대해 천만 개의 목록이나 사전이 필요하지 않지만 훨씬 더 작은 목록이 필요합니다.
sfotiadis

답변:


222

속도

목록의 조회는 O (n)이고, 사전의 조회는 데이터 구조의 항목 수와 관련하여 O (1)로 상각됩니다. 값을 연관시킬 필요가 없으면 세트를 사용하십시오.

기억

사전과 세트 모두 해싱을 사용하며 오브젝트 스토리지에만 사용하는 것보다 훨씬 많은 메모리를 사용합니다. Beautiful Code의 AM Kuchling에 따르면 구현시 해시 2/3가 가득 차도록 유지하려고하므로 약간의 메모리가 낭비 될 수 있습니다.

업데이트 된 질문에 따라 즉시 새 항목을 추가하지 않으면 목록을 정렬하고 이진 검색을 사용하는 것이 좋습니다. 이것은 O (log n)이며 문자열의 경우 속도가 느려질 수 있으며 자연스러운 순서가없는 객체에서는 불가능합니다.


6
예, 그러나 내용이 절대 바뀌지 않으면 일회성 작업입니다. 이진 검색은 O (log n)입니다.
Torsten Marek

1
@ John Fouhy : ints는 해시 테이블에 저장되지 않으며 포인터 만 있습니다. 즉, int에 대해 40M (실제로 많은 것이 작을 때)이 40M이고 해시 테이블에 60M이 있습니다. 요즘은 그다지 큰 문제가 아니라는 점에 동의하지만 여전히 명심해야합니다.
Torsten Marek가

2
이것은 오래된 질문이지만, 상각 된 O (1) 은 매우 큰 세트 / 딕트에 대해서는 맞지 않을 수 있다고 생각 합니다. wiki.python.org/moin/TimeComplexity 에 따른 최악의 시나리오 는 O (n)입니다. 평균 시간이 O (1)에서 벗어나 O (n)에 수렴하기 시작하는 시점의 내부 해싱 구현에 달려 있다고 생각합니다. 최적의 세트 크기를 가져야하는 한, 쉽게 식별 할 수있는 속성 (첫 번째 숫자 값, 두 번째, 세 번째 값 등)을 기반으로 전역 세트를 더 작은 섹션으로 분할하여 조회 성능을 향상시킬 수 있습니다 . .
Nisan.H

3
@TorstenMarek 혼란 스럽습니다. 에서 이 페이지 , 목록 조회 O (1)이며, DICT 조회 당신이 말한 것과 반대입니다 O (N)입니다. 내가 오해하고 있습니까?
temporary_user_name

3
@Aerovistae 해당 페이지의 정보를 잘못 읽은 것 같습니다. 목록 아래에 "x in s"에 대한 O (n)이 있습니다 (조회). 또한 set 및 dict 조회를 O (1) 평균 사례로 표시합니다.
Dennis

45

dict는 해시 테이블이므로 키를 찾는 것이 정말 빠릅니다. dict과 list 사이에서 dict가 더 빠릅니다. 그러나 연결할 값이 없으면 세트를 사용하는 것이 좋습니다. "테이블"부분이없는 해시 테이블입니다.


편집 : 새로운 질문, 예, 세트가 더 좋습니다. 하나는 1로 끝나는 시퀀스와 다른 하나는 89로 끝나는 시퀀스에 대해 2 개의 세트를 만들면됩니다. 세트를 사용하여이 문제를 성공적으로 해결했습니다.


35

set()정확히 당신이 원하는 것입니다. O (1) 조회이며 dict보다 작습니다.


31

나는 약간의 벤치마킹을했고 dict가 Linux의 i7 CPU에서 python 2.7.3을 실행하여 큰 데이터 세트에 대해 목록과 설정보다 빠릅니다.

  • python -mtimeit -s 'd=range(10**7)' '5*10**6 in d'

    10 루프, 루프 당 3 : 64.2 msec 이상

  • python -mtimeit -s 'd=dict.fromkeys(range(10**7))' '5*10**6 in d'

    10000000 루프, 3 : 3의 최고 루프 당 0.0759 usec

  • python -mtimeit -s 'from sets import Set; d=Set(range(10**7))' '5*10**6 in d'

    1000000 루프, 최고 3 : 3 루프 당 0.262 usec

보시다시피, dict는 목록보다 상당히 빠르며 설정보다 약 3 배 빠릅니다. 그러나 일부 응용 프로그램에서는 여전히 아름다움의 세트를 선택하려고 할 수 있습니다. 그리고 데이터 세트가 실제로 작은 경우 (<1000 요소) 목록이 잘 수행됩니다.


정확히 반대해서는 안됩니까? 목록 : 10 * 64.2 * 1000 = 642000 usec, dict : 10000000 * 0.0759 = 759000 usec 및 세트 : 1000000 * 0.262 = 262000 usec ... 그래서 세트가 가장 빠르며 목록과 예제에서 마지막으로 dict가 표시됩니다. 아니면 뭔가 빠졌습니까?
andzep

1
...하지만 여기에 대한 질문은 :이 시간은 실제로 무엇을 측정하고 있습니까? 주어진 목록, dict 또는 set에 대한 액세스 시간이 아니라 , 목록 을 작성 하기 위한 시간 및 루프 , dict, set 및 마지막으로 하나의 값을 찾아 액세스합니다. 그래서, 이것은 질문과 전혀 관련이 있습니까? ... 흥미롭지 만 ...
andzep

8
@andzep, 당신은 착각합니다. -s옵션은 timeit환경 을 설정하는 것입니다. 즉, 총 시간에 포함되지 않습니다. 이 -s옵션은 한 번만 실행됩니다. 파이썬 3.3에서는 다음과 같은 결과를 얻습니다 : gen (range)-> 0.229 usec, list-> 157 msec, dict-> 0.0806 usec, set-> 0.0807 usec. 설정 및 받아쓰기 성능은 동일합니다. 그러나 Dict는 설정보다 초기화하는 데 시간이 조금 더 걸립니다 (전체 시간 13.580 초 v. 11.803 초)
sleblanc

1
내장 세트를 사용하지 않는 이유는 무엇입니까? 실제로 내장 set ()보다 sets.Set ()에서 훨씬 더 나쁜 결과를 얻었습니다.
Thomas Guyot-Sionnest

2
@ ThomasGuyot-Sionnest 내장 세트는 python 2.4에서 도입되었으므로 제안 된 솔루션에서 왜 사용하지 않았는지 잘 모르겠습니다. python -mtimeit -s "d=set(range(10**7))" "5*10**6 in d"dict 벤치 마크와 거의 동일한 Python 3.6.0 (10000000 루프, 최고 3 : 0.0608 usec) 을 사용 하면 좋은 성능을 얻을 수 있으므로 의견에 감사드립니다.
EriF89

6

당신은 받아쓰기를 원합니다.

파이썬에서 (정렬되지 않은)리스트의 경우, "in"연산에는 O (n) 시간이 필요합니다. 많은 양의 데이터가있을 때는 좋지 않습니다. 반면 dict은 해시 테이블이므로 O (1) 조회 시간을 예상 할 수 있습니다.

다른 사람들이 지적했듯이 키 / 값 쌍이 아닌 키 만있는 경우 대신 세트 (특별한 유형의 dict)를 선택할 수 있습니다.

관련 :

  • Python Wiki : Python 컨테이너 작업의 시간 복잡성에 대한 정보.
  • SO : Python 컨테이너 작업 시간 및 메모리 복잡성

1
정렬 된 목록의 경우에도 "in"은 O (n)입니다.

2
링크 된 목록의 경우, 그렇습니다. 그러나 파이썬의 "목록"은 정렬 될 때 대부분의 사람들이 벡터를 호출하는 것으로 O (1)에서 인덱스 액세스를 제공하고 O (log n)에서 찾기 작업을 제공합니다.
zweiterlinde

in정렬 된 목록에 적용된 연산자가 정렬되지 않은 목록에 적용되는 경우 (임의의 값을 검색하는 경우)보다 성능이 우수 하다고 말하고 있습니까? (내가 벡터로 구현되는지 또는 링크 된 목록의 노드로 구현되는지는 생각하지 않습니다.)
martineau

4

데이터가 고유 한 경우 set ()이 가장 효율적이지만 두 가지 dict (독점도 필요합니다 :)


난 내 대답 %) 게시 보았을 때 나는 깨달았다
SilentGhost

2
@SilentGhost 답변이 잘못된 경우 삭제하지 않겠습니까? upvotes에 대해 너무 나쁘지만, 그 일이 (잘, 일어났다 )
Jean-François Fabre

3

@ EriF89를 보여주는 새로운 테스트 세트는이 세월이 지난 후에도 여전히 옳습니다.

$ python -m timeit -s "l={k:k for k in xrange(5000)}"    "[i for i in xrange(10000) if i in l]"
1000 loops, best of 3: 1.84 msec per loop
$ python -m timeit -s "l=[k for k in xrange(5000)]"    "[i for i in xrange(10000) if i in l]"
10 loops, best of 3: 573 msec per loop
$ python -m timeit -s "l=tuple([k for k in xrange(5000)])"    "[i for i in xrange(10000) if i in l]"
10 loops, best of 3: 587 msec per loop
$ python -m timeit -s "l=set([k for k in xrange(5000)])"    "[i for i in xrange(10000) if i in l]"
1000 loops, best of 3: 1.88 msec per loop

여기서는 일부 사용 사례에서 tuple보다 빠르며 lists메모리를 적게 사용 하는 것으로 알려진을 비교합니다 . 조회 테이블의 경우tuple 공정하지 않습니다.

모두 dictset 아주 잘 수행. 이것은 고유성에 대한 @SilentGhost 답변과 관련이있는 흥미로운 점을 제시합니다. OP에 데이터 세트에 10M 값이 있고 중복 된 값이 있는지 알 수없는 경우 요소의 세트 / dict를 병렬로 유지할 가치가 있습니다. 실제 데이터 세트 및 해당 세트 / dict에 존재하는지 테스트합니다. 10M 데이터 포인트는 10 개의 고유 한 값만 가질 수 있으며, 이는 검색 할 공간이 훨씬 작습니다!

dicts에 대한 SilentGhost의 실수는 실제로 dict을 사용하여 중복 된 데이터 (값)를 중복되지 않은 세트 (키)와 상관시켜 하나의 데이터 오브젝트를 유지하여 모든 데이터를 보유 할 수 있지만 여전히 룩업 테이블처럼 빠르기 때문입니다. 예를 들어, dict 키는 조회되는 값일 수 있으며 값은 해당 값이 발생한 가상 목록의 색인 목록 일 수 있습니다.

예를 들어, 검색 할 소스 데이터 목록이 l=[1,2,3,1,2,1,4]인 경우 검색 및 메모리를이 dict로 바꾸어 검색 및 메모리 모두에 대해 최적화 할 수 있습니다.

>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> l=[1,2,3,1,2,1,4]
>>> for i, e in enumerate(l):
...     d[e].append(i)
>>> d
defaultdict(<class 'list'>, {1: [0, 3, 5], 2: [1, 4], 3: [2], 4: [6]})

이 구술을 통해 다음을 알 수 있습니다.

  1. 경우 값이 원본 데이터 셋에 있었다 (즉, 2 in d반환 True)
  2. 어디 값이 원본 데이터 셋에 있었다 (즉 d[2]데이터가 원본 데이터 목록에서 발견 된 인덱스의 목록을 반환합니다 [1, 4])

마지막 단락의 경우 읽는 것이 합리적이지만 설명하려는 실제 코드를 보는 것이 좋습니다.
kaiser 2016 년

0

실제로 테이블에 천만 값을 저장할 필요가 없으므로 어느 쪽도 큰 문제는 아닙니다.

힌트 : 첫 번째 제곱합 연산 후 결과가 얼마나 큰지 생각해보십시오. 가장 큰 결과는 천만보다 훨씬 작을 것입니다 ...

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