2015-09-09 20 views
12

W pythonie otrzymałem 64-bitową liczbę całkowitą. Ta liczba całkowita została utworzona przez pobranie kilku różnych 8-bitowych liczb całkowitych i połączenie ich w jedną gigantyczną 64-bitową liczbę całkowitą. Moim zadaniem jest znów je rozdzielić.Konwertowanie 64-bitowej liczby całkowitej na 8 oddzielnych 1-bajtowych liczb całkowitych w pythonie

Na przykład:

Source number: 2592701575664680400 
Binary (64 bits): 0010001111111011001000000101100010101010000101101011111000000000 
int 1: 00100011 (35) 
int 2: 11111011 (251) 
int 3: 00100000 (32) 
int 4: 01011000 (88) 
int 5: 10101010 (170) 
int 6: 00010110 (22) 
int 7: 10111110 (190) 
int 8: 00000000 (0) 

Więc co chciałbym zrobić, to wziąć mój numer źródłowy 2592701575664680373 i zwraca tablicę o długości 8, gdzie każdy int w tablicy są ints wymienione powyżej.

Chciałem użyć struct, ale szczerze mówiąc, przeczytanie documentation nie uczyniło całkiem jasnego, jak to osiągnąć.

+0

Czy próbowałeś 'divmod()'? – lenz

+0

Dang it, masz rację @PadraicCunningham. Używałem szybkiego, brudnego narzędzia, które nie obsługiwało wystarczająco dużych liczb, a to zaowocowało ostatnią częścią z zerami. Teraz, gdy uruchomiłem 'bin = '{0: 064b}.' Format (source)' widzę, że masz rację. – JHixson

+0

Fakt, że 'n' jest nieparzysty i nie było 1, na końcu nie myliłem się –

Odpowiedz

6

W języku Python 2.x, struct.pack zwraca ciąg bajtów. Łatwo to przekonwertować na tablicę liczb całkowitych.

>>> bytestr = struct.pack('>Q', 2592701575664680400) 
>>> bytestr 
'#\xfb X\xaa\x16\xbd\xd0' 
>>> [ord(b) for b in bytestr] 
[35, 251, 32, 88, 170, 22, 189, 208] 

modułu w pytona struct jest wykorzystywany do konwersji od obiektu Pythonowi łańcuchów bajtów, zwykle pakowane zgodnie z zasadami struktura opakowania C. struct.pack przyjmuje specyfikator formatu (ciąg znaków, który opisuje sposób rozmieszczenia bajtów struktury) oraz niektóre dane z Pythona i pakuje je w ciąg bajtów. struct.unpack wykonuje odwrotność, przyjmując specyfikator formatu i ciąg bajtów i ponownie zwracając krotkę rozpakowanych danych w formacie obiektów Pythona.

Używany specyfikator formatu składa się z dwóch części. Znak wiodący określa endianness (kolejność bajtów) ciągu. Następujące znaki określają typy pól w strukturze, która jest pakowana lub rozpakowywana. Tak więc '>Q' oznacza spakować dane jako big-endian unsigned long long. Aby uzyskać bajty w odwrotnej kolejności, można użyć wartości < dla little-endian.

Ostatnią operacją jest zrozumienie listy, które polega na iteracji po znakach ciągu bajtowego i wykorzystaniu wbudowanej funkcji ord, aby uzyskać całkowitą reprezentację tego znaku.

Ostatnia uwaga: Python w rzeczywistości nie ma pojęcia liczby całkowitej. W wersji 2.x istnieje int, który jest ograniczony do 32 bitów i long, który ma nieograniczony rozmiar. W 3.x te dwa zostały zunifikowane w jeden typ. Tak więc nawet jeśli ta operacja gwarantuje podanie liczb całkowitych, które zajmują tylko jeden bajt, zwrócenie uwagi na pytona zmusi otrzymane liczby całkowite do pozostania w ten sposób, jeśli użyjesz ich w innych operacjach.

+0

Dziękuję bardzo za wyjaśnienie! Nie tylko rozwiązuje to mój problem, ale czuję się o wiele pewniej w mojej zdolności do używania modułu 'struct' od teraz. – JHixson

+0

@JHixson możesz podziękować zstewart, który dodał całe wyjaśnienie po tym, jak odpowiedziałem z kodem. –

2
bn = "0010001111111011001000000101100010101010000101101011111000000000" 

print([int(bn[i:i+8], 2) for i in range(0,len(bn), 8)]) 
[35, 251, 32, 88, 170, 22, 190, 0] 

Jeśli używasz binarnej reprezentacji n to wynik byłby inny:

n = 2592701575664680373 
bn = bin(n) 

print([int(bn[i:i+8], 2) for i in range(0,len(bn), 8)]) 
[35, 251, 32, 88, 170, 22, 189, 181] 

Niektóre czasy:

In [16]: %%timeit             
numbers = list((n >> i) & 0xFF for i in range(0,64,8)) 
list(reversed(numbers)) 
    ....: 
100000 loops, best of 3: 2.97 µs per loop 

In [17]: timeit [(n >> (i * 8)) & 0xFF for i in range(7, -1, -1)] 
1000000 loops, best of 3: 1.73 µs per loop 

In [18]: %%timeit             
bn = bin(n) 
[int(bn[i:i+8], 2) for i in range(0,len(bn), 8)] 
    ....: 
100000 loops, best of 3: 3.96 µs per loop 

Można też po prostu divmod:

out = [] 
for _ in range(8): 
    n, i = divmod(n, 256) 
    out.append(i) 
out = out[::-1] 

Który jest prawie jak e fficient:

In [31]: %%timeit 
    ....: n = 2592701575664680411 
    ....: out = [] 
    ....: for _ in range(8): 
    ....:  n, i = divmod(n, 1 << 8) 
    ....:  out.append(i) 
    ....: out[::-1] 
    ....: 
100000 loops, best of 3: 2.35 µs per loop 

Jest bardzo mało zaletą bitu przeniesienia z pytona, byłbym bardziej skłonny do korzystania cokolwiek i inni znajdą bardziej czytelny.

8

Rozwiązanie

Rozwiązanie bez konwersji liczby na ciąg znaków:

x = 0b0010001111111011001000000101100010101010000101101011111000000000 

numbers = list((x >> i) & 0xFF for i in range(0,64,8)) 
print(numbers)     # [0, 190, 22, 170, 88, 32, 251, 35] 
print(list(reversed(numbers))) # [35, 251, 32, 88, 170, 22, 190, 0] 

Wyjaśnienie

Tutaj użyłem listowych, tworząc pętlę w odstępach 8 nad i. Tak więc i przyjmuje wartości 0, 8, 16, 24, 32, 40, 48, 56. Za każdym razem operator bitshift >> tymczasowo przesuwa liczbę x w dół o i bitów. Jest to równoznaczne z dzieleniem przez 256^i.

Tak uzyskana liczba jest:

i = 0: 0010001111111011001000000101100010101010000101101011111000000000 
i = 8:   00100011111110110010000001011000101010100001011010111110 
i = 16:     001000111111101100100000010110001010101000010110 
i = 24:       0010001111111011001000000101100010101010 
i = 32:         00100011111110110010000001011000 
i = 40:           001000111111101100100000 
i = 48:             0010001111111011 
i = 56:               00100011 

Przez usig & 0xFF, wybiorę ostatnie 8 bitów tego numeru. Przykład:

x >> 48:   001000111111101100100000 
0xff:        11111111 
(x >> 48) & 0xff: 000000000000000000100000 

Ponieważ początkowe zera nie mają znaczenia, masz żądaną liczbę.

Wynik zostanie przekonwertowany na listę i wydrukowany w normalnej i odwrotnej kolejności (tak, jak chciał tego OP).

Wydajność

Porównałem taktowanie tego wyniku do innych rozwiązań proponowanych w tym wątku:

In: timeit list(reversed([(x >> i) & 0xFF for i in range(0,64,8)])) 
100000 loops, best of 3: 13.9 µs per loop 

In: timeit [(x >> (i * 8)) & 0xFF for i in range(7, -1, -1)] 
100000 loops, best of 3: 11.1 µs per loop 

In: timeit [(x >> i) & 0xFF for i in range(63,-1,-8)] 
100000 loops, best of 3: 10.2 µs per loop 

In: timeit reversed(struct.unpack('8B', struct.pack('Q', x))) 
100000 loops, best of 3: 3.22 µs per loop 

In: timeit reversed(struct.pack('Q', x)) 
100000 loops, best of 3: 2.07 µs per loop 

Rezultat: moje rozwiązanie jest nie najszybszy! Obecnie użycie bezpośrednio przy pomocy struct (zgodnie z propozycją Marka Ransoma) wydaje się być najszybszym fragmentem.

+1

Możesz również "[(n >> (i * 8)) i 0xFF dla i w zakresie (7, -1, -1)]' i zapomnieć cofania –

+0

Z jakiegoś powodu otrzymuję różne wyniki czasowe. Używam iPython 2.0.0 na Python 3.4.2, 32-bitowy. Na 64-bitowym komputerze z systemem Windows. – jojonas

+0

Istnieje prosta odpowiedź, nic nie robisz w pierwszym kodzie, masz wyrażenie generatora na liście –

2

Oto wersja użyciu struct:

import struct 
n = 2592701575664680400 
bytes = struct.unpack('8B', struct.pack('Q', n)) 

bytes są zwracane w odwrotnej kolejności, że pokazał w swoim pytaniu.

Oto statystyki wydajności:

python -m timeit -s "import struct" "struct.unpack('8B', struct.pack('Q', 2592701575664680400))" 
1000000 loops, best of 3: 0.33 usec per loop 

Na moim komputerze, to jest trzy razy szybciej niż roztwór bajtów przesunięcia.

+1

Prawdopodobnie możesz kontrolować kolejność zwracanych bajtów, określając kolejność bajtów dla 64-bitowej liczby całkowitej (np. Big-endian, z '>'). – Blckknght