O python tem uma lista classificada?

128

Com o que quero dizer uma estrutura com:

  • O (log n) complexidade para x.push()operações
  • O (log n) complexidade para encontrar um elemento
  • O (n) complexidade para calcular list(x)qual será classificado

Eu também tive uma pergunta relacionada sobre o desempenho, list(...).insert(...)que agora está aqui .

ilya n.
fonte
memcpyainda é uma operação O (n) . Não tenho certeza de como o Python implementa listas exatamente , mas minha aposta seria que elas sejam armazenadas na memória contígua (certamente não como uma lista vinculada). Se assim é, a inserção bisectque você demonstra terá complexidade O (n) .
21139 Stephan202
2
Infelizmente não fora da caixa. Mas a biblioteca de contêineres classificados de Grant Jenk é excelente. stackoverflow.com/a/22616929/284795
Coronel Panic

Respostas:

52

A lista padrão do Python não está classificada de nenhuma forma. O módulo heapq padrão pode ser usado para anexar em O (log n) a uma lista existente e remover o menor em O (log n), mas não é uma lista classificada em sua definição.

Existem várias implementações de árvores equilibradas para Python que atender às suas necessidades, por exemplo rbtree , RBTree , ou pyavl .

Martin v. Löwis
fonte
1
+1 para rbtree, ele funciona muito bem (mas contém código nativo, não pura python, não tão fácil de implantar talvez)
Will
12
O sortedcontainers é puro Python e rápido como C (como rbtree) com uma comparação de desempenho.
GrantJ
"não é uma lista classificada em sua definição." Como assim?
Coronel Panic
4
O heapq permite apenas encontrar o menor elemento; o OP estava solicitando uma estrutura que pudesse encontrar qualquer elemento em O (log n), o que não seria possível.
Martin v. Löwis
70

Existe uma razão específica para os seus requisitos de grande porte? Ou você só quer que seja rápido? O módulo ordenado de contêineres é Python puro e rápido (como nas implementações rápidas como C, como blist e rbtree).

A comparação de desempenho mostra os benchmarks mais rapidamente ou a par do tipo de lista classificada do blist. Observe também que rbtree, RBTree e PyAVL fornecem tipos de ditado e conjunto classificados, mas não têm um tipo de lista classificada.

Se o desempenho é um requisito, lembre-se sempre de fazer benchmark. Um módulo que comprove a alegação de ser rápido com a notação Big-O deve ser suspeito até que também mostre comparações de benchmark.

Isenção de responsabilidade: eu sou o autor do módulo de contêineres classificados do Python.


Instalação:

pip install sortedcontainers

Uso:

>>> from sortedcontainers import SortedList
>>> l = SortedList()
>>> l.update([0, 4, 1, 3, 2])
>>> l.index(3)
3
>>> l.add(5)
>>> l[-1]
5
GrantJ
fonte
4
Na verdade, comparei os recipientes classificados contra o bisect: 0.0845024989976para SortedList.add () vs 0.596589182518para bisect.insort (), portanto, uma diferença de 7x na velocidade! E espero que a diferença de velocidade aumente com o tamanho da lista, pois a classificação de inserção de contêineres classificados funciona em O (log n) enquanto bisect.insort () em O (n).
gaborous
1
@gaborous porque bisect ainda usa uma lista, então os restos de inserçãoO(n)
njzk2
34

Embora eu ainda nunca tenha verificado as velocidades "grandes O" das operações básicas da lista Python, bisectprovavelmente vale a pena mencionar o módulo padrão neste contexto:

import bisect
L = [0, 100]

bisect.insort(L, 50)
bisect.insort(L, 20)
bisect.insort(L, 21)

print L
## [0, 20, 21, 50, 100]

i = bisect.bisect(L, 20)
print L[i-1], L[i]
## 20, 21

PS. Ah, desculpe, bisecté mencionado na pergunta referenciada. Ainda assim, acho que não será muito prejudicial se essa informação estiver aqui)

PPS. E as listas CPython são na verdade matrizes (não, digamos, skiplists ou etc). Bem, acho que eles devem ser algo simples, mas quanto a mim, o nome é um pouco enganador.


Portanto, se não me engano, as velocidades de bissecção / lista provavelmente seriam:

  • for push (): O (n) para o pior caso;
  • para uma pesquisa: se considerarmos a velocidade de indexação da matriz como O (1), a pesquisa deve ser uma operação O (log (n));
  • para a criação da lista: O (n) deve ser a velocidade da cópia da lista, caso contrário, é O (1) para a mesma lista)

Upd. Após uma discussão nos comentários, deixe-me vincular aqui estas perguntas de SO: Como a lista do Python é implementada e qual é a complexidade do tempo de execução das funções da lista do python

ジ ョ ー
fonte
push () deve estar em O (log n), pois a lista já está classificada.
Estani
1
pode ser que eu deveria ter dito "para uma inserção op" . de qualquer maneira, isso foi há cerca de um ano, então agora eu posso facilmente misturar as coisas ou perder alguma coisa
ジ ョ ー ジ
Você sempre pode inserir um valor em uma lista classificada em O (log n), consulte pesquisa binária. push () é definido como uma operação de inserção.
Estani
2
Verdade. Mas, embora encontrar a localização da inserção usasse O (log n) ops, a inserção real (isto é, adicionar o elemento à estrutura de dados) provavelmente depende dessa estrutura (pense em inserir um elemento em uma matriz classificada). E como as listas Python são realmente matrizes , isso pode levar O (n). Devido ao limite de tamanho dos comentários, irei vincular duas perguntas relacionadas ao SO do texto da resposta (veja acima).
precisa
Bom argumento. Eu não estava ciente da lista de manipulados como matrizes em Python.
Estani
7
import bisect

class sortedlist(list):
    '''just a list but with an insort (insert into sorted position)'''
    def insort(self, x):
        bisect.insort(self, x)
Dave31415
fonte
a inserção implícita () em bisect.insort () é O (n)
j314erre
6

Embora ainda não forneça uma função de pesquisa personalizada, o heapqmódulo pode atender às suas necessidades. Ele implementa uma fila de heap usando uma lista regular. Você teria que escrever seu próprio teste eficiente de associação que faz uso da estrutura interna da fila (que pode ser feita em O (log n) , eu diria ...). Há uma desvantagem: extrair uma lista classificada tem complexidade O (n log n) .

Stephan202
fonte
É bom, mas difícil de cortar.
ilya n.
3
Como pode haver um teste de associação O (log n) em um heap? Se você estiver procurando pelo valor x, poderá parar de procurar um ramo se encontrar algo maior que x, mas para um valor aleatório de x é 50% provável que esteja em uma folha e provavelmente não poderá podar muito.
mercados
1

Eu usaria o biscectou sortedcontainersmódulos. Eu realmente não tenho experiência, mas acho que o heapqmódulo funciona. Contém umHeap Queue

Slass33
fonte
0

Pode não ser difícil implementar sua própria lista de classificação no Python. Abaixo está uma prova de conceito:

import bisect

class sortlist:
    def __init__(self, list):
        self.list = list
        self.sort()
    def sort(self):
        l = []
        for i in range(len(self.list)):
            bisect.insort(l, self.list[i])
        self.list = l
        self.len = i
    def insert(self, value):
        bisect.insort(self.list, value)
        self.len += 1
    def show(self):
        print self.list
    def search(self,value):
        left = bisect.bisect_left(self.list, value)
        if abs(self.list[min([left,self.len-1])] - value) >= abs(self.list[left-1] - value):
            return self.list[left-1]
        else:
            return self.list[left]

list = [101, 3, 10, 14, 23, 86, 44, 45, 45, 50, 66, 95, 17, 77, 79, 84, 85, 91, 73]
slist = sortlist(list)
slist.show()
slist.insert(99)
slist.show()
print slist.search(100000000)
print slist.search(0)
print slist.search(56.7)

========= Resultados ============

[3, 10, 14, 17, 23, 44, 45, 45, 50, 66, 73, 77, 79, 84, 85, 86, 91, 95, 101]

[3, 10, 14, 17, 23, 44, 45, 45, 50, 66, 73, 77, 79, 84, 85, 86, 91, 95, 99, 101]

101

3

50.

Ventilador
fonte