He estado aprendiendo sobre diferentes algoritmos en mi tiempo libre recientemente, y uno que encontré que parece ser muy interesante se llama algoritmo HyperLogLog, que estima cuántos elementos únicos hay en una lista.
Esto fue particularmente interesante para mí porque me trajo de vuelta a mis días de MySQL cuando vi ese valor de "Cardinalidad" (que siempre supuse hasta hace poco que se calculaba no se estimaba).
Entonces sé cómo escribir un algoritmo en O ( n ) que calculará cuántos elementos únicos hay en una matriz. Escribí esto 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;
}
Pero el problema es que mi algoritmo, mientras O ( n ), usa mucha memoria (almacenando valores en Table
).
He estado leyendo este documento sobre cómo contar duplicados en una lista en el tiempo O ( n ) y usando un mínimo de memoria.
Explica que mediante el hash y contando bits o algo se puede estimar dentro de una cierta probabilidad (suponiendo que la lista se distribuya uniformemente) el número de elementos únicos en una lista.
He leído el periódico, pero parece que no puedo entenderlo. ¿Alguien puede dar una explicación más laica? Sé qué son los hashes, pero no entiendo cómo se usan en este algoritmo HyperLogLog.
Respuestas:
El truco principal detrás de este algoritmo es que si usted, observando una secuencia de enteros aleatorios, ve un número entero cuya representación binaria comienza con algún prefijo conocido, existe una mayor probabilidad de que la cardinalidad de la secuencia sea 2 ^ (tamaño del prefijo) .
Es decir, en una secuencia aleatoria de enteros, ~ 50% de los números (en binario) comienza con "1", 25% comienza con "01", 12,5% comienza con "001". Esto significa que si observa una secuencia aleatoria y ve un "001", existe una mayor probabilidad de que esta secuencia tenga una cardinalidad de 8.
(El prefijo "00..1" no tiene un significado especial. Está ahí solo porque es fácil encontrar el bit más significativo en un número binario en la mayoría de los procesadores)
Por supuesto, si observa solo un número entero, la probabilidad de que este valor sea incorrecto es alta. Es por eso que el algoritmo divide la secuencia en "m" subcadenas independientes y mantiene la longitud máxima de un prefijo "00 ... 1" visto de cada subcorriente. Luego, estima el valor final tomando el valor medio de cada subcorriente.
Esa es la idea principal de este algoritmo. Faltan algunos detalles (la corrección para valores estimados bajos, por ejemplo), pero todo está bien escrito en el documento. Perdón por el terrible inglés.
fuente
Un HyperLogLog es una estructura de datos probabilística . Cuenta el número de elementos distintos en una lista. Pero en comparación con una forma directa de hacerlo (tener un conjunto y agregar elementos al conjunto) lo hace de manera aproximada.
Antes de ver cómo el algoritmo HyperLogLog hace esto, uno tiene que entender por qué lo necesita. El problema con una forma directa es que consume
O(distinct elements)
espacio. ¿Por qué hay una gran notación O aquí en lugar de solo elementos distintos? Esto se debe a que los elementos pueden ser de diferentes tamaños. Un elemento puede ser1
otro elemento"is this big string"
. Entonces, si tiene una lista enorme (o una gran corriente de elementos), le tomará mucha memoria.Conteo probabilístico
¿Cómo se puede obtener una estimación razonable de varios elementos únicos? Suponga que tiene una cadena de longitud
m
que consta de{0, 1}
igual probabilidad. ¿Cuál es la probabilidad de que comience con 0, con 2 ceros, con k ceros? Es1/2
,1/4
y1/2^k
. Esto significa que si ha encontrado una cadena conk
ceros, ha examinado aproximadamente los2^k
elementos. Entonces este es un buen punto de partida. Tener una lista de elementos que se distribuyen uniformemente0
y2^k - 1
puede contar el número máximo del prefijo más grande de ceros en representación binaria y esto le dará una estimación razonable.El problema es que la suposición de tener números distribuidos uniformemente desde
0
t2^k-1
es demasiado difícil de lograr (los datos que encontramos no son en su mayoría números, casi nunca se distribuyen de manera uniforme, y pueden estar entre cualquier valor. Pero al usar una buena función de hash puedes asumir que los bits de salida se distribuirían uniformemente y la mayoría de las funciones de hashing tienen salidas entre0
y2^k - 1
( SHA1 le da valores entre0
y2^160
). Entonces, lo que hemos logrado hasta ahora es que podemos estimar el número de elementos únicos con la cardinalidad máxima dek
bits almacenando solo un número delog(k)
bits de tamaño . La desventaja es que tenemos una gran variación en nuestra estimación. Una cosa genial que casi creamosDocumento de conteo probabilístico de 1984 (es un poco más inteligente con la estimación, pero aún estamos cerca).LogLog
Antes de continuar, tenemos que entender por qué nuestra primera estimación no es tan buena. La razón detrás de esto es que una ocurrencia aleatoria del elemento de prefijo 0 de alta frecuencia puede estropear todo. Una forma de mejorarlo es usar muchas funciones hash, contar el máximo para cada una de las funciones hash y al final promediarlas. Esta es una idea excelente, que mejorará la estimación, pero el papel LogLog utilizó un enfoque ligeramente diferente (probablemente porque el hashing es algo costoso).
Usaron un hash pero lo dividieron en dos partes. Uno se llama un cubo (el número total de cubos es
2^x
) y otro, es básicamente el mismo que nuestro hash. Fue difícil para mí entender lo que estaba pasando, así que daré un ejemplo. Suponga que tiene dos elementos y su función hash que le da forma0
a los valores para2^10
producir 2 valores:344
y387
. Decidiste tener 16 cubos. Así que tienes:Al tener más cubos, disminuye la variación (usa un poco más de espacio, pero aún es pequeño). Usando habilidades matemáticas, pudieron cuantificar el error (que es
1.3/sqrt(number of buckets)
).HyperLogLog
HyperLogLog no introduce ninguna idea nueva, pero en su mayoría utiliza muchas matemáticas para mejorar la estimación anterior. Los investigadores han descubierto que si eliminas el 30% de los números más grandes de los cubos, mejorarás significativamente la estimación. También usaron otro algoritmo para promediar números. El papel es matemático pesado.
Y quiero terminar con un artículo reciente, que muestra una versión mejorada del algoritmo hyperLogLog (hasta ahora no tenía tiempo para entenderlo completamente, pero tal vez más adelante mejoraré esta respuesta).
fuente
k zeroes
que no es una cosa especial. en su lugar, puede buscark ones
y la lógica sería la misma o incluso buscar unak length
cadena,{0,1}
pero tomar una de esas cadenas y quedarse con ella. porque todos tienen la misma probabilidad de 1/2 ^ k en caso de tales cadenas binarias?La intuición es que si su entrada es un conjunto grande de números aleatorios (por ejemplo, valores hash), deben distribuirse uniformemente en un rango. Digamos que el rango es de hasta 10 bits para representar un valor de hasta 1024. Luego se observó el valor mínimo. Digamos que es 10. Entonces la cardinalidad se estimará en aproximadamente 100 (10 × 100 × 1024).
Lea el periódico para la lógica real, por supuesto.
Aquí puede encontrar otra buena explicación con un código de muestra:
Malditos algoritmos geniales: Estimación de la cardinalidad - Nick's Blog
fuente