Eu acredito que existe uma maneira de encontrar o k-ésimo elemento em uma matriz não classificada de comprimento n em O (n). Ou talvez seja "esperado" O (n) ou algo assim. Como podemos fazer isso?
performance
algorithm
big-o
MrDatabase
fonte
fonte
Respostas:
Isso é chamado de encontrar a estatística de ordem k-ésima . Há um algoritmo muito simples randomizado (chamado QuickSelect ) tomando
O(n)
tempo médio,O(n^2)
pior momento caso, e um algoritmo não-randomizado muito complicado (chamado introselect ), tendoO(n)
pior momento caso. Há algumas informações na Wikipedia , mas não é muito bom.Tudo o que você precisa está nesses slides do powerpoint. Apenas para extrair o algoritmo básico doO(n)
pior algoritmo (introseleção):Também é muito bem detalhado no livro Introdução aos algoritmos de Cormen et al.
fonte
Se você deseja um
O(n)
algoritmo verdadeiro , ao contrário deO(kn)
algo parecido, use a seleção rápida (é basicamente o quicksort onde você joga fora a partição na qual não está interessado). Meu professor tem um ótimo artigo, com a análise de tempo de execução: ( referência )O algoritmo QuickSelect encontra rapidamente o k-ésimo elemento menor de uma matriz não classificada de
n
elementos. Como é um algoritmo randomizado , calculamos o pior tempo de execução esperado .Aqui está o algoritmo.
Qual é o tempo de execução desse algoritmo? Se o adversário jogar moedas para nós, podemos descobrir que o pivô é sempre o elemento maior e
k
é sempre 1, dando um tempo de execução deMas se as escolhas são realmente aleatórias, o tempo de execução esperado é dado por
onde estamos assumindo que não é inteiramente razoável que a recursão sempre chegue ao maior de
A1
ouA2
.Vamos adivinhar isso
T(n) <= an
para algunsa
. Então nós temose agora, de alguma forma, temos que obter a soma horrenda à direita do sinal de mais para absorver
cn
a esquerda. Se nós apenas ligá-lo como , ficamos grosseiramente . Mas isso é muito grande - não há espaço para extrair um extra . Então, vamos expandir a soma usando a fórmula da série aritmética:2(1/n) ∑i=n/2 to n an
2(1/n)(n/2)an = an
cn
onde tiramos vantagem de n ser "suficientemente grande" para substituir os
floor(n/2)
fatores feios pelo muito mais limpo (e menor)n/4
. Agora podemos continuar comfornecido
a > 16c
.Isso dá
T(n) = O(n)
. É claroOmega(n)
, então chegamosT(n) = Theta(n)
.fonte
k > length(A) - length(A2)
?A
dentroA1
eA2
ao redor do pivô, sabemos dissolength(A) == length(A1)+length(A2)+1
. Então,k > length(A)-length(A2)
é equivalente ak > length(A1)+1
, o que é verdade quandok
está em algum lugarA2
.Um Google rápido nesse ('kº maior conjunto de elementos') retornou isso: http://discuss.joelonsoftware.com/default.asp?interview.11.509587.17
(foi especificamente para o maior 3D)
e esta resposta:
fonte
Você gosta do quicksort. Escolha um elemento aleatoriamente e empurre tudo para cima ou para baixo. Nesse ponto, você saberá qual elemento realmente escolheu e, se for o k-ésimo elemento que você fez, caso contrário, repita com a posição (superior ou inferior) que o k-ésimo elemento. Estatisticamente, o tempo leva para encontrar o k-ésimo elemento cresce com n, O (n).
fonte
O programa que acompanha a análise de algoritmos fornece uma versão que é O (n), embora o autor afirme que o fator constante é tão alto, você provavelmente preferiria o método ingênuo de classificar a lista e selecionar.
Eu respondi a carta da sua pergunta :)
fonte
A biblioteca padrão C ++ possui quase exatamente essa chamada de função
nth_element
, embora modifique seus dados. Ele esperava tempo de execução linear, O (N), e também faz uma classificação parcial.fonte
Embora não tenha muita certeza sobre a complexidade de O (n), mas certamente estará entre O (n) e nLog (n). Certifique-se também de estar mais próximo de O (n) do que nLog (n). Função é escrita em Java
fonte
Eu implementei encontrar o kth mínimo em n elementos não classificados usando programação dinâmica, especificamente o método de torneio. O tempo de execução é O (n + klog (n)). O mecanismo usado está listado como um dos métodos na página da Wikipedia sobre Algoritmo de seleção (conforme indicado em uma postagem acima). Você pode ler sobre o algoritmo e também encontrar o código (Java) na minha página do blog Finding Kth mínima . Além disso, a lógica pode fazer a ordem parcial da lista - retornar primeiro K min (ou max) no tempo O (klog (n)).
Embora o código fornecido resulte no mínimo, é possível usar uma lógica semelhante para encontrar o máximo em O (klog (n)), ignorando o pré-trabalho feito para criar a árvore do torneio.
fonte
Você pode fazer isso em O (n + kn) = O (n) (para constante k) por tempo e O (k) por espaço, acompanhando os k maiores elementos que você já viu.
Para cada elemento da matriz, você pode digitalizar a lista de k maior e substituir o menor elemento pelo novo, se for maior.
A solução de pilha prioritária de Warren é mais clara.
fonte
O(n log k)
... ainda degenera para O (nlogn) no caso de um k grande. Eu acho que iria funcionar bem para pequenos valores de k No entanto ... possivelmente mais rápido do que alguns dos outros algoritmos mencionados aqui [???]Seleção rápida sexy em Python
fonte
a1 = [i for i in arr if i > arr[r]]
ea2 = [i for i in arr if i < arr[r]]
retornará o k-ésimo elemento maior .numpy.sort
paranumpy array
ousorted
para listas) do que para usar esta aplicação manual.Encontre a mediana da matriz em tempo linear e use o procedimento de partição exatamente como no quicksort para dividir a matriz em duas partes, valores à esquerda da mediana menor (<) que a mediana e à direita maior que (>) mediana , isso também pode ser feito em tempo linear, agora, vá para a parte da matriz onde está o k-elemento. Agora a recorrência se torna: T (n) = T (n / 2) + cn, o que me dá O (n) overal.
fonte
Abaixo está o link para a implementação completa, com uma explicação bastante extensa de como o algoritmo para encontrar o K-elemento em um algoritmo não classificado funciona. A idéia básica é particionar a matriz como no QuickSort. Mas, para evitar casos extremos (por exemplo, quando o menor elemento é escolhido como pivô em todas as etapas, para que o algoritmo degenere em O (n ^ 2) tempo de execução), é aplicada uma seleção de pivô especial, chamada algoritmo de mediana das medianas. Toda a solução é executada em O (n) tempo no pior e no caso médio.
Aqui está o link para o artigo completo (trata-se de encontrar Kth menor elemento, mas o princípio é o mesmo para encontrar Kth maior ):
Localizando o menor elemento em uma matriz não classificada
fonte
De acordo com este artigo, Encontrando o Kº maior item em uma lista de n itens, o algoritmo a seguir levará
O(n)
tempo na pior das hipóteses.Análise: Como sugerido no artigo original:
Por que o tamanho da partição é obtido 5 e não 3?
Como mencionado no artigo original :
Agora eu tentei implementar o algoritmo acima como:
Para fins de conclusão, outro algoritmo faz uso da Fila prioritária e leva tempo
O(nlogn)
.Ambos os algoritmos podem ser testados como:
Como o resultado esperado é:
18 18
fonte
Que tal esse tipo de abordagem
Mantenha a
buffer of length k
e atmp_max
, obtendo tmp_max é O (k) e é feito n vezes, para que algo comoO(kn)
Está certo ou estou faltando alguma coisa?
Embora não supere o caso médio de seleção rápida e o pior caso do método estatístico mediano, é bem fácil de entender e implementar.
fonte
percorra a lista. se o valor atual for maior que o maior valor armazenado, armazene-o como o maior valor e reduza 1-4 e 5 cai da lista. Caso contrário, compare-o com o número 2 e faça o mesmo. Repita, comparando-o com todos os 5 valores armazenados. isso deve fazê-lo em O (n)
fonte
eu gostaria de sugerir uma resposta
se pegarmos os primeiros k elementos e os classificarmos em uma lista vinculada de valores k
agora para todos os outros valores, mesmo para o pior caso, se fizermos a ordenação por inserção para valores rest nk, mesmo no pior caso, o número de comparações será k * (nk) e, para que os valores anteriores k sejam classificados, seja k * (k- 1) de modo que seja (nk-k) que é o (n)
Felicidades
fonte
A explicação do algoritmo mediana - de - medianas para encontrar o k-ésimo número inteiro entre n pode ser encontrada aqui: http://cs.indstate.edu/~spitla/presentation.pdf
A implementação em c ++ está abaixo:
fonte
Há também o algoritmo de seleção do Wirth , que possui uma implementação mais simples que o QuickSelect. O algoritmo de seleção do Wirth é mais lento que o QuickSelect, mas com algumas melhorias, ele se torna mais rápido.
Em mais detalhes. Usando a otimização MODIFIND de Vladimir Zabrodsky e a seleção de pivô mediana de 3 e prestando atenção às etapas finais da parte de particionamento do algoritmo, criei o seguinte algoritmo (imaginadamente chamado "LefSelect"):
Nos benchmarks que fiz aqui , o LefSelect é 20 a 30% mais rápido que o QuickSelect.
fonte
Solução Haskell:
Isso implementa a mediana das soluções medianas usando o método withShape para descobrir o tamanho de uma partição sem realmente calculá-la.
fonte
Aqui está uma implementação em C ++ do Randomized QuickSelect. A idéia é escolher aleatoriamente um elemento pivô. Para implementar a partição aleatória, usamos uma função aleatória, rand () para gerar índice entre l e r, trocamos o elemento no índice gerado aleatoriamente pelo último elemento e, finalmente, chamamos o processo de partição padrão que usa o último elemento como pivô.
A pior complexidade de tempo da solução acima ainda é O (n2). No pior caso, a função aleatória pode sempre escolher um elemento de canto. A complexidade do tempo esperado do QuickSelect aleatório acima é Θ (n)
fonte
Enquete de chamada () k vezes.
fonte
Esta é uma implementação em Javascript.
Se você liberar a restrição de que não pode modificar a matriz, poderá impedir o uso de memória extra usando dois índices para identificar a "partição atual" (no estilo quicksort clássico - http://www.nczonline.net/blog/2012/ 11/27 / ciência da computação em javascript-quicksort / ).
Se você quiser testar o desempenho, use esta variação:
O restante do código é apenas para criar um playground:
Agora, execute seus testes algumas vezes. Por causa do Math.random (), ele sempre produz resultados diferentes:
Se você testá-lo algumas vezes, poderá ver, mesmo empiricamente, que o número de iterações é, em média, O (n) ~ = constante * n e o valor de k não afeta o algoritmo.
fonte
Eu vim com esse algoritmo e parece ser O (n):
Digamos k = 3 e queremos encontrar o terceiro maior item da matriz. Eu criaria três variáveis e compararia cada item da matriz com o mínimo dessas três variáveis. Se o item da matriz for maior que o mínimo, substituiríamos a variável min pelo valor do item. Continuamos a mesma coisa até o final da matriz. O mínimo de nossas três variáveis é o terceiro maior item da matriz.
E, para encontrar o quinto maior item, precisamos de K variáveis.
Exemplo: (k = 3)
Alguém pode revisar isso e me informar o que está faltando?
fonte
Aqui está a implementação do algoritmo eladv sugerida (eu também coloquei aqui a implementação com pivô aleatório):
fonte
é semelhante à estratégia quickSort, onde escolhemos um pivô arbitrário e trazemos os elementos menores para a esquerda e os maiores para a direita
fonte
Vá para o final deste link: ...........
http://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array-set-3-worst-case-linear-time/
fonte
Você pode encontrar o k-ésimo elemento no O (n) tempo e no espaço constante. Se considerarmos que o array é apenas para números inteiros.
A abordagem é fazer uma pesquisa binária no intervalo de valores da matriz. Se tivermos um min_value e um max_value ambos no intervalo inteiro, podemos fazer uma pesquisa binária nesse intervalo. Podemos escrever uma função comparadora que nos dirá se algum valor é o k-menor ou menor que o k-menor ou maior que o k-menor. Faça a pesquisa binária até atingir o número k-menor
Aqui está o código para isso
Solução de classe:
fonte
Há também um algoritmo que supera o algoritmo de seleção rápida. É chamado algoritmo de Floyd-Rivets (FR) .
Artigo original: https://doi.org/10.1145/360680.360694
Versão para download: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.309.7108&rep=rep1&type=pdf
Artigo da Wikipedia https://en.wikipedia.org/wiki/Floyd%E2%80%93Rivest_algorithm
Tentei implementar o quickselect e o algoritmo FR em C ++. Também os comparei com as implementações padrão da biblioteca C ++ std :: nth_element (que é basicamente o híbrido introselect de quickselect e heapselect). O resultado foi a seleção rápida e o nth_element foi executado comparativamente em média, mas o algoritmo FR foi executado aprox. duas vezes mais rápido em comparação com eles.
Código de exemplo que eu usei para o algoritmo FR:
fonte
O que eu faria é o seguinte:
Você pode simplesmente armazenar ponteiros para o primeiro e o último elemento na lista vinculada. Eles mudam apenas quando são feitas atualizações na lista.
Atualizar:
fonte
Primeiro, podemos construir uma BST a partir de uma matriz não classificada, que leva tempo O (n) e, a partir da BST, podemos encontrar o k-ésimo menor elemento em O (log (n)) que, em geral, conta com uma ordem de O (n).
fonte