Hướng dẫn bignum python - hai con trăn

Tìm hiểu làm thế nào Python đại diện cho số nguyên của bất kỳ độ lớn nào bằng Bignum.

Hướng dẫn bignum python - hai con trăn

Ảnh của Crissy Jarvis trên unplash

Các lập trình viên mã hóa bằng các ngôn ngữ cấp thấp hơn như C/C ++ cần xem xét lượng bộ nhớ được sử dụng cho biểu diễn số nguyên. Họ phải xem xét các giá trị tối thiểu và tối đa của một biến để tránh các vấn đề tràn. Các lập trình viên này phải xem xét nếu NO___Trans___Pre___4 là đủ hoặc nếu NO___Trans___Pre___5 là cần thiết.

So với C/C ++, Python có lợi thế khi làm việc với các số nguyên. Trong Python, không có vấn đề tràn số nguyên; Do đó, các lập trình viên Python không cần phải lo lắng về loại biến cần sử dụng cho mỗi số nguyên. Python cho phép các lập trình viên thao túng số lượng lớn mà không phải lo lắng về việc mất chính xác. Giới hạn đại diện duy nhất cho các số nguyên trong Python là khi máy hết bộ nhớ miễn phí, nhưng đó là một giới hạn phần cứng.

Trong thực tế, điều này rất hữu ích để tính toán các giá trị lớn như giai thừa. Không cần sử dụng các thư viện bên ngoài, Python có thể tính toán độc lập với cường độ kết quả. Dưới đây là một ví dụ về chức năng tính toán giai thừa:

def factorial(n):
if n == 0 or n == 1:
return 1
return n * factorial(n-1)

Thực hiện hàm no___trans___pre___6 với đầu vào NO___TRANS___PRE___7 cho thấy độ lớn của giá trị số nguyên mà Python có thể đại diện.

>>> factorial(231)
1792233667382633521618843263044232513197622942259968207385215805123682159320161029848328112148883186161436034535802659466205111867109614573242316954383604389464524535467759401326264883566523043560811873179996072188155290081861628010250468430411854935707396605833540921031884571521279145124581094374547412403086564118143957940727734634769439112260383017302489106932716079961487372942529947238400000000000000000000000000000000000000000000000000000000

Lưu ý rằng có các thuật toán hiệu quả hơn để tính toán giai thừa; Ví dụ này phục vụ để minh họa cường độ của đầu ra.

Đại diện số nguyên

Trước khi tiến hành thêm trong cuộc thảo luận, lưu ý rằng bài viết này chỉ xem xét việc thực hiện CPython. Việc triển khai này là phiên bản mặc định và phiên bản Python được phân phối phổ biến nhất. Các triển khai khác nhau có thể đại diện cho các số nguyên khác nhau, nhưng cuộc thảo luận này sẽ chỉ bao gồm biểu diễn số nguyên trong cpython. Một lợi thế của việc sử dụng Cpython là toàn bộ CodeBase có sẵn công khai trên kho lưu trữ CPython trên GitHub.

Kể từ Python 3, tất cả các giá trị số nguyên được biểu diễn bằng cấu trúc bên dưới:

struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
};

Có thể mở rộng các macro và đại diện cho cấu trúc theo cách này:

struct {
sssize_t ob_refcnt;
struct _typeobject *ob_type;
ssize_t ob_size;
uint32_t ob_digit[1];
};

Hai yếu tố đầu tiên của cấu trúc ở trên không quan trọng cho cuộc thảo luận này. Phần tử NO___Trans___Pre___8 được sử dụng trong Trình thu thập rác Python, và NO___Trans___Pre___9 được sử dụng để nhận dạng loại, trong trường hợp này, đối tượng là một số nguyên.

Giá trị số nguyên được biểu thị bằng hai biến khác: no___trans___pre___10 và no___trans___pre___11. Python sử dụng mảng NO___TRANS___PRE___10 để lưu trữ từng chữ số của số một cách riêng biệt ở các vị trí chỉ mục khác nhau. Ngoài ra, biến OB_SIZE được sử dụng để lưu trữ hai giá trị. Nó lưu trữ độ dài của mảng no___trans___pre___10 và dấu hiệu của số nguyên (dương hoặc âm).

Trong phần lớn các hệ thống, no___trans___pre___10 là một mảng no___trans___pre___15, nhưng trong một số máy tính cũ hơn, NO___Trans___Pre___10 có thể là một mảng NO___Trans___Pre___17. Bài viết này sẽ chỉ xem xét trường hợp đầu tiên của các mảng NO___Trans___PRE___15.

Phương pháp biểu diễn các giá trị số nguyên với một chuỗi các chữ số với các chuỗi hoặc mảng được gọi là số học bignum. Thông thường, việc triển khai Bignum đại diện cho các giá trị trong nhị phân; Tuy nhiên, điều đó sẽ không hiệu quả không gian với các mảng no___trans___pre___15.

Cơ sở 2³⁰

Xem xét các hệ thống sử dụng các mảng NO___TRANS___PRE___15 để biểu diễn số nguyên, Python không thể sử dụng tất cả 32 bit để lưu trữ chữ số. Một lời giải thích ngắn gọn về giới hạn này là nhiều chức năng tích hợp trong Python đòi hỏi một số lượng cụ thể cho việc thể hiện số nguyên vì lý do thực tế và hiệu quả. Đối với một người đọc tò mò hơn, có những bình luận về giới hạn này trong kho lưu trữ CPYThon chính thức.

Vì Python chỉ có thể sử dụng 30 trong số 32 bit của mỗi phần tử, tất cả các số nguyên được chuyển đổi thành cơ sở 2³⁰. Do đó, tất cả các chữ số trong mảng có các giá trị trong khoảng từ 0 đến 1073741823 (2³⁰-1). Lưu ý rằng biến số no___trans___pre___11 lưu trữ độ dài của mảng với số trong cơ sở 2³⁰, không phải cơ sở 10.

Ngoài ra, biểu diễn mảng theo thứ tự ít người cuối. Nói cách khác, thứ tự có giá trị ít đáng kể nhất (giá trị chỉ số thấp hơn). Chẳng hạn, giả sử các số được lưu trữ trong cơ sở 10 thay vì cơ sở 2³⁰, số no___trans___pre___22 được biểu thị bằng mảng sẽ là: no___trans___pre___23.

Ví dụ: số NO___Trans___Pre___24 trong Python sẽ được chuyển đổi thành cơ sở 2 là. Vì chúng ta không có đủ ký tự để biểu diễn tất cả các chữ số của một số trong cơ sở 2³⁰, cho các mục đích minh họa, các chữ số của số trong cơ sở 2³⁰ sẽ được biểu diễn trong cơ sở 10. Do đó, số no___trans___pre___24 trong cơ sở 2 là không đại diện cho chữ số đầu tiên và vv với hai giá trị khác. Đó là bởi vì 462328538 × (2³⁰) ⁰ + 197050268 × (2³⁰)

Do đó, số NO___Trans___Pre___24 trong Python có cơ sở 2³⁰ có 3 chữ số: no___trans___pre___26 và nó sẽ được biểu diễn bằng Python theo cách sau:

Hướng dẫn bignum python - hai con trăn

Nếu số này là âm, biểu diễn Python sẽ có cùng một mảng nhưng no___trans___pre___11 sẽ là no___trans___pre___31.

Tối ưu hóa số nguyên chung

Quá trình chuyển đổi và đại diện cho các số nguyên sử dụng số học Bignum là tốn thời gian cho các hoạt động thời gian chạy. Vì lý do này, vì loại NO___Trans___Pre___4 là bất biến trong Python, Python tạo ra các biểu diễn cho tất cả các giá trị giữa NO___Trans___Pre___33 và NO___Trans___Pre___34 trước khi thực hiện chương trình. Trong quá trình thực hiện, Python tái sử dụng các đối tượng này bất cứ khi nào chúng được yêu cầu.

Một nhược điểm rõ ràng của việc sử dụng số học Bignum là về mặt sử dụng bộ nhớ. Bất kỳ giá trị số nguyên nào trong Python đều sử dụng ít nhất 28 byte bộ nhớ, gấp 7 đến 14 lần so với ngôn ngữ C sẽ yêu cầu tạo một biến của loại no___trans___pre___4.

Bignum bổ sung

Một lợi ích của việc sử dụng số học Bignum là sự đơn giản của việc thực hiện các hoạt động số học. Bài viết này sẽ chỉ bao gồm bổ sung, nhưng các hoạt động khác tuân theo cùng một khái niệm.

Ý tưởng đằng sau việc bổ sung Bignum, là thực hiện bổ sung giống như cách mọi người thêm các số vào cơ sở 10 bằng giấy và bút chì. Quá trình bắt đầu với chữ số ít có ý nghĩa nhất trước tiên và tiếp tục hướng tới các chữ số có ý nghĩa cao hơn. Mỗi chữ số từ mỗi số được thêm vào với nhau một cách riêng biệt và kết quả di chuyển qua giá trị mang theo theo chữ số có ý nghĩa cao hơn sau.

Hướng dẫn bignum python - hai con trăn

Số học Bignum theo cách tiếp cận này bằng cách sử dụng các mảng. Quá trình này là thêm từng giá trị từ cùng một chỉ mục từ mỗi mảng một cách riêng biệt và mang theo giá trị vượt quá vị trí thập phân cho chỉ mục sau. Thuật toán bắt đầu tại INDEX NO___TRANS___PRE___36 và lặp lại cho đến khi độ dài của mảng nhỏ nhất, thêm các chữ số bằng phương thức mang trong suốt.

Thuật toán bắt đầu bằng cách tạo một mảng trống mới để lưu trữ kết quả. Lưu ý rằng kết quả của tổng giữa hai giá trị có nhiều hơn một chữ số hơn số lớn nhất trong tổng. Ví dụ: tổng giữa NO___Trans___Pre___37 và NO___Trans___Pre___38 là NO___TRANS___PRE___39. Giá trị lớn nhất trong ví dụ này là no___trans___pre___38 và nó có 2 chữ số. Kết quả của việc bổ sung có 3 chữ số, một chữ số nhiều hơn số không ___trans___pre___38. Trong một số trường hợp, số chữ số của kết quả bằng với số chữ số của số lớn nhất. Trong trường hợp đó, thuật toán giảm kích thước của mảng để phù hợp với kết quả mà không dẫn đầu ___ trans ___ pre ___ 36's. Trường hợp duy nhất trong đó ô mảng cuối cùng (chỉ số cao nhất) có giá trị no___trans___pre___36is cho biểu diễn của số no___trans___pre___36.

Đối với mục đích minh họa, đây là một ví dụ về sự bổ sung giữa hai số được biểu thị bằng số học bignum. Các giá trị được thêm vào với nhau là no___trans___pre___24 và no___trans___pre___46.

Số

struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
};
4 sẽ được biểu diễn theo cách này:

Hướng dẫn bignum python - hai con trăn

và số NO___Trans___PRE___46 sẽ được biểu diễn theo cách này:

Hướng dẫn bignum python - hai con trăn

Thuật toán bắt đầu bằng cách tạo một mảng mới có kích thước 4 (dài hơn một ô so với mảng đại diện cho giá trị lớn nhất trong phép bổ sung).

Hướng dẫn bignum python - hai con trăn

Tiếp theo, thuật toán bắt đầu chỉ số quy trình mang theo chỉ mục.

Hướng dẫn bignum python - hai con trăn

Sau khi lặp qua tất cả các chỉ số lên đến độ dài mảng nhỏ nhất, thuật toán tính toán các giá trị sau:

Hướng dẫn bignum python - hai con trăn

Cuối cùng, chương trình giảm kích thước mảng xuống 1 để loại bỏ ô trống cuối cùng. Sau đó, cấu trúc hoàn chỉnh được tạo từ việc bổ sung được thể hiện theo cách này:

Hướng dẫn bignum python - hai con trăn

Thuật toán ban đầu được viết bằng C, nhưng đây là hàm Python mô phỏng quá trình này.

Hàm no___trans___pre___49 có trong hai danh sách python. Mỗi danh sách đại diện cho một trong các số nguyên được thêm vào trong hàm này. Các số đã được chuyển đổi thành cơ sở 2³⁰ và mỗi phần tử danh sách lưu trữ một chữ số của giá trị.

Tóm lại, Python sử dụng số học Bignum để đại diện cho số nguyên. So với các ngôn ngữ khác như Java và C/C ++, Python làm cho việc làm việc với số nguyên rất đơn giản. Trong khi các ngôn ngữ khác yêu cầu lập trình viên xác định biến kích thước để lưu trữ một số, Python tóm tắt nhu cầu này. Tuy nhiên, phương pháp này cũng có nhược điểm về việc sử dụng bộ nhớ. Mặc dù các ngôn ngữ như C sử dụng 2 hoặc 4 byte để biểu thị một biến của loại NO___Trans___Pre___4, Python yêu cầu ít nhất 28 byte. Đối với các tập lệnh đơn giản, việc sử dụng bộ nhớ thêm này không gây ra sự khác biệt; Tuy nhiên, đối với các chương trình nặng về dữ liệu, có thể rất thú vị khi sử dụng các ngôn ngữ khác như C.

Cảm ơn bạn rất nhiều vì đã đọc bài viết này!Chẳng mấy chốc tôi sẽ xuất bản thêm về Python và các chủ đề lập trình khác.