Comment fonctionne l'algorithme HyperLogLog?

172

J'ai récemment découvert différents algorithmes pendant mon temps libre, et l'un que j'ai rencontré et qui semble très intéressant s'appelle l'algorithme HyperLogLog - qui estime le nombre d'éléments uniques dans une liste.

Cela a été particulièrement intéressant pour moi car cela m'a ramené à mes jours MySQL quand j'ai vu cette valeur "Cardinality" (que j'ai toujours supposé jusqu'à récemment qu'elle était calculée et non estimée).

Je sais donc comment écrire un algorithme en O ( n ) qui calculera le nombre d'éléments uniques dans un tableau. J'ai écrit ceci en JavaScript:

function countUniqueAlgo1(arr) {
    var Table = {};
    var numUnique = 0;
    var numDataPoints = arr.length;
    for (var j = 0; j < numDataPoints; j++) {
        var val = arr[j];
        if (Table[val] != null) {
            continue;
        }
        Table[val] = 1;
        numUnique++;
    }
    return numUnique;
}

Mais le problème est que mon algorithme, bien que O ( n ), utilise beaucoup de mémoire (stockage des valeurs Table).

J'ai lu cet article sur la façon de compter les doublons dans une liste en temps O ( n ) et en utilisant une mémoire minimale.

Il explique qu'en hachant et en comptant des bits ou quelque chose, on peut estimer avec une certaine probabilité (en supposant que la liste est uniformément répartie) le nombre d'éléments uniques dans une liste.

J'ai lu le journal, mais je n'arrive pas à le comprendre. Quelqu'un peut-il donner une explication plus profane? Je sais ce que sont les hachages, mais je ne comprends pas comment ils sont utilisés dans cet algorithme HyperLogLog.

K2xL
la source
4
Cet article ( research.google.com/pubs/pub40671.html ) résume également l'algorithme HyperLogLog et quelques améliorations. Je pense que c'est plus facile à comprendre que l'article original.
zhanxw
11
Juste un indice sur la nomenclature: certaines personnes utilisent le mot ensemble pour décrire une collection d' objets uniques . Pour eux, votre question pourrait avoir plus de sens si vous utilisiez plutôt le terme liste ou tableau.
Paddy3118

Réponses:

153

L'astuce principale derrière cet algorithme est que si vous, en observant un flux d'entiers aléatoires, voyez un entier dont la représentation binaire commence par un préfixe connu, il y a plus de chances que la cardinalité du flux soit 2 ^ (taille du préfixe) .

Autrement dit, dans un flux aléatoire d'entiers, ~ 50% des nombres (en binaire) commencent par "1", 25% commencent par "01", 12,5% commencent par "001". Cela signifie que si vous observez un flux aléatoire et voyez un "001", il y a plus de chances que ce flux ait une cardinalité de 8.

(Le préfixe "00..1" n'a pas de signification particulière. Il est là simplement parce qu'il est facile de trouver le bit le plus significatif d'un nombre binaire dans la plupart des processeurs)

Bien sûr, si vous observez un seul entier, le risque que cette valeur soit erronée est élevé. C'est pourquoi l'algorithme divise le flux en "m" sous-flux indépendants et conserve la longueur maximale d'un préfixe "00 ... 1" vu de chaque sous-flux. Ensuite, estime la valeur finale en prenant la valeur moyenne de chaque sous-flux.

C'est l'idée principale de cet algorithme. Il manque des détails (la correction pour les valeurs d'estimation faibles, par exemple), mais tout est bien écrit dans l'article. Désolé pour le terrible anglais.

Juan Lopes
la source
"il y a plus de chances que ce flux ait une cardinalité de 8" Pouvez-vous expliquer pourquoi 000 signifie le nombre prévu d'essais 2 ^ 3. J'ai essayé de calculer l'espérance mathématique du nombre d'essais en supposant que nous ayons au moins une exécution avec 3 zéros et aucune exécution avec 4 zéros ...
yura
5
Je n'ai pas bien compris le journal avant de lire ceci. Maintenant, cela a du sens.
josiah
5
@yura Je sais que c'est un très vieux commentaire, mais il peut être utile pour d'autres personnes. Il a dit "Autrement dit, dans un flux aléatoire d'entiers, (...) 12,5% commence par" 001 "." La cardinalité probable est de 8 car 12,5% représente un huitième de l'ensemble du flux.
braunmagrin
111

Un HyperLogLog est une structure de données probabiliste . Il compte le nombre d'éléments distincts dans une liste. Mais par rapport à une manière simple de le faire (avoir un ensemble et ajouter des éléments à l'ensemble), il le fait de manière approximative.

Avant de regarder comment l'algorithme HyperLogLog fait cela, il faut comprendre pourquoi vous en avez besoin. Le problème avec un moyen simple est qu'il consomme O(distinct elements)de l'espace. Pourquoi y a-t-il une grosse notation O ici au lieu de seulement des éléments distincts? En effet, les éléments peuvent être de différentes tailles. Un élément peut être 1un autre élément "is this big string". Donc, si vous avez une liste énorme (ou un énorme flux d'éléments), cela prendra beaucoup de mémoire.


Comptage probabiliste

Comment peut-on obtenir une estimation raisonnable d'un certain nombre d'éléments uniques? Supposons que vous ayez une chaîne de longueur mcomposée de {0, 1}avec une probabilité égale. Quelle est la probabilité qu'il commence par 0, avec 2 zéros, avec k zéros? C'est 1/2, 1/4et 1/2^k. Cela signifie que si vous avez rencontré une chaîne avec des kzéros, vous avez approximativement parcouru les 2^kéléments. C'est donc un bon point de départ. Avoir une liste d'éléments qui sont répartis uniformément entre 0et 2^k - 1vous pouvez compter le nombre maximum du plus grand préfixe de zéros dans la représentation binaire et cela vous donnera une estimation raisonnable.

Le problème est que l'hypothèse d'avoir des nombres uniformément répartis à partir de 0t 2^k-1est trop difficile à réaliser (les données que nous avons rencontrées ne sont généralement pas des nombres, presque jamais uniformément distribuées, et peuvent être entre toutes les valeurs. Mais en utilisant une bonne fonction de hachage, vous pouvez supposer que les bits de sortie seraient uniformément répartis et la plupart des fonctions de hachage ont des sorties entre 0et 2^k - 1( SHA1 vous donne des valeurs entre 0et 2^160). Donc, ce que nous avons réalisé jusqu'à présent, c'est que nous pouvons estimer le nombre d'éléments uniques avec la cardinalité maximale de kbits en ne stockant que un certain nombre de log(k)bits de taille . L'inconvénient est que nous avons un énorme écart dans notre estimation. Une chose cool que nous avons presque crééePapier de comptage probabiliste de 1984 (c'est un peu plus intelligent avec l'estimation, mais nous sommes quand même proches).

LogLog

Avant d'aller plus loin, nous devons comprendre pourquoi notre première estimation n'est pas si bonne. La raison en est qu'une occurrence aléatoire d'un élément de préfixe 0 haute fréquence peut tout gâcher. Une façon de l'améliorer est d'utiliser de nombreuses fonctions de hachage, de compter au maximum pour chacune des fonctions de hachage et à la fin de les faire la moyenne. C'est une excellente idée, qui améliorera l'estimation, mais le papier LogLog a utilisé une approche légèrement différente (probablement parce que le hachage est assez cher).

Ils ont utilisé un hachage mais l'ont divisé en deux parties. L'un s'appelle un seau (le nombre total de seaux est 2^x) et l'autre - est fondamentalement le même que notre hachage. J'ai eu du mal à comprendre ce qui se passait, alors je vais donner un exemple. Supposons que vous ayez deux éléments et votre fonction de hachage qui donne la forme de valeurs 0aux 2^102 valeurs produites: 344et 387. Vous avez décidé d'avoir 16 seaux. Donc vous avez:

0101 011000  bucket 5 will store 1
0110 000011  bucket 6 will store 4

En ayant plus de seaux, vous diminuez la variance (vous utilisez un peu plus d'espace, mais il est encore minuscule). En utilisant des compétences en mathématiques, ils ont pu quantifier l'erreur (ce qui est 1.3/sqrt(number of buckets)).

HyperLogLog

HyperLogLog n'introduit aucune nouvelle idée, mais utilise principalement beaucoup de mathématiques pour améliorer l'estimation précédente. Les chercheurs ont constaté que si vous supprimez 30% des plus grands nombres des seaux, vous améliorez considérablement l'estimation. Ils ont également utilisé un autre algorithme pour calculer la moyenne des nombres. Le papier est lourd en mathématiques.


Et je veux terminer avec un article récent, qui montre une version améliorée de l'algorithme hyperLogLog (jusqu'à présent, je n'ai pas eu le temps de le comprendre pleinement, mais peut-être que plus tard j'améliorerai cette réponse).

Salvador Dali
la source
2
Je suppose qu'en théorie, ce k zeroesn'est pas une chose spéciale. vous pouvez plutôt rechercher k oneset la logique serait la même ou même rechercher une k lengthchaîne de {0,1}mais prenez une telle chaîne et respectez-la? parce que tous ont une probabilité égale de 1/2 ^ k dans le cas de telles chaînes binaires?
user881300
3
HyperLogLog ne supprime pas 30% des plus grands nombres. C'est l'idée de l'algorithme SuperLogLog également décrit dans l'article LogLog. L'idée principale de l'algorithme HyperLogLog est de faire la moyenne de la puissance de deux en utilisant la moyenne harmonique au lieu de la moyenne géométrique utilisée par SuperLogLog et LogLog.
otmar
21

L'intuition est que si votre entrée est un grand ensemble de nombres aléatoires (par exemple des valeurs hachées), ils devraient se répartir uniformément sur une plage. Disons que la plage est jusqu'à 10 bits pour représenter une valeur jusqu'à 1024. Ensuite, observé la valeur minimale. Disons que c'est 10. La cardinalité sera alors estimée à environ 100 (10 × 100 ≈ 1024).

Lisez le papier pour la vraie logique bien sûr.

Une autre bonne explication avec un exemple de code peut être trouvée ici:
Damn Cool Algorithms: Cardinality Estimation - Nick's Blog

Wai Yip Tung
la source
3
voté pour le lien vers le post de blog sur les algorithmes. cela m'a vraiment aidé à comprendre l'algorithme.
Igor Serebryany