web-dev-qa-db-fra.com

Algorithme pour trouver les 10 meilleurs termes de recherche

Je suis en train de préparer une interview et cela m'a rappelé une question qui m'avait déjà été posée lors d'une précédente interview:

"Il vous a été demandé de concevoir un logiciel permettant d'afficher en permanence les 10 principaux termes de recherche sur Google. Vous avez accès à un flux qui fournit un flux infini de termes de recherche actuellement recherchés sur Google. Décrivez l'algorithme et les structures de données. vous utiliseriez pour implémenter ceci. Vous devez concevoir deux variantes: 

(i) Affichez les 10 termes de recherche les plus fréquents (c’est-à-dire depuis que vous avez commencé à lire le flux). 

(ii) Affichez uniquement les 10 principaux termes de recherche du mois dernier, mis à jour toutes les heures. 

Vous pouvez utiliser une approximation pour obtenir le top 10, mais vous devez justifier vos choix. "
J'ai bombardé cet entretien et je ne sais toujours pas comment le mettre en œuvre. 

La première partie demande les 10 éléments les plus fréquents dans une sous-séquence sans cesse croissante d'une liste infinie. J'ai examiné les algorithmes de sélection, mais je n'ai trouvé aucune version en ligne pour résoudre ce problème. 

La deuxième partie utilise une liste finie, mais en raison du grand nombre de données traitées, vous ne pouvez pas réellement stocker en mémoire le mois entier de termes de recherche et calculer un histogramme toutes les heures. 

Le problème est rendu plus difficile par le fait que la liste du top 10 est constamment mise à jour. Vous devez donc calculer votre top 10 sur une fenêtre glissante.

Des idées?

112
del

Eh bien, cela ressemble à une énorme quantité de données, avec un coût peut-être prohibitif pour stocker toutes les fréquences. Lorsque la quantité de données est si importante que nous ne pouvons pas espérer tout stocker, nous entrons dans le domaine des algorithmes flux de données.

Livre utile dans ce domaine: Muthukrishnan - "Flux de données: algorithmes et applications"

Référence étroitement liée au problème en question que j’ai retenu de ce qui précède: Manku, Motwani - "Nombre approximatif de fréquences sur des flux de données" [pdf]

En passant, Motwani, de Stanford, est un auteur du très important "Randomized Algorithms" book. Le 11ème chapitre de ce livre traite de ce problème. Edit: Désolé, mauvaise référence, ce chapitre est consacré à un problème différent. Après vérification, je recommande à la place la section 5.1.2 du livre de Muthukrishnan} _ , disponible en ligne.

Hé, bonne question d'entrevue. 

46
Dimitris Andreou

Aperçu de l'estimation de fréquence

Certains algorithmes bien connus peuvent fournir des estimations de fréquence pour un tel flux en utilisant une quantité de stockage fixe. L'un est Frequent, de Misra et Gries (1982). Dans une liste d'éléments n, il trouve tous les éléments qui apparaissent plus de n/k fois, à l'aide de compteurs k - 1. Ceci est une généralisation de l'algorithme Majority de Boyer et Moore (Fischer-Salzberg, 1982), où k vaut 2. Manku et Motwani LossyCounting (2002) et Metwally SpaceSaving ( 2005) ont des besoins en espace similaires, mais peuvent fournir des estimations plus précises dans certaines conditions. 

La chose importante à retenir est que ces algorithmes ne peuvent fournir que des estimations de fréquence. Plus précisément, l'estimation de Misra-Gries peut sous-comptabiliser la fréquence réelle de (n/k) éléments. 

Supposons que vous disposiez d'un algorithme permettant d'identifier positivement un élément uniquement s'il survient plus de 50% du temps. Introduisez dans cet algorithme un flux de N éléments distincts, puis ajoutez une autre copie N - 1 d'un élément, x, pour un total de 2N - 1 éléments. . Si l'algorithme vous dit que x dépasse 50% du total, cela doit être dans le premier flux; sinon, x n'était pas dans le flux initial. Pour que l'algorithme puisse effectuer cette détermination, il doit stocker le flux initial (ou un résumé proportionnel à sa longueur)! Nous pouvons donc nous prouver que l’espace requis par un tel algorithme "exact" serait Ω (N).

Au lieu de cela, ces algorithmes de fréquence décrits ici fournissent une estimation, identifiant tout élément dépassant le seuil, ainsi que certains éléments se trouvant en dessous d'une certaine marge. Par exemple, l'algorithme Majority, utilisant un seul compteur, donnera toujours un résultat; si un élément dépasse 50% du flux, il sera trouvé. Mais cela pourrait aussi vous donner un objet qui n’apparaît qu’une fois. Vous ne le sauriez pas sans passer une seconde fois sur les données (en utilisant, encore une fois, un seul compteur, mais en recherchant uniquement cet élément).

L'algorithme fréquent

Voici une description simple de l'algorithme Frequent de Misra-Gries. Demaine (2002) et d'autres ont optimisé l'algorithme, mais cela vous donne le Gist.

Spécifiez la fraction de seuil, 1/k; tout élément qui survient plus de n/k fois sera trouvé. Créez une carte vide (comme un arbre rouge-noir); les clés seront des termes de recherche, et les valeurs constitueront un compteur pour ce terme. 

  1. Regardez chaque élément du flux. 
  2. Si le terme existe dans la carte, incrémentez le compteur associé. 
  3. Sinon, si la carte est inférieure à k - 1 entrées, ajoutez le terme à la carte avec un compteur égal à un.
  4. Toutefois, si la mappe a déjà k - 1 entrées, décrémentez le compteur à chaque entrée. Si un compteur atteint zéro au cours de ce processus, supprimez-le de la carte.

Notez que vous pouvez traiter une quantité infinie de données avec une quantité de stockage fixe (uniquement la carte de taille fixe). La quantité de stockage requise dépend uniquement du seuil d’intérêt, et la taille du flux n’a pas d’importance.

Comptage des recherches

Dans ce contexte, vous pouvez peut-être mettre en mémoire tampon une heure de recherches et effectuer ce processus sur les données de cette heure. Si vous pouvez effectuer un second passage dans le journal de recherche de cette heure, vous pouvez obtenir un nombre exact d'occurrences des meilleurs "candidats" identifiés lors du premier passage. Ou peut-être qu'il est acceptable de faire un seul laissez-passer et de signaler tous les candidats, sachant que tout élément qui devrait y figurer est inclus, et que tous les suppléments ne sont que du bruit qui disparaîtra dans la prochaine heure.

Tous les candidats qui dépassent réellement le seuil d’intérêt sont stockés sous forme de résumé. Conservez ces résumés pendant un mois, en jetant les plus vieux toutes les heures, et vous obtiendrez une bonne approximation des termes de recherche les plus courants.

53
erickson

C'est l'un des projets de recherche que je mène actuellement. L’exigence est presque identique à la vôtre et nous avons développé des algorithmes de Nice pour résoudre le problème. 

L'entrée

L'entrée est un flot incessant de mots ou de phrases anglais (nous les appelons tokens). 

Le résultat

  1. Nous avons vu tellement Loin (de tous les jetons que nous avons Vu!)
  2. Affiche les N premiers éléments dans une fenêtre historique, par exemple, le dernier jour ou la dernière semaine.

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 qui explore sur le site Web, ce qui génère un flux de mots qui alimentera le système. Le système affichera ensuite les mots ou les expressions de la fréquence la plus élevée, soit globalement, soit historiquement. Imaginez, ces dernières semaines, l'expression "Coupe du monde" apparaîtrait plusieurs fois sur Twitter. Il en va de même de "Paul la pieuvre". :) 

Chaîne en nombres entiers

Le système a un identifiant entier pour chaque mot. Bien qu'il y ait 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 basse. Nous avons déjà trouvé 4 millions de mots différents et attribué un identifiant unique à chacun. Cet ensemble 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 nécessite une 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 est beaucoup plus rapide que sur les chaînes.

Données archivé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 la partitionner physiquement. Une fois, le schéma de partition est basé sur les ngrams du jeton. Si le jeton est un mot unique, il s'agit d'un gramme. Si le jeton est une phrase de deux mots, c'est 2gram. Et ça continue. À peu près à 4 grammes, nous avons 1 milliard d'enregistrements, avec une table d'environ 60 Go.

Traitement des flux entrants

Le système absorbera 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 les avoir mémorisées, le système se met en pause et commence à segmenter chaque phrase en mots et en phrases. Chaque jeton (mot ou expression) est compté. 

Pour les jetons très fréquents, ils sont toujours gardés en mémoire. Pour les jetons moins fréquents, ils sont triés en fonction des identifiants (rappelons que nous traduisons la chaîne en un tableau d'entiers) et sérialisés dans un fichier de disque. 

(Cependant, pour votre problème, étant donné que vous ne comptez que des mots, vous pouvez mettre toute la carte Word-frequency en mémoire uniquement. Une structure de données soigneusement conçue ne consomme que 300 Mo de mémoire pour 4 millions de mots différents. Quelques astuces: utilisez ASCII char pour représenter Strings), ce qui est très acceptable.

Pendant ce temps, un autre processus sera activé lorsqu'il trouvera un fichier de disque généré par le système, puis commencera à le fusionner. Puisque le fichier de disque est trié, la fusion prendrait un processus similaire au tri par fusion. Certains dessins doivent également être pris en compte, car nous voulons éviter trop de recherches aléatoires sur le disque. 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. Ceci est similaire à la mise en œuvre d'un verrouillage. 

Fin de la journée

En fin de journée, le système aura de nombreux jetons fréquents avec une fréquence stockée en mémoire, ainsi que de nombreux autres jetons moins fréquents stockés dans plusieurs fichiers de disque (et chaque fichier est trié).

Le système vide la mappe en mémoire dans un fichier de disque (le trie). Maintenant, le problème devient la fusion d'un ensemble de fichier de disque trié. En utilisant un processus similaire, nous aurions un fichier disque trié à la fin.

La dernière tâche consiste ensuite à fusionner le fichier de disque trié dans la base de données d’archive. Dépend de la taille de la base de données d’archives, l’algorithme fonctionne comme ci-dessous s’il est assez gros:

   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ération sera sur la mise à jour seulement. Et cette mise à jour ne sera pas pénalisée par index.

J'espère que toute cette explication pourrait aider. :)

17
SiLent SoNG

Vous pouvez utiliser une table de hachage associée à un arbre de recherche binaire . Implémentez un dictionnaire <search term, count> qui vous indique combien de fois chaque terme de recherche a été recherché. 

Évidemment, parcourir la table de hachage en entier toutes les heures pour obtenir le top 10 est très mauvais. Mais nous parlons de Google, vous pouvez donc supposer que les dix premiers obtiendront tous, disons, plus de 10 000 visites (il s'agit probablement d'un nombre beaucoup plus important). Ainsi, chaque fois qu'un nombre de termes de recherche dépasse 10 000, insérez-le dans le fichier BST. Ensuite, toutes les heures, il vous suffit de récupérer les 10 premières heures du BST, qui devrait contenir relativement peu d'entrées.

Cela résout le problème du top 10 de tous les temps.


La partie la plus délicate est qu’un terme prenne la place d’un terme dans le rapport mensuel (par exemple, "stack overflow" pourrait avoir 50 000 hits au cours des deux derniers mois, mais seulement 10 000 le mois dernier, tandis que "Amazon" pourrait en contenir 40 000 pour les deux derniers mois mais 30 000 pour le dernier mois. Vous voulez qu'Amazon vienne avant le "débordement de pile" dans votre rapport mensuel). Pour ce faire, je stockais, pour tous les termes de recherche majeurs (plus de 10 000 recherches de tous les temps), une liste de 30 jours indiquant le nombre de recherches effectuées sur ce terme chaque jour. La liste fonctionnerait comme une file d'attente FIFO: vous supprimez le premier jour et en insère un nouveau tous les jours (ou toutes les heures, mais vous devrez peut-être stocker davantage d'informations, ce qui signifie davantage de mémoire/espace. pas un problème le faire, sinon optez pour cette "approximation" dont ils parlent).

Cela ressemble à un bon début. Vous pouvez alors vous inquiéter de la taille des termes qui ont plus de 10 000 occurrences mais n'en ont pas eu beaucoup depuis longtemps et ainsi de suite. 

4
IVlad

cas i)

Conservez une table de hachage pour tous les termes de recherche, ainsi qu'une liste triée des 10 premiers éléments distincte de la table de hachage. Chaque fois qu'une recherche est effectuée, incrémentez l'élément approprié dans la table de hachage et vérifiez si cet élément doit maintenant être remplacé par le dixième élément de la liste des dix premiers.

O (1) recherche dans la liste des dix premiers, et insertion maximale O(log(n)) dans la table de hachage (en supposant que les collisions soient gérées par un arbre binaire à équilibrage automatique).

cas ii) Au lieu de conserver une grande table de hachage et une petite liste, nous maintenons une table de hachage et une liste triée de tous les éléments. Chaque fois qu'une recherche est effectuée, ce terme est incrémenté dans la table de hachage et, dans la liste triée, le terme peut être vérifié pour voir s'il doit basculer avec le terme suivant. Un arbre binaire à auto-équilibrage pourrait bien fonctionner pour cela, car nous devons aussi pouvoir l'interroger rapidement (plus sur cela plus tard). 

En outre, nous gérons également une liste d’heures sous la forme d’une liste FIFO (file d’attente). Chaque élément "heure" contiendrait une liste de toutes les recherches effectuées au cours de cette heure. Ainsi, par exemple, notre liste d’heures pourrait ressembler à ceci:

Time: 0 hours
      -Search Terms:
          -free stuff: 56
          -funny pics: 321
          -stackoverflow: 1234
Time: 1 hour
      -Search Terms:
          -ebay: 12
          -funny pics: 1
          -stackoverflow: 522
          -BP sucks: 92

Ensuite, toutes les heures: si la liste a au moins 720 heures (c'est le nombre d'heures sur 30 jours), examinez le premier élément de la liste et décrivez-le pour chaque terme recherché, décrémentez-le de la quantité appropriée. . Ensuite, supprimez cet élément de la première heure de la liste.

Supposons donc que nous en sommes à l'heure 721 et que nous sommes prêts à examiner la première heure de notre liste (ci-dessus). Nous décrémentions les éléments gratuits de 56 points dans la table de hachage, les images amusantes de 321, etc., puis supprimions complètement l’heure 0 de la liste, car nous n’aurons jamais besoin de les revoir.

La raison pour laquelle nous maintenons une liste triée de tous les termes permettant des requêtes rapides est que, toutes les heures après, lorsque nous parcourons les termes de recherche d'il y a 720 heures, nous devons nous assurer que la liste des dix premiers postes reste triée. Ainsi, lorsque nous décrémentons les «éléments gratuits» de 56 dans la table de hachage, par exemple, nous vérifions pour voir à quel emplacement ils appartiennent maintenant dans la liste. Comme il s'agit d'un arbre binaire à auto-équilibrage, tout cela peut être accompli facilement en O(log(n)).


Edit: sacrifier la précision pour l'espace ...

Il pourrait être utile de mettre également en place une grande liste dans la première, comme dans la seconde. Nous pourrions ensuite appliquer l'optimisation de l'espace suivante dans les deux cas: Exécutez un travail cron pour supprimer tous les éléments sauf les premiers x de la liste. Cela réduirait l'espace requis (et par conséquent rendrait les requêtes de la liste plus rapides). Bien sûr, cela donnerait un résultat approximatif, mais cela est autorisé. x peut être calculé avant de déployer l'application en fonction de la mémoire disponible et ajusté dynamiquement si davantage de mémoire devient disponible.

3
Cam

Les 10 termes de recherche du mois dernier

L’utilisation d’une structure d’indexation/de données économe en mémoire, telle que try-condensé (à partir d’entrées wikipedia sur try ) définit approximativement une relation entre les besoins en mémoire et le nombre n de termes.

Si la mémoire requise est disponible (hypothèse 1), vous pouvez conserver des statistiques mensuelles exactes et les regrouper tous les mois en statistiques de tous les temps.

Il y a aussi une hypothèse ici qui interprète le «mois dernier» comme une fenêtre fixe . Mais même si la fenêtre mensuelle glisse, la procédure ci-dessus montre le principe (le glissement peut être rapproché avec des fenêtres fixes de taille donnée).

Cela me rappelle la base de données round-robin à l'exception que certaines statistiques sont calculées sur "tout le temps" (dans le sens où toutes les données ne sont pas conservées; rrd consolide les périodes en ignorant les détails en faisant la moyenne, la somme ou en choisissant max. Les valeurs/min, dans une tâche donnée, le détail perdu est une information sur les éléments de basse fréquence, ce qui peut introduire des erreurs).

Hypothèse 1

Si nous ne pouvons pas disposer de statistiques parfaites pour tout le mois, nous devrions être en mesure de trouver une certaine période P pour laquelle nous devrions être en mesure de conserver des statistiques parfaites… .. Par exemple, en supposant que nous ayons des statistiques parfaites sur une période donnée P , qui entre dans le mois n fois.
Les statistiques parfaites définissent la fonction f(search_term) -> search_term_occurance.

Si nous pouvons garder toutes les tables de statistiques n parfaites en mémoire, les statistiques mensuelles glissantes peuvent être calculées comme suit:

  • ajouter des statistiques pour la nouvelle période
  • supprime les statistiques pour la période la plus ancienne (il faut donc garder n tables de statistiques parfaites)

Cependant, si nous ne conservons que le top 10 au niveau agrégé (mensuel), nous pourrons alors écarter un grand nombre de données des statistiques complètes de la période fixée. Cela donne déjà une procédure de travail qui a des besoins en mémoire fixes (en supposant que la limite supérieure de la table statistique est parfaite pour la période P).

Le problème avec la procédure ci-dessus est que si nous conservons des informations uniquement sur les 10 termes les plus utilisés dans une fenêtre glissante (de la même manière pour tous les temps), les statistiques seront correctes pour les termes de recherche présentant un pic au cours d'une période donnée, mais pouvant ne pas s'afficher. statistiques pour les termes de recherche qui arrivent constamment dans le temps.

Cela peut être compensé en conservant des informations sur plus de 10 termes principaux, par exemple les 100 termes les plus importants, en espérant que les 10 meilleurs termes seront corrects.

Je pense qu'une analyse plus approfondie pourrait établir un lien entre le nombre minimum d'occurrences nécessaires pour qu'une entrée fasse partie des statistiques (ce qui est lié à l'erreur maximale).

(Pour décider quelles entrées doivent faire partie des statistiques, vous pouvez également suivre et suivre les tendances; par exemple, si une extrapolation linéaire des occurrences dans chaque période, P pour chaque terme vous indique que le terme deviendra significatif dans un mois ou deux. Le principe similaire s’applique pour supprimer le terme de recherche du pool suivi.)

Le pire cas de ce qui précède est lorsque vous avez beaucoup de termes presque aussi fréquents et qu’ils changent tout le temps (par exemple, si vous ne suivez que 100 termes, alors si les 150 termes les plus fréquents sont fréquents, les 50 premiers étant plus souvent le premier mois). de temps en temps, les statistiques ne seront pas conservées correctement).

Il pourrait aussi exister une autre approche qui n’est pas fixée dans la taille de la mémoire (ce n’est pas non plus le cas ci-dessus), qui définirait une importance minimale en termes d’occurrences/période (jour, mois, année, de tous les temps) pendant Statistiques. Cela pourrait garantir une erreur maximale dans chacune des statistiques lors de l'agrégation (voir à nouveau le tourniquet).

2
Unreason

Solution exacte

Premièrement, une solution qui garantit des résultats corrects, mais nécessite beaucoup de mémoire (une grande carte).

Variante "de tous les temps"

Maintenez une carte de hachage avec les requêtes sous forme de clés et leur nombre en tant que valeurs. En outre, gardez une liste des 10 requêtes les plus fréquentes jusqu’à présent et le nombre des 10 plus fréquentes (un seuil).

Mettez constamment à jour la carte au fur et à mesure que le flux de requêtes est lu. Procédez comme suit chaque fois qu'un nombre dépasse le seuil actuel: supprimez la dixième requête de la liste "Top 10", remplacez-la par la requête que vous venez de mettre à jour et mettez également à jour le seuil.

Variante "mois passé"

Conservez la même liste "Top 10" et mettez-la à jour de la même manière que ci-dessus. Conservez également une carte similaire, mais stockez des vecteurs de 30 * 24 = 720 (un par heure) comme valeurs. Toutes les heures, procédez comme suit pour chaque clé: supprimez le compteur le plus ancien du vecteur et ajoutez-en un nouveau (initialisé à 0) à la fin. Supprimez la clé de la carte si le vecteur est à zéro. En outre, chaque heure, vous devez calculer la liste "Top 10" à partir de zéro.

Remarque: Oui, cette fois, nous stockons 720 nombres entiers au lieu d'un, mais il y a beaucoup moins de clés (la variante de tous les temps a un vraiment long tail).

Approximations

Ces approximations ne garantissent pas la solution correcte, mais consomment moins de mémoire.

  1. Traitez chaque énième requête en ignorant le reste.
  2. (Pour la variante de tous les temps uniquement) Conservez au maximum M paires clé-valeur dans la carte (M devrait être aussi grand que vous pouvez vous le permettre). C'est une sorte de cache LRU: chaque fois que vous lisez une requête qui ne se trouve pas sur la carte, supprimez la requête la moins récemment utilisée avec le nombre 1 et remplacez-la par la requête en cours de traitement.
2
Bolo

Pensée approximative ...

Pour le top 10 de tous les temps

  • Utiliser une collection de hachage où un compte pour chaque terme est stocké (nettoie les termes, etc.)
  • Un tableau trié contenant le top 10 en cours, un terme/nombre ajouté à ce tableau chaque fois que le nombre d'un terme devient égal ou supérieur au plus petit nombre du tableau.

Pour le top 10 mensuel mis à jour toutes les heures:

  • Utilisation d'un tableau indexé sur le nombre d'heures écoulées depuis le début du modulo 744 (nombre d'heures pendant un mois), lesquelles entrées de tableau consistent en une collection de hachages où un compte pour chaque terme rencontré pendant cette heure est stocké. Une entrée est réinitialisée chaque fois que le compteur d'heures est modifié
  • les statistiques du tableau indexé sur le créneau horaire doivent être collectées chaque fois que le compteur du créneau horaire actuel change (au maximum une fois par heure), en copiant et en aplatissant le contenu de ce tableau indexé sur les créneaux horaires

Euh ... ça a du sens? Je n'ai pas réfléchi comme je le ferais dans la vraie vie

Ah oui, j'ai oublié de mentionner que la "copie/mise à plat" horaire requise pour les statistiques mensuelles peut en réalité réutiliser le même code que celui utilisé pour le top 10 de tous les temps, un effet secondaire sympa.

2
R. Hill

Qu'en est-il de l'adaptation de "algorithme de remplacement de page d'horloge" (également appelé "deuxième chance")? J'imagine que cela fonctionnera très bien si les demandes de recherche sont distribuées de manière uniforme (cela signifie que les termes les plus recherchés apparaissent régulièrement plutôt que 5 millions de fois d'affilée, puis plus jamais).

Voici une représentation visuelle de l'algorithme: clock page replacement algorithm

2
Dave O.

Stockez le nombre de termes de recherche dans une table de hachage géante, où chaque nouvelle recherche entraîne l’incrémentation d’un élément particulier. Gardez une trace des 20 termes principaux de recherche ou plus; Lorsque l’élément à la 11ème place est incrémenté, vérifiez s’il doit échanger les positions avec # 10 * (il n’est pas nécessaire de garder le top 10 trié; vous ne vous souciez que de faire la distinction entre 10ème et 11ème).

*Des vérifications similaires doivent être effectuées pour voir si un nouveau terme de recherche se trouve à la 11ème place. Cet algorithme est donc également lié à d'autres termes de recherche - je simplifie donc un peu.

0
Ether

parfois, la meilleure réponse est "je ne sais pas". 

Je vais prendre un coup plus profond. Mon premier instinct serait d’alimenter les résultats avec un Q. Un processus traiterait continuellement les éléments entrant dans le Q. Le processus conserverait une carte de 

terme -> compte

chaque fois qu'un élément Q est traité, il vous suffit de rechercher le terme recherché et d'incrémenter le nombre. 

Dans le même temps, je maintiendrais une liste de références aux 10 premières entrées de la carte. 

Pour l'entrée en cours d'implémentation, vérifiez si son nombre est supérieur au nombre d'éléments de la plus petite entrée dans le top 10. (si ce n'est déjà dans la liste). Si c'est le cas, remplacez le plus petit par l'entrée.

Je pense que cela fonctionnerait. Aucune opération ne prend beaucoup de temps. Vous devez trouver un moyen de gérer la taille de la carte de comptage. mais cela devrait suffire pour une réponse à un entretien.

Ils ne s'attendent pas à une solution, ils veulent voir si vous pouvez penser. Vous n'avez pas à écrire la solution alors et là ....

0
hvgotcodes

Qu'en est-il d'utiliser un Splay Tree avec 10 nœuds? Chaque fois que vous essayez d'accéder à une valeur (terme de recherche) qui ne figure pas dans l'arborescence, jetez une feuille, insérez-la à la place et accédez-y.

L'idée sous-jacente est la même que dans mon autre réponse . En supposant que les termes de recherche sont utilisés de manière uniforme/régulière, cette solution devrait fonctionner très bien.

modifier

On pourrait aussi stocker davantage de termes de recherche dans l’arborescence (il en va de même pour la solution que je suggère dans mon autre réponse) afin de ne pas supprimer un nœud auquel on pourrait accéder très bientôt. Plus on y stocke de valeurs, meilleurs sont les résultats.

0
Dave O.

Une façon est que pour chaque recherche, vous stockez ce terme de recherche et son horodatage. De cette façon, trouver les dix meilleurs pour une période donnée consiste simplement à comparer tous les termes de la recherche dans la période donnée.

L'algorithme est simple, mais l'inconvénient serait une plus grande consommation de mémoire et de temps.

0
Jesse Jashinsky

Le problème ne peut pas être résolu universellement lorsque vous avez une quantité de mémoire fixe et un flux de jetons «infini» (pensez très très gros).

Une explication approximative ...

Pour voir pourquoi, considérons un flux de jetons comportant un jeton particulier (Word) T tous les N jetons dans le flux d'entrée.

En outre, supposons que la mémoire puisse contenir des références (identifiant et nombre de mots) d'au plus M jetons.

Avec ces conditions, il est possible de construire un flux d'entrée dans lequel le jeton T ne sera jamais détecté si le N est suffisamment grand pour que le flux contienne différents M jetons entre les T.

Ceci est indépendant des détails de l'algorithme top-N. Cela ne dépend que de la limite M.

Pour comprendre pourquoi cela est vrai, considérons le flux entrant constitué de groupes de deux jetons identiques:

T a1 a2 a3 ... a-M T b1 b2 b3 ... b-M ...

où les a et les b sont des jetons valides différents de T.

Notez que dans ce flux, le T apparaît deux fois pour chaque a-i et b-i. Pourtant, il semble rarement suffisant d'être éliminé du système.

En commençant par une mémoire vide, le premier jeton (T) occupera un emplacement dans la mémoire (délimité par M). Ensuite, a1 utilisera une fente jusqu’à a- (M-1) lorsque le M est épuisé.

Quand a-M arrive, l'algorithme doit supprimer un symbole, alors laissez-le être le T . Le prochain symbole sera b-1, ce qui fera vider a-1, etc.

Donc, le T ne restera pas dans la mémoire résident assez longtemps pour construire un compte réel. En bref, tout algorithme manquera un jeton de fréquence locale suffisamment basse mais de fréquence globale élevée (sur la longueur du flux).

0
david marcus

Je sais pas si je comprends bien ou pas. Ma solution utilise tas. En raison du top 10 des éléments de recherche, je crée un segment de taille 10. puis met à jour ce segment avec une nouvelle recherche. Si la fréquence d'une nouvelle recherche est supérieure à heap (Max Heap), mettez-la à jour. Abandonner celui avec la plus petite fréquence. 

Mais, comment calculer la fréquence de la recherche spécifique sera compté sur autre chose. Comme tout le monde l’a dit, l’algorithme de flux de données .... 

0
Chris

Utilisez cm-sketch pour stocker le nombre total de recherches depuis le début, conservez un min-tas de taille 10 pour le top 10 . Pour obtenir un résultat mensuel, conservez 30 cm-sketch/hash-table et min-heap, chacun commence à compter et à mettre à jour à partir des 30, 29 .., 1 derniers jours. En tant que laissez-passer journalier, effacez le dernier et utilisez-le comme jour 1.. Idem pour le résultat horaire, gardez 60 tables de hachage et min-tas et commencez à compter depuis 60, 59, ... 1 minute. En une minute, effacez le dernier et utilisez-le comme minute 1.

Le résultat mensuel est précis dans l'intervalle de 1 jour, le résultat horaire est exact dans l'intervalle de 1 min.

0
Jingyi Fang