2015-09-09 20 views
12

Trong python, tôi đã được cung cấp số nguyên 64 bit. Integer này được tạo ra bằng cách lấy một số nguyên khác nhau 8 bit và nghiền chúng lại với nhau thành một số nguyên khổng lồ 64 bit. Đó là công việc của tôi để tách chúng lại.Chuyển đổi số nguyên 64 bit thành 8 số nguyên 1 byte riêng biệt trong python

Ví dụ:

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) 

Vì vậy, những gì tôi muốn làm là lấy số nguồn của tôi 2592701575664680373 và trả về một mảng có độ dài 8, trong đó mỗi int trong mảng là ints liệt kê ở trên.

Tôi sẽ sử dụng struct, nhưng thành thật mà nói, đọc documentation đã không làm cho nó khá rõ ràng chính xác như thế nào tôi sẽ thực hiện điều đó.

+0

Bạn đã thử 'divmod()' chưa? – lenz

+0

Hãy đăng nhập, bạn đúng @PadraicCunningham. Tôi đã sử dụng một công cụ bẩn thỉu mà không hỗ trợ số lượng đủ lớn, và nó trucnated rằng phần cuối cùng với 0's. Bây giờ tôi đã chạy 'bin = '{0: 064b}'. Định dạng (nguồn)' tôi thấy rằng bạn là chính xác. – JHixson

+0

Thực tế 'n' là lẻ và không có 1 ở cuối đã làm tôi bối rối –

Trả lời

6

Trong Python 2.x, struct.pack trả về một chuỗi byte. Thật dễ dàng để chuyển đổi nó thành một mảng các số nguyên.

>>> 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] 

Mô-đun struct trong python được sử dụng để chuyển đổi từ đối tượng python sang chuỗi byte, thường được đóng gói theo quy tắc đóng gói cấu trúc C. struct.pack có một định dạng thông số (một chuỗi mô tả cách thức các byte của cấu trúc nên được đặt ra), và một số dữ liệu python, và gói nó thành một chuỗi byte. struct.unpack không đảo ngược, lấy một định dạng specifier và một chuỗi byte và trả về một tuple của unpacked dữ liệu một lần nữa trong định dạng của python đối tượng.

Trình định dạng thông số được sử dụng có hai phần. Ký tự chính xác định endianness (thứ tự byte) của chuỗi. Các ký tự sau chỉ định các loại của các trường của cấu trúc đang được đóng gói hoặc giải nén. Vì vậy, '>Q' có nghĩa là đóng gói dữ liệu đã cho là một số lớn nhất là unsigned long long. Để nhận các byte theo thứ tự ngược lại, bạn có thể sử dụng < để thay thế cho người ít tuổi.

Thao tác cuối cùng là danh sách hiểu lặp lại qua các ký tự của chuỗi byte và sử dụng hàm dựng sẵn ord để lấy đại diện số nguyên của ký tự đó.

Lưu ý cuối cùng: Python không thực sự có khái niệm về kích thước nguyên. Trong 2.x, có int được giới hạn ở 32 bit và long có kích thước không giới hạn. Trong 3.x hai người đó được hợp nhất thành một loại duy nhất. Vì vậy, mặc dù hoạt động này đảm bảo cung cấp cho các số nguyên chỉ chiếm một byte, lưu ý về python sẽ buộc các số nguyên kết quả ở lại theo cách đó nếu bạn sử dụng chúng trong các hoạt động khác.

+0

Cảm ơn bạn rất nhiều vì đã giải thích! Không chỉ điều này giải quyết vấn đề của tôi, nhưng tôi cảm thấy tự tin hơn nhiều về khả năng sử dụng mô-đun 'struct' của tôi từ bây giờ. – JHixson

+0

@ JHixson bạn có thể cảm ơn zstewart đã thêm toàn bộ lời giải thích sau khi tôi trả lời bằng mã. –

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] 

Nếu bạn đang sử dụng biểu diễn nhị phân của n thì đầu ra sẽ khác nhau:

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] 

Một số timings:

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 

Bạn cũng có thể chỉ divmod:

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

Mà gần như là 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 

Có rất ít lợi thế trong chút thay đổi với trăn, tôi sẽ nghiêng nhiều hơn để sử dụng bất cứ điều gì bạn và những người khác tìm thấy dễ đọc hơn.

8

Giải pháp

Giải pháp mà không cần chuyển đổi số thành một chuỗi:

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] 

Giải thích

Ở đây tôi sử dụng comprehensions danh sách, thực hiện một vòng lặp trong từng bước của 8 trên i. Vì vậy, i có các giá trị 0, 8, 16, 24, 32, 40, 48, 56. Mỗi lần, toán tử bithift >> tạm thời thay đổi số x xuống i bit. Điều này tương đương với chia cho 256^i.

Vì vậy, số lượng kết quả là:

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

By usig & 0xFF, tôi chọn 8 bit cuối cùng của con số này. Ví dụ:

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

Vì số 0 đứng đầu không quan trọng, bạn có số mong muốn.

Kết quả được chuyển đổi thành danh sách và được in theo thứ tự bình thường và được đảo ngược (như OP muốn nó).

Performance

Tôi đã so sánh thời gian của kết quả này với các giải pháp khác được đề xuất trong chủ đề này:

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 

Kết quả: giải pháp của tôi là không nhanh nhất! Hiện tại, việc sử dụng trực tiếp struct (như đề xuất bởi Mark Ransom) có vẻ là đoạn mã nhanh nhất.

+1

Bạn cũng có thể '[(n >> (i * 8)) & 0xFF cho i trong phạm vi (7, -1, -1)]' và quên đảo chiều –

+0

Vì lý do nào đó, tôi nhận được kết quả thời gian khác nhau. Tôi đang sử dụng iPython 2.0.0 trên Python 3.4.2, 32 bit. Trên máy tính Windows 64 bit. – jojonas

+0

Có một câu trả lời đơn giản, bạn không làm gì trong mã đầu tiên, bạn có một biểu thức máy phát điện bên trong danh sách –

2

Dưới đây là một phiên bản sử dụng struct:

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

Các bytes được trả về theo thứ tự ngược mà bạn thấy trong câu hỏi của bạn.

Dưới đây là số liệu thống kê hiệu suất:

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

Trên máy tính của tôi, đây là nhanh hơn so với giải pháp byte-chuyển ba lần.

+1

Bạn có thể kiểm soát thứ tự các byte được trả về bằng cách chỉ định thứ tự byte cho số nguyên 64 bit (ví dụ: big-endian, với '>'). – Blckknght

Các vấn đề liên quan