2016-10-31 2 views
3

Ich habe einen Datensatz mit mehreren Zeichenfolgen. Ich möchte jeden dieser Strings zu einem Float "zufällig" im Bereich [0:1] verteilt zuordnen. Beispiele:Wie Hashzeichenfolgen in einem Float in [0: 1]?

>>> myfunction(string_1) 
0.26756754 
>>> myfunction(string_2) 
0.86764534 

random nicht mein Bedürfnis erfüllen, weil es nicht eine beliebige Zeichenfolge als Eingabe/deterministischen Parameter nimmt. Ich suche etwas mehr wie eine Hash-Funktion.

Antwort

2

Man könnte so etwas wie dies versuchen:

import random 
random.seed(hash(your_string)) 
random.random() 
+0

Wenn ich gut verstehen, random.seed() nur den Generator initialisiert aber nicht wieder etwas? – Vincent

+0

@Vincent Das ist richtig. Aber es beeinflusst die Reihenfolge der Werte, die von 'random.random()' zurückgegeben werden. Die erste Zufallszahl hängt also direkt vom Seed ab. – Barmar

+1

Diese Antwort ist interessant, aber es gibt zwei Einschränkungen: Erstens kann es abhängig von der Python-Installation unterschiedliche Ergebnisse zurückgeben, da 'hash' möglicherweise andere Ergebnisse über Python-Installationen zurückgibt. Zweitens ändert sich der gemeinsame Random Seed. Wenn also andere Teile des Programms auf Zufallszahlen beruhen, kann dies negative Auswirkungen haben. Ich würde entweder meine Antwort empfehlen oder zumindest 'random.seed (hash (your_string))' mit 'rnd = random ersetzen.Random (your_string) ', dann' rnd.random() '. – MiniQuark

3

Eine schnelle und tragbare Lösung:

from zlib import crc32 

def bytes_to_float(b): 
    return float(crc32(b) & 0xffffffff)/2**32 

Dieser wandelt ein Byte-String in einen Schwimmer zwischen 0,0 und 1,0. Wenn Sie einen Unicode-String verwenden (zB in Python 3), dann müssen Sie es kodieren:

def str_to_float(s, encoding="utf-8"): 
    return bytes_to_float(s.encode(encoding)) 

Beispiel

>>> str_to_float(u"café") 
0.5963937465567142 

Dies sollte das gleiche Ergebnis auf jeder Maschine geben und jede Version von Python (getestet auf Python 2.7 und 3.5).

Hinweis: Die & 0xffffffff ist hier, um ein vorzeichenloses int-Ergebnis zu garantieren. Es wird benötigt, da abhängig von der Python-Version crc32(b) eine signed oder unsigned int zurückgeben kann.

bearbeiten

Wenn Sie etwas mehr "zufällig" als ein CRC32, können Sie stattdessen, wie SHA256 eine Hash-Funktion verwenden:

from struct import unpack 
from hashlib import sha256 

def bytes_to_float(b): 
    return float(unpack('L', sha256(b).digest()[:8])[0])/2**64 

Leistungstest

  String length 
Function 7  70  700  7000 
b2f_crc32 0.34 0.38 0.87 5.59  
b2f_md5  0.96 1.08 2.11 11.13 
b2f_sha1 0.99 1.07 1.76 8.37  
b2f_sha256 1.11 1.20 2.60 16.44 
b2f_rnd  6.59 6.55 6.59 6.60  

Grundsätzlich ist die CRC32-Lösung mit Abstand die schnellste für kurze Strings (18x schneller als @ user3030010's random = RND-Lösung). Es ist ungefähr 3x schneller als SHA256, unabhängig von der Saitenlänge. SHA256 ist langsamer als MD5, was langsamer als SHA1 ist (außer für sehr kurze Strings). Wie auch immer, die RND-Option hängt nicht von der String-Länge ab. Wenn die Strings sehr lang sind, kann es die schnellste Option sein (siehe meine Kommentare zu @ user3030010): auf meinem Computer schlägt es SHA256 für Strings länger als 2500 Zeichen, und es schlägt CRC32 für Strings länger als 8000 Zeichen.

Hier ist der Code, mit timeit.timeit():

from __future__ import print_function 
[...] # define b2f_crc32, b2f_md5 and so on. 
for func in ("b2f_crc32", "b2f_md5", "b2f_sha1", "b2f_sha256", "b2f_rnd"): 
    for length in (7, 70, 700, 7000): 
    t = timeit('b2f(b"%s")'%(b"x"*length), 
       'from __main__ import %s as b2f' % func) 
    print("%.2f"%t, end="\t") 
    print()