C'est l'un des projets de recherche que je vis actuellement. L'exigence est presque exactement la vôtre, et nous avons développé de beaux algorithmes pour résoudre le problème.
L'entrée
L'entrée est un flux infini de mots ou de phrases anglais (nous les appelons tokens
).
Le résultat
- Sortez les N premiers jetons que nous avons vus jusqu'à présent (de tous les jetons que nous avons vus!)
- Sortez les N premiers jetons dans une fenêtre historique, par exemple, le dernier jour ou la semaine dernière.
Une application de cette recherche est de trouver le sujet brûlant ou les tendances du sujet sur Twitter ou Facebook. Nous avons un robot d'exploration qui parcourt le site Web, ce qui génère un flux de mots, qui alimentera le système. Le système sortira alors les mots ou les phrases de fréquence la plus élevée, soit globalement, soit historiquement. Imaginez au cours des dernières semaines que l'expression «Coupe du monde» apparaîtra plusieurs fois sur Twitter. Il en va de même pour "Paul le poulpe". :)
Chaîne en nombres entiers
Le système a un identifiant entier pour chaque mot. Bien qu'il existe une infinité de mots possibles sur Internet, mais après avoir accumulé un grand nombre de mots, la possibilité de trouver de nouveaux mots devient de plus en plus faible. Nous avons déjà trouvé 4 millions de mots différents et attribué un identifiant unique à chacun. Cet ensemble complet de données peut être chargé en mémoire sous forme de table de hachage, consommant environ 300 Mo de mémoire. (Nous avons implémenté notre propre table de hachage. L'implémentation de Java prend une énorme surcharge de mémoire)
Chaque phrase peut alors être identifiée comme un tableau d'entiers.
Ceci est important, car le tri et les comparaisons sur les entiers sont beaucoup plus rapides que sur les chaînes.
Archiver les données
Le système conserve les données d'archive pour chaque jeton. Fondamentalement, ce sont des paires de (Token, Frequency)
. Cependant, la table qui stocke les données serait si énorme que nous devions partitionner la table physiquement. Une fois que le schéma de partition est basé sur les ngrammes du jeton. Si le jeton est un seul mot, c'est 1 gramme. Si le jeton est une phrase de deux mots, il s'agit de 2 grammes. Et cela continue. Environ à 4 grammes, nous avons 1 milliard d'enregistrements, avec une table d'environ 60 Go.
Traitement des flux entrants
Le système absorbe les phrases entrantes jusqu'à ce que la mémoire soit pleinement utilisée (oui, nous avons besoin d'un MemoryManager). Après avoir pris N phrases et enregistré en mémoire, le système s'arrête et commence à convertir chaque phrase en mots et en phrases. Chaque jeton (mot ou phrase) est compté.
Pour les jetons très fréquents, ils sont toujours conservés en mémoire. Pour les jetons moins fréquents, ils sont triés en fonction des ID (rappelez-vous que nous traduisons la chaîne en un tableau d'entiers) et sérialisés dans un fichier disque.
(Cependant, pour votre problème, puisque vous ne comptez que des mots, vous pouvez mettre tous les mappages de fréquences de mots en mémoire uniquement. Une structure de données soigneusement conçue ne consommerait que 300 Mo de mémoire pour 4 millions de mots différents. Un conseil: utilisez le caractère ASCII pour représentent des chaînes), ce qui est tout à fait acceptable.
En attendant, il y aura un autre processus qui sera activé une fois qu'il trouvera un fichier disque généré par le système, puis commencera à le fusionner. Puisque le fichier disque est trié, la fusion prendrait un processus similaire à celui du tri par fusion. Certaines conceptions doivent également être prises en compte ici, car nous voulons éviter trop de recherches aléatoires sur les disques. L'idée est d'éviter la lecture (processus de fusion) / écriture (sortie système) en même temps et de laisser le processus de fusion lire un disque tout en écrivant sur un autre disque. Cela revient à implémenter un verrouillage.
Fin de la journée
À la fin de la journée, le système aura de nombreux jetons fréquents avec une fréquence stockée en mémoire, et de nombreux autres jetons moins fréquents stockés dans plusieurs fichiers disque (et chaque fichier est trié).
Le système vide la carte en mémoire dans un fichier disque (trie-le). Maintenant, le problème devient la fusion d'un ensemble de fichiers de disque triés. En utilisant un processus similaire, nous obtiendrions un fichier disque trié à la fin.
Ensuite, la dernière tâche consiste à fusionner le fichier disque trié dans la base de données d'archives. Dépend de la taille de la base de données d'archive, l'algorithme fonctionne comme ci-dessous s'il est assez grand:
for each record in sorted disk file
update archive database by increasing frequency
if rowcount == 0 then put the record into a list
end for
for each record in the list of having rowcount == 0
insert into archive database
end for
L'intuition est qu'après un certain temps, le nombre d'insertions deviendra de plus en plus petit. De plus en plus d'opérations se feront uniquement sur la mise à jour. Et cette mise à jour ne sera pas pénalisée par l'index.
J'espère que toute cette explication aiderait. :)
what is the most frequent item in the subsequence [2; 2; 3; 3; 3; 4; 4; 4; 4; 5; 5] of your sequence?