Custo da função len ()

Respostas:

341

É O (1) (tempo constante, não dependendo do comprimento real do elemento - muito rápido) em todos os tipos que você mencionou, mais sete outros como array.array.

Alex Martelli
fonte
17
Obrigado pela resposta útil! Existem tipos nativos para os quais esse não é o caso?
mvanveen
141

Chamar len () nesses tipos de dados é O (1) no CPython , a implementação mais comum da linguagem Python. Aqui está um link para uma tabela que fornece a complexidade algorítmica de muitas funções diferentes no CPython:

Página Wiki do TimeComplexity Python

James Thompson
fonte
84

Todos esses objetos mantêm o controle de seu próprio comprimento. O tempo para extrair o comprimento é pequeno (O (1) em notação O-grande) e consiste principalmente em [descrição aproximada, escrita em termos de Python, não em termos de C]: procure "len" em um dicionário e envie-o para o A função built_in len que procurará o __len__método do objeto e chamará isso ... tudo o que precisa fazer éreturn self.length

John Machin
fonte
3
Eu acho que essa é a resposta mais apropriada, pois fornece informações sobre os detalhes da implementação.
AK
por que não lengthaparece no dicionário por dir(list)?
ViFI 26/04
isto é o que eu estava procurando
Visakh Vijayan
@ ViFI Porque é apenas um exemplo. A list.lenghtvariável ilustrada é implementada em C, não em Python.
Kowalski
73

As medições abaixo fornecem evidências de len()O (1) para estruturas de dados frequentemente usadas.

Uma observação referente a timeit: Quando o -ssinalizador é usado e duas cadeias são passadas para timeita primeira, é executada apenas uma vez e não é cronometrada.

Lista:

$ python -m timeit -s "l = range(10);" "len(l)"
10000000 loops, best of 3: 0.0677 usec per loop

$ python -m timeit -s "l = range(1000000);" "len(l)"
10000000 loops, best of 3: 0.0688 usec per loop

Tupla:

$ python -m timeit -s "t = (1,)*10;" "len(t)"
10000000 loops, best of 3: 0.0712 usec per loop

$ python -m timeit -s "t = (1,)*1000000;" "len(t)"
10000000 loops, best of 3: 0.0699 usec per loop

Corda:

$ python -m timeit -s "s = '1'*10;" "len(s)"
10000000 loops, best of 3: 0.0713 usec per loop

$ python -m timeit -s "s = '1'*1000000;" "len(s)"
10000000 loops, best of 3: 0.0686 usec per loop

Dicionário (compreensão de dicionário disponível em 2.7+):

$ python -mtimeit -s"d = {i:j for i,j in enumerate(range(10))};" "len(d)"
10000000 loops, best of 3: 0.0711 usec per loop

$ python -mtimeit -s"d = {i:j for i,j in enumerate(range(1000000))};" "len(d)"
10000000 loops, best of 3: 0.0727 usec per loop

Matriz:

$ python -mtimeit -s"import array;a=array.array('i',range(10));" "len(a)"
10000000 loops, best of 3: 0.0682 usec per loop

$ python -mtimeit -s"import array;a=array.array('i',range(1000000));" "len(a)"
10000000 loops, best of 3: 0.0753 usec per loop

Conjunto (compreensão do conjunto disponível em 2.7+):

$ python -mtimeit -s"s = {i for i in range(10)};" "len(s)"
10000000 loops, best of 3: 0.0754 usec per loop

$ python -mtimeit -s"s = {i for i in range(1000000)};" "len(s)"
10000000 loops, best of 3: 0.0713 usec per loop

Deque:

$ python -mtimeit -s"from collections import deque;d=deque(range(10));" "len(d)"
100000000 loops, best of 3: 0.0163 usec per loop

$ python -mtimeit -s"from collections import deque;d=deque(range(1000000));" "len(d)"
100000000 loops, best of 3: 0.0163 usec per loop
carne_mecânica
fonte
1
Essa não é uma referência tão boa, embora mostre o que já sabemos. Isso ocorre porque o intervalo (10) e o intervalo (1000000) não devem ser O (1).
Desconhecido
3
Esta é de longe a melhor resposta. Você deve apenas adicionar uma conclusão caso alguém não perceba o tempo constante.
21813 santiagobasulto
4
Obrigado pelo comentário. Eu adicionei uma nota sobre a complexidade O (1) de len()e também corrigi as medidas para usar corretamente o -ssinalizador.
Mechanical_meat
É importante observar que salvar o comprimento em uma variável pode economizar uma quantidade significativa de tempo computacional: python -m timeit -s "l = range(10000);" "len(l); len(l); len(l)"223 nsec por loop python -m timeit -s "l = range(100);" "len(l)"66,2 nsec por loop
Radostin Stoyanov
16

len é um O (1) porque, na sua RAM, as listas são armazenadas como tabelas (séries de endereços contíguos). Para saber quando a tabela para, o computador precisa de duas coisas: comprimento e ponto de partida. É por isso que len () é O (1), o computador armazena o valor e, portanto, só precisa procurá-lo.

RAHUL KUMAR
fonte
3

Eu estive pensando em len () em Python depende do tamanho da lista, então eu sempre armazeno o comprimento em uma variável se eu usar várias vezes. Mas hoje, durante a depuração, notei o atributo __len__ no objeto list, então len () deve apenas buscá-lo, o que torna a complexidade O (1). Então, pesquisei no Google se alguém já perguntou e me deparei com este post.

AYUSH SENAPATI
fonte
Mas __len__é uma função contínua, não uma variável que representa comprimento de uma lista.
Kowalski
@Kowalski yes len é uma função, mas tudo o que faz é, ele retorna self.length
AYUSH SENAPATI
Mas sua postagem não diz nada sobre isso. Além disso, como você sabe que a list.__len__função é executada em tempo constante? Sim, mas não apenas por causa disso, é uma função. Porque foi implementado assim.
Kowalski