Quando você tem um arquivo muito grande e muitos elementos, mas o elemento mais comum é muito comum - ocorre fração do tempo - você pode encontrá-lo em tempo linear com palavras O ( k ) de espaço (o A constante na notação O ( ) é muito pequena, basicamente 2 se você não contar o armazenamento para itens auxiliares como hash). Além disso, isso funciona muito bem com armazenamento externo, pois o arquivo é processado em sequência, um elemento de cada vez, e o algoritmo nunca "olha para trás". Uma maneira de fazer isso é através de um algoritmo clássico de Misra e Gries, veja estas notas de aula> 1 / kO ( k )O ( ). O problema agora é conhecido como problema dos rebatedores pesados (os elementos frequentes são os rebatedores pesados).
A suposição de que o elemento mais frequente aparece fração do tempo para k um número pequeno pode parecer forte, mas é de certa forma necessário! Ou seja, se você tiver acesso seqüencial ao seu arquivo (e caso o arquivo seja enorme, o acesso aleatório será muito caro), qualquer algoritmo que sempre encontre o elemento mais frequente em um número constante de passes utilizará espaço linear no número de elementos . Portanto, se você não assume algo sobre a entrada, não pode vencer uma tabela de hash. A suposição de que o elemento mais frequente é muito frequente talvez seja a maneira mais natural de contornar os resultados negativos.> 1 / kk
Aqui está um esboço para , ou seja, quando existe um único elemento que ocorre mais da metade do tempo. Esse caso especial é conhecido como algoritmo de votação majoritária e é devido a Boyer e Moore. Manteremos um único elemento e uma única contagem. Inicialize a contagem para 1 e armazene o primeiro elemento do arquivo. Em seguida, processe o arquivo em sequência:k = 2
- se o elemento atual do arquivo for o mesmo que o elemento armazenado, aumente a contagem em um
- se o elemento atual do arquivo for diferente do elemento armazenado, diminua a contagem em um
- se a contagem atualizada for 0, "expulse" o elemento armazenado e armazene o elemento atual do arquivo; aumentar a contagem para 1
- prossiga para o próximo elemento do arquivo
Um pouco de reflexão sobre este procedimento o convencerá de que, se existir um elemento "majoritário", ou seja, um que ocorra mais da metade do tempo, esse elemento será o elemento armazenado após o processamento do arquivo inteiro.
Para geral , você mantém k - 1kk - 1 elementos e contagens e inicializa os elementos nos primeiros k elementos distintos do arquivo e as contagens no número de vezes que cada um desses elementos aparece antes de ver k- ésimo elemento distinto. Em seguida, você executa essencialmente o mesmo procedimento: a contagem de um elemento é aumentada cada vez que é encontrada, todas as contagens de elementos são diminuídas se um elemento que não é armazenado for encontrado e, quando alguma contagem for zero, esse elemento será expulso em favor do elemento. elemento atual do arquivo. Este é o algoritmo de Misra-Gries.k - 1kk
Obviamente, você pode usar uma tabela de hash para indexar o k - 11 / kO ( k )
Uma coisa final: depois de encontrar candidato "hitters pesados" (ou seja, elementos frequentes), você pode fazer mais uma passagem sobre o arquivo para contar a frequência de cada elemento. Dessa forma, você pode classificar os elementos entre si e verificar se todos eles ocorrem mais de 1k1 / kk - 1