Entrada: Um número inteiro positivo K e um texto grande. O texto pode realmente ser visto como uma sequência de palavras. Portanto, não precisamos nos preocupar em como quebrá-lo na sequência de palavras.
Saída: as K palavras mais frequentes no texto.
Meu pensamento é assim.
use uma tabela de hash para registrar a frequência de todas as palavras enquanto percorre toda a sequência de palavras. Nesta fase, a chave é "palavra" e o valor é "frequência de palavras". Isso leva tempo O (n).
classifique o par (palavra, palavra-frequência); e a chave é "frequência de palavras". Isso leva tempo O (n * lg (n)) com o algoritmo de classificação normal.
Após a classificação, apenas pegamos as primeiras K palavras. Isso leva tempo O (K).
Para resumir, o tempo total é O (n + n lg (n) + K) , Uma vez que K é certamente menor que N, então é na verdade O (n lg (n)).
Podemos melhorar isso. Na verdade, queremos apenas as K palavras principais. A frequência de outras palavras não nos preocupa. Portanto, podemos usar "classificação Heap parcial". Para as etapas 2) e 3), não fazemos apenas a classificação. Em vez disso, mudamos para ser
2 ') construir um monte de pares de (palavra, palavra-frequência) com "palavra-frequência" como chave. Leva tempo O (n) para construir um heap;
3 ') extraia as K principais palavras da pilha. Cada extração é O (lg (n)). Portanto, o tempo total é O (k * lg (n)).
Para resumir, essa solução custa tempo O (n + k * lg (n)).
Este é apenas o meu pensamento. Não descobri uma maneira de melhorar a etapa 1).
Espero que alguns especialistas em Recuperação de Informações possam esclarecer melhor essa questão.
fonte
Respostas:
Isso pode ser feito em tempo O (n)
Solução 1:
Passos:
Conte as palavras e faça hash, o que vai acabar na estrutura como esta
Percorra o hash e encontre a palavra usada com mais frequência (neste caso, "foo" 100) e, em seguida, crie a matriz desse tamanho
Então, podemos percorrer o hash novamente e usar o número de ocorrências de palavras como o índice do array, se não houver nada no índice, crie um array, senão anexe-o ao array. Então acabamos com uma matriz como:
Em seguida, basta percorrer a matriz a partir do final e coletar as k palavras.
Solução 2:
Passos:
fonte
Em geral, você não obterá um tempo de execução melhor do que a solução que descreveu. Você precisa fazer pelo menos O (n) trabalho para avaliar todas as palavras e, depois, O (k) trabalho extra para encontrar os k termos principais.
Se o seu conjunto de problemas for realmente grande, você pode usar uma solução distribuída como mapear / reduzir. Faça com que n trabalhadores do mapa contem frequências em 1 / enésimo do texto cada e, para cada palavra, envie para um dos trabalhadores do redutor m calculados com base no hash da palavra. Os redutores somam as contagens. A mesclagem de classificação sobre as saídas dos redutores fornecerá as palavras mais populares em ordem de popularidade.
fonte
Uma pequena variação em sua solução produz um algoritmo O (n) se não nos importarmos em classificar os K principais, e uma solução O (n + k * lg (k)) se o fizermos. Eu acredito que ambos os limites são ótimos dentro de um fator constante.
A otimização aqui vem novamente depois de percorrermos a lista, inserindo na tabela hash. Podemos usar o algoritmo da mediana das medianas para selecionar o K-ésimo maior elemento da lista. Este algoritmo é comprovadamente O (n).
Depois de selecionar o menor elemento K, particionamos a lista em torno desse elemento, assim como no quicksort. Obviamente, isso também é O (n). Qualquer coisa no lado "esquerdo" do pivô está em nosso grupo de elementos K, então terminamos (podemos simplesmente jogar fora todo o resto à medida que avançamos).
Portanto, esta estratégia é:
Se você quiser classificar os K elementos, simplesmente classifique-os com qualquer classificação de comparação eficiente em tempo O (k * lg (k)), resultando em um tempo de execução total de O (n + k * lg (k)).
O limite de tempo O (n) é ótimo dentro de um fator constante porque devemos examinar cada palavra pelo menos uma vez.
O limite de tempo O (n + k * lg (k)) também é ótimo porque não há uma maneira baseada em comparação para classificar k elementos em menos de tempo k * lg (k).
fonte
Se a sua "lista grande de palavras" for grande o suficiente, você pode simplesmente amostrar e obter estimativas. Caso contrário, gosto de agregação de hash.
Editar :
Por amostra, quero dizer escolher algum subconjunto de páginas e calcular a palavra mais frequente nessas páginas. Desde que você selecione as páginas de forma razoável e selecione uma amostra estatisticamente significativa, suas estimativas das palavras mais frequentes devem ser razoáveis.
Essa abordagem só é realmente razoável se você tiver tantos dados que processá-los todos seja uma espécie de besteira. Se você tem apenas alguns megas, deve ser capaz de analisar os dados e calcular uma resposta exata sem suar muito, em vez de se preocupar em calcular uma estimativa.
fonte
Você pode reduzir ainda mais o tempo particionando usando a primeira letra das palavras e, em seguida, particionando o maior conjunto de várias palavras usando o próximo caractere até que você tenha k conjuntos de palavras únicas. Você usaria uma espécie de árvore de 256 maneiras com listas de palavras parciais / completas nas folhas. Você precisaria ter muito cuidado para não causar cópias de string em todos os lugares.
Este algoritmo é O (m), onde m é o número de caracteres. Isso evita a dependência de k, o que é muito bom para k grandes [pelo jeito que seu tempo de execução postado está errado, deveria ser O (n * lg (k)), e não tenho certeza do que isso significa em termos de m].
Se você executar os dois algoritmos lado a lado, obterá o que tenho certeza de que é um algoritmo O (min (m, n * lg (k))) assintoticamente ideal, mas o meu deve ser mais rápido em média porque não envolve hashing ou classificação.
fonte
Você tem um bug em sua descrição: a contagem leva O (n) tempo, mas a classificação leva O (m * lg (m)), onde m é o número de palavras únicas . Isso geralmente é muito menor do que o número total de palavras, então provavelmente deve apenas otimizar como o hash é construído.
fonte
Seu problema é o mesmo que este- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/
Use Trie e min heap para resolvê-lo com eficiência.
fonte
Se o que você está procurando é a lista de k palavras mais frequentes em seu texto para qualquer k prático e para qualquer idioma natural, então a complexidade de seu algoritmo não é relevante.
Apenas amostra , digamos, alguns milhões de palavras de seu texto, processo que com qualquer algoritmo em questão de segundos , e conta mais freqüentes vai ser muito preciso.
Como uma observação lateral, a complexidade do algoritmo fictício (1. conte todos 2. classifique as contagens 3. pegue o melhor) é O (n + m * log (m)), onde m é o número de palavras diferentes em seu texto. log (m) é muito menor do que (n / m), então permanece O (n).
Praticamente, o passo longo é contar.
fonte
Aqui está o código
}
Aqui estão os testes de unidade
Para mais detalhes, consulte este caso de teste
fonte
use uma tabela de hash para registrar a frequência de todas as palavras enquanto percorre toda a sequência de palavras. Nesta fase, a chave é "palavra" e o valor é "frequência de palavras". Isso leva tempo O (n). É o mesmo que cada um explicado acima
Enquanto se insere no hashmap, mantenha o Treeet (específico para java, existem implementações em todas as linguagens) de tamanho 10 (k = 10) para manter as 10 palavras mais frequentes. Até que o tamanho seja menor que 10, continue adicionando. Se o tamanho for igual a 10, se o elemento inserido for maior que o elemento mínimo, ou seja, o primeiro elemento. Se sim, remova-o e insira um novo elemento
Para restringir o tamanho do conjunto de árvores, consulte este link
fonte
Suponha que tenhamos uma sequência de palavras "ad" "ad" "menino" "grande" "mau" "com" "venha" "frio". E K = 2. como você mencionou "particionamento usando a primeira letra das palavras", obtivemos ("ad", "ad") ("menino", "grande", "mau") ("com" "venha" "frio") "então particionar o maior conjunto de várias palavras usando o próximo caractere até que você tenha k conjuntos de palavras únicas. " ele irá particionar ("boy", "big", "bad") ("com" "come" "cold"), a primeira partição ("ad", "ad") é perdida, enquanto "ad" é na verdade o palavra mais frequente.
Talvez eu não tenha entendido seu ponto. Você pode detalhar seu processo sobre partição?
fonte
Eu acredito que este problema pode ser resolvido por um algoritmo O (n). Poderíamos fazer a classificação na hora. Em outras palavras, a classificação nesse caso é um subproblema do problema de classificação tradicional, uma vez que apenas um contador é incrementado em um cada vez que acessamos a tabela hash. Inicialmente, a lista é classificada, uma vez que todos os contadores são zero. À medida que continuamos incrementando os contadores na tabela de hash, registramos outra matriz de valores de hash ordenados por frequência como segue. Cada vez que incrementamos um contador, verificamos seu índice no array classificado e verificamos se sua contagem excede seu predecessor na lista. Nesse caso, trocamos esses dois elementos. Como tal, obtemos uma solução que é no máximo O (n) onde n é o número de palavras no texto original.
fonte
Eu também estava lutando contra isso e me inspirei em @aly. Em vez de classificar depois, podemos apenas manter uma lista pré-classificada de palavras (
List<Set<String>>
) e a palavra estará no conjunto na posição X, onde X é a contagem atual da palavra. Em geral, é assim que funciona:Map<String, Integer>
.A desvantagem disso é que a lista pode ser grande - pode ser otimizada usando um
TreeMap<Integer, Set<String>>
- mas isso adicionará alguma sobrecarga. Por fim, podemos usar uma combinação de HashMap ou nossa própria estrutura de dados.O código
fonte
Acabei de descobrir a outra solução para este problema. Mas não tenho certeza se está certo. Solução:
fonte
Tente pensar em uma estrutura de dados especial para abordar esse tipo de problema. Neste caso, tipo especial de árvore, como a tentativa de armazenar strings de maneira específica, muito eficiente. Ou a segunda maneira de construir sua própria solução, como contar palavras. Eu acho que este TB de dados estaria em inglês, então temos cerca de 600.000 palavras em geral, então será possível armazenar apenas essas palavras e contar quais strings seriam repetidas + esta solução precisará de regex para eliminar alguns caracteres especiais. A primeira solução será mais rápida, tenho certeza.
http://en.wikipedia.org/wiki/Trie
fonte
Esta é uma ideia interessante de pesquisar e eu poderia encontrar este artigo relacionado ao Top-K https://icmi.cs.ucsb.edu/research/tech_reports/reports/2005-23.pd f
Também há uma implementação disso aqui .
fonte
Código mais simples para obter a ocorrência da palavra usada com mais frequência.
fonte
Nessas situações, recomendo o uso de recursos integrados do Java. Desde então, eles já estão bem testados e estáveis. Neste problema, encontro as repetições das palavras usando a estrutura de dados HashMap. Em seguida, envio os resultados para uma série de objetos. Classifico o objeto por Arrays.sort () e imprimo as k palavras principais e suas repetições.
Para obter mais informações, visite https://github.com/m-vahidalizadeh/foundations/blob/master/src/algorithms/TopKWordsTextFile.java . Espero que ajude.
fonte
I recommend to use Java built-in features
Como loops foreach e processamento de streams ?)**
};
fonte