2010-03-24 9 views
16

Me gustaría establecer claves primarias no enteras para una tabla utilizando algún tipo de función hash. md5() parece ser algo largo (32 caracteres).Cortocircuito alfanumérico corto de Python con colisiones mínimas

Cuáles son algunas de las funciones de hash alternativas que tal vez utilizan cada letra del alfabeto, así como números enteros que son, quizás, más corto en longitud de la cadena y tienen tasas bajas de colisión?

Gracias!

Respuesta

15

¿Por qué no simplemente truncar SHA1 o MD5? Tendrás más colisiones si no truncaste, pero aún así es mejor que diseñar el tuyo propio. Tenga en cuenta que puede codificar64 base el hash truncado, en lugar de usar hexadecimal. P.ej.

import base64 
import hashlib 
hasher = hashlib.sha1("The quick brown fox") 
base64.urlsafe_b64encode(hasher.digest()[0:10]) 

Puede limitar el menor (incluyendo nada) o tanto como desee, siempre y cuando usted entienda las ventajas y desventajas.

EDIT: Ya que menciona el URL de fallos, puede utilizar y urlsafe_b64decode, que utiliza - y _ en lugar de + y /.

+0

Thanks. ¿Hay alguna función hash alfanumérica de baja colisión, menos de 16 caracteres, que no implica truncar? Gracias. – ensnare

+3

¿Por qué no quieres truncar? –

+1

Es posible que también desee eliminar todos los caracteres '=' añadidos al final. No reducen sustancialmente la tasa de colisión, pero agregan dos caracteres. Así que tal vez algo como: 'base64.urlsafe_b64encode (hasher.digest() [0:10]). Replace ('=', '')' – speedplane

17

El más pequeño de hash incorporado soy consciente de es MD5

>>> import hashlib 
>>> hashlib.md5("hello worlds").digest().encode("base64") 
'uWuHitcvVnCdu1Yo4c6hjQ==\n' 

baja colisión y corta son algo mutuamente excluyentes debido a la birthday paradox

Para que sea urlsafe es necesario utilizar la función de la base 64 módulo

>>> import base64 
>>> base64.urlsafe_b64encode(hashlib.md5("hello world").digest()) 
'XrY7u-Ae7tCTyyK7j1rNww==' 

Sin embargo, no debería haber ningún problema al almacenar el resumen de md5 de 16 bytes en la base de datos en forma binaria.

>>> md5bytes=hashlib.md5("hello world").digest() 
>>> len(md5bytes) 
16 
>>> urllib.quote_plus(md5bytes) 
'%5E%B6%3B%BB%E0%1E%EE%D0%93%CB%22%BB%8FZ%CD%C3' 
>>> base64.urlsafe_b64encode(md5bytes) 
'XrY7u-Ae7tCTyyK7j1rNww==' 

Se puede elegir entre el quote_plus o la urlsafe_b64encode para su url, entonces decodificar con la función correspondiente unquote_plus o urlsafe_b64decode antes de que se vean en la base de datos.

+0

Gracias. ¿Cómo puedo hacer esto seguro? – ensnare

3

A continuación se muestra una solución que utiliza caracteres alfanuméricos además de algunos caracteres de puntuación. Devuelve cadenas muy cortas (alrededor de 8 caracteres).

import binascii, struct 

def myhash(s): 
    return binascii.b2a_base64(struct.pack('i', hash(s))) 
+1

'hash (s)' da un resultado diferente para plataformas de 32/64 bits –

+1

@gnibbler La pregunta no enumera la coherencia entre plataformas como un requisito. –

0

Puede utilizar algo como la notación base 32. Es más compacto que la notación decimal, no distingue entre mayúsculas y minúsculas y no presenta colisiones. Simplemente codifica un viejo número de secuencia simple para generar un código corto parecido a un hash.

Si la clave no es para el consumo humano, se puede utilizar la notación de base 64, que es sensible a mayúsculas, pero un poco más compacto.

Ver http://code.google.com/p/py-cupom/ para un ejemplo.