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.
Réponses:
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.
la source
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 être1
un 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
m
composé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'est1/2
,1/4
et1/2^k
. Cela signifie que si vous avez rencontré une chaîne avec desk
zéros, vous avez approximativement parcouru les2^k
éléments. C'est donc un bon point de départ. Avoir une liste d'éléments qui sont répartis uniformément entre0
et2^k - 1
vous 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
0
t2^k-1
est 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 entre0
et2^k - 1
( SHA1 vous donne des valeurs entre0
et2^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 dek
bits en ne stockant que un certain nombre delog(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 valeurs0
aux2^10
2 valeurs produites:344
et387
. Vous avez décidé d'avoir 16 seaux. Donc vous avez: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).
la source
k zeroes
n'est pas une chose spéciale. vous pouvez plutôt rechercherk ones
et la logique serait la même ou même rechercher unek length
chaî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?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
la source