2010-03-24 14 views
16

Mi piacerebbe impostare chiavi primarie non-intere per una tabella usando una qualche funzione di hash. md5() sembra essere un po 'lungo (32 caratteri).Hash alfanumerico corto Python con collisioni minime

Quali sono alcune funzioni di hash alternative che forse utilizzano ogni lettera dell'alfabeto e numeri interi forse più brevi nella lunghezza della stringa e con bassi tassi di collisione?

Grazie!

risposta

15

Perché non basta troncare SHA1 o MD5? Avrai più collisioni se non hai troncato, ma è ancora meglio che progettare il tuo. Si noti che è possibile codificare in base64 l'hash troncato, anziché utilizzare l'esadecimale. Per esempio.

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

È possibile troncare il meno (tra cui non a tutti) o tanto quanto si vuole, a patto che si capisce i compromessi.

EDIT: Dal momento che lei ha citato sicuro per le URL, è possibile utilizzare e urlsafe_b64decode, che utilizza - e _ piuttosto che + e /.

+0

Grazie. Esiste una funzione di hashish alfanumerica a bassa collisione, a meno di dire 16 caratteri, che non prevede il troncamento? Grazie. – ensnare

+3

Perché non vuoi troncare? –

+1

Si potrebbe anche voler rimuovere tutti i caratteri '=' aggiunti alla fine. Non riducono sostanzialmente il tasso di collisione, ma aggiungono due caratteri. Quindi forse qualcosa del tipo: 'base64.urlsafe_b64encode (hasher.digest() [0:10]). Replace ('=', '')' – speedplane

17

Il più piccolo hash incorporato Sono consapevole è md5

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

bassa di collisione e breve sono un po 'escludono a vicenda a causa della birthday paradox

Per rendere urlsafe è necessario utilizzare la funzione dalla Base64 modulo

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

Tuttavia, non dovrebbe esserci alcun problema nel memorizzare il digest md5 a 16 byte nel database in formato binario.

>>> 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==' 

È possibile scegliere la quote_plus o urlsafe_b64encode per il vostro URL, poi decodificare con la funzione corrispondente unquote_plus o urlsafe_b64decode prima di guardare in alto nel database.

+0

Grazie. Come posso rendere questo urlsafe? – ensnare

3

Di seguito è una soluzione che utilizza caratteri alfanumerici più alcuni caratteri di punteggiatura. Restituisce stringhe molto corte (circa 8 caratteri).

import binascii, struct 

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

'' hash (s) 'fornisce un risultato diverso per le piattaforme 32/64 bit –

+1

@gnibbler La domanda non elenca la coerenza tra le piattaforme come requisito. –

0

È possibile utilizzare qualcosa come la notazione di base 32. È più compatto della notazione decimale, senza distinzione tra maiuscole e minuscole e senza collisioni. Basta codificare un semplice vecchio numero di sequenza per generare un breve codice simile a un hash.

Se la chiave non è per il consumo umano, è possibile utilizzare la notazione di base 64, che è sensibile al maiuscolo e al minuscolo ma un po 'più compatta.

Vedere http://code.google.com/p/py-cupom/ per un esempio.