Jeśli masz zamiar wywołać funkcję odległość wielu razy podczas jednej z realizacji programu, można uzyskać pewną prędkość za pomocą wstępnie obliczonej tabeli bitów. Oto (kolejny) wersja funkcji odległości Hamminga:
# _nbits[k] is the number of 1s in the binary representation of k for 0 <= k < 256.
_nbits = np.array(
[0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3,
4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4,
4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2,
3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,
4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4,
5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3,
3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2,
3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5,
6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5,
5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6,
7, 7, 8], dtype=np.uint8)
def hamming_distance1(a, b):
c = np.bitwise_xor(a, b)
n = _nbits[c].sum()
return n
Poniżej a
i b
są listy Python o długości 32 podane w komentarzu na pytanie. divakar_hamming_distance()
i divakar_hamming_distance_v2()
są z odpowiedzi @ Divakara.
Oto czasy z @ funkcje Divakar za:
In [116]: %timeit divakar_hamming_distance(a, b)
The slowest run took 5.57 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 11.3 µs per loop
In [117]: %timeit divakar_hamming_distance_v2(a, b)
The slowest run took 5.35 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 10.3 µs per loop
hamming_distance1(a, b)
jest nieco szybciej:
In [118]: %timeit hamming_distance1(a, b)
The slowest run took 6.04 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 7.42 µs per loop
Na moim komputerze, inicjowanie _nbits
trwa około 11 mikrosekund, więc nie ma zaletę używając hamming_distance1
, jeśli wywołasz funkcję tylko raz. Jeśli nazwiesz to trzy lub więcej razy, osiągniesz zysk netto.
Jeżeli wejścia są już numpy tablice, wszystkie funkcje są znacznie szybsze:
In [119]: aa = np.array(a)
In [120]: bb = np.array(b)
In [121]: %timeit divakar_hamming_distance_v2(aa, bb)
The slowest run took 8.22 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 5.72 µs per loop
In [122]: %timeit hamming_distance1(aa, bb)
The slowest run took 12.67 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 2.77 µs per loop
Oczywiście, jeśli zawsze to zrobić bezpośrednio przed obliczyć odległość Hamminga, czas zrobić konwersję muszą być uwzględnione w ogólnym harmonogramie.Jednakże, jeśli napiszesz kod, który generuje a
i b
, aby móc korzystać z numpy wcześniej, możesz już mieć je jako tablice numpy przed obliczeniem odległości Hamminga.
(również eksperymenty trochę z 2-d tablicy precomputed odległości Hamminga pomiędzy wartościami 8-bitowych - tablica z kształtu (256, 256), - inicjowanie ale koszt jest wyższa i wzrost wydajności są małe.)
Czy nie będzie to "0 + 1", ponieważ 254 to wszystko 1s, z wyjątkiem jednobitowych, podczas gdy 255 to wszystko 1s? – Divakar
Prawdopodobnie po prostu weź standardowy przepis na popcount, wyślij go przez tablicę i zsumuj wynik. Możesz być w stanie uzyskać przyspieszenie, przeglądając bufor tablicy jako większy typ dtype. – user2357112
@Divakar To był literówka z mojego końca. Dobry chwyt. Zaktualizowano liczbę do 240 w przykładowych danych. –