Hola colegas estadísticos,
Tengo una fuente que genera hashes (por ejemplo, calcular una cadena con una marca de tiempo y otra información y hashing con md5) y quiero proyectarla en un número fijo de cubos (digamos 100).
hash de muestra: 0fb916f0b174c66fd35ef078d861a367
Lo que pensé al principio era usar solo el primer carácter del hash para elegir un cubo, pero esto conduce a una proyección salvajemente no uniforme (es decir, algunas letras aparecen muy raramente y otras muy frecuentemente)
Luego, traté de convertir esta cadena hexa en un entero usando la suma de los valores de caracteres, luego tomé el módulo para elegir un cubo:
import sys
for line in sys.stdin:
i = 0
for c in line:
i += ord(c)
print i%100
Parece funcionar en la práctica, pero no sé si hay algún sentido común o resultados teóricos que puedan explicar por qué y en qué medida esto es cierto.
[Editar] Después de pensarlo, llegué a la siguiente conclusión: en teoría, puedes convertir el hash en un entero (muy grande) interpretándolo como un número: i = h [0] + 16 * h [1] + 16 * 16 * h [2] ... + 16 ^ 31 * h [31] (cada letra representa un número hexadecimal). Entonces podría modular este gran número para proyectarlo en el espacio del cubo. [/Editar]
Gracias !
Respuestas:
NB: poner en forma la respuesta que surgió de la discusión en los comentarios para que sea más fácil de leer para las personas interesadas
(Versión actualizada)
Supongamos que tenemos una fuente que genera eventos independientes que queremos distribuir uniformemente en cubos.B
Los pasos clave son:
Para 1. una solución popular es usar MurmurHash para generar un entero de 64 o 128 bits.
Para 3. una solución simple es iterar en y verificar que esté enj=1..B p [bjB,bj+1B[
En el seudocódigo (python), el procedimiento general podría ser:
(versión anterior, realmente no es óptima)
La primera observación es que el n carta -ésima del hash debe ser distribuido de manera uniforme con respecto al alfabeto (que es aquí 16 letras de largo - gracias a @leonbloy por señalarlo).
Luego, para proyectarlo a un rango [0,100 [, el truco consiste en tomar 2 letras del hash (por ejemplo, 1ª y 2ª posiciones) y generar un número entero con eso:Este valor vive en el rango [0,16+ (16-1) * 16 [, por lo tanto, solo tenemos que modularlo a 100 para generar un depósito en el rango [0, 100 [:como se señaló en los comentarios, haciendo así que impacta la uniformidad de la distribución ya que la primera letra es más influyente que la segunda.En teoría, puede convertir el hash completo en un entero (muy grande) interpretándolo como un número: i = h [0] + 16 * h [1] + 16 * 16 * h [2] ... + 16 ^ 31 * h [31] (cada letra representa un número hexadecimal). Entonces podría modular este gran número para proyectarlo en el espacio del cubo. Entonces se puede notar que tomar el módulo de i se puede descomponer en una operación distributiva y aditiva:
fuente
Tuve un problema similar y se me ocurrió una solución diferente que puede implementarse más rápido y más fácilmente en cualquier idioma.
Mi primer pensamiento fue enviar artículos de manera rápida y uniforme en un número fijo de cubos, y también para ser escalable, debería imitar la aleatoriedad.
Así que codifiqué esta pequeña función que devuelve un número flotante en [0, 1 [dada una cadena (o cualquier tipo de datos de hecho).
Aquí en Python:
Por supuesto, no es aleatorio, de hecho, ni siquiera es pseudoaleatorio, los mismos datos siempre devolverán la misma suma de verificación. Pero actúa como aleatorio y es bastante rápido.
Puede despachar y recuperar elementos en N depósitos simplemente asignando cada elemento al número de depósito math.floor (N * pseudo_random_checksum (item)).
fuente