Một lược đồ tốt để biểu diễn các số nguyên từ 0 đến vô cùng, giả sử bạn có lưu trữ nhị phân tuyến tính vô hạn?


10

Tôi muốn một lược đồ biểu diễn các số nguyên bắt đầu bằng 0, không có bất kỳ giới hạn nào (giả sử truy cập vào bộ lưu trữ tuyến tính vô hạn).

Đây là một lược đồ có thể biểu thị các số từ 0 đến 255:

Sử dụng byte đầu tiên của bộ lưu trữ (địa chỉ 0) để lưu trữ số nguyên.

Bây giờ, giả sử tôi muốn biểu diễn các số lớn hơn 255. Tất nhiên, tôi có thể sử dụng nhiều hơn 1 byte để biểu diễn số nguyên, nhưng miễn là nó là một số cố định, cuối cùng sẽ có một số nguyên lớn đến mức không thể biểu thị bằng số nguyên lược đồ ban đầu.

Đây là một lược đồ khác có thể thực hiện nhiệm vụ, nhưng có lẽ nó không hiệu quả.

Chỉ cần sử dụng một số loại byte "kết thúc số" duy nhất và sử dụng tất cả các byte trước đó để thể hiện số. Rõ ràng, byte "kết thúc số" này không thể được sử dụng ở bất cứ đâu trong biểu diễn số, nhưng điều này có thể đạt được bằng cách sử dụng hệ thống đánh số cơ sở 255 (thay vì cơ sở 256).

Tuy nhiên, điều đó chậm và có thể không hiệu quả. Tôi muốn có một cái tốt hơn hoạt động tốt hơn với các giá trị thấp và tỷ lệ tốt.

Về cơ bản, đó là một hệ thống UUID. Tôi muốn xem liệu có thể tạo ra một hệ thống UUID hoạt động nhanh, theo lý thuyết có thể mở rộng quy mô để sử dụng trong nhiều năm, hàng ngàn năm, hàng triệu năm mà không phải thiết kế lại.


1
Bạn có muốn một cái gì đó có thể mở rộng quy mô vô tận (như trong phần mở đầu của bạn), hoặc trong hàng triệu năm (như khi bạn đóng cửa) không? Hai yêu cầu là (rõ ràng) hoàn toàn khác nhau. Twos bổ sung trên máy 64 bit sẽ có quy mô hàng triệu năm.
dùng16764

1
@ user16764, ý bạn là một biến số nguyên 64 bit? Điều đó chắc chắn sẽ không hiệu quả: nếu 6 triệu người đang tiêu thụ 1 triệu UUID mỗi giây, nó sẽ chỉ kéo dài hơn một tháng.
Dmitri Shuralyov

1
Và nó sẽ mất bao lâu trên một máy 128 bit?
dùng16764

2
Các ý tưởng trong RFC 2550 , cung cấp một đại diện ASCII theo thứ tự từ điển cho các số nguyên dương lớn tùy ý, có thể thích ứng với điều này. Cuối cùng, nó phân chia thành một phân đoạn đơn nhất mã hóa độ dài của phân đoạn cơ sở 26, mã hóa độ dài của phân khúc cơ sở 10 - hai cơ sở sau liên quan nhiều hơn đến đại diện ASCII hơn bất kỳ điều gì cơ bản cho sơ đồ.
Random832

1
Giả sử bạn tạo ra các số 128 bit một cách tuần tự: nếu chúng ta vượt quá khả năng tính toán của tất cả các máy tính bằng cách cho mỗi con người một máy tính petaflop, thì sẽ mất 9 triệu năm trước khi các số này hết. Mặt khác, mỗi người sẽ tạo ngẫu nhiên 600 triệu số 128 bit, có 50% khả năng họ tạo 1 bản sao. Có đủ tốt cho bạn không? ( en.wikipedia.org/wiki/Universally_unique_identifier ) Nếu không, sử dụng 256 bit nhân cả hai số liệu này với 2 ^ 128 = 3,4 * 10 ^ 38, nhiều hơn bình phương tuổi của vũ trụ tính bằng giây.
Alex ten Brink

Câu trả lời:


13

Một cách tiếp cận tôi đã sử dụng: đếm số lượng 1 bit hàng đầu, giả sử n. Kích thước của số đó là 2 ^ n byte (bao gồm 1 bit hàng đầu). Lấy các bit sau 0 bit đầu tiên làm số nguyên và thêm giá trị tối đa (cộng một) có thể được biểu thị bằng một số bằng cách sử dụng mã hóa này trong 2 ^ (n-1) byte.

Như vậy

                  0 = 0b00000000
                   ...
                127 = 0b01111111
                128 = 0b1000000000000000
                   ...
              16511 = 0b1011111111111111
              16512 = 0b11000000000000000000000000000000
                   ...
          536887423 = 0b11011111111111111111111111111111
          536887424 = 0b1110000000000000000000000000000000000000000000000000000000000000
                   ...
1152921505143734399 = 0b1110111111111111111111111111111111111111111111111111111111111111
1152921505143734400 = 0b111100000000000000000000000000000000000000000000 ...

Lược đồ này cho phép mọi giá trị không âm được biểu diễn theo đúng một cách.

(Tương đương, đã sử dụng số lượng bit 0 hàng đầu.)


1
Thật khó cho tôi để tìm ra câu trả lời nào được đánh dấu là được chấp nhận, bởi vì tôi nghĩ nhiều trong số chúng rất nhiều thông tin và tốt. Nhưng tôi nghĩ rằng đây là câu hỏi phù hợp nhất cho câu hỏi tôi đã hỏi (có thể không phải là câu hỏi cơ bản mà tôi có trong đầu, khó diễn đạt hơn).
Dmitri Shuralyov

2
Tôi đã viết một bài viết chuyên sâu hơn với việc xem xét thực hiện và thiết kế mẫu.
truy cứu

10

Có rất nhiều lý thuyết dựa trên những gì bạn đang cố gắng làm. Hãy xem trang wiki về các mã phổ quát - có một danh sách khá đầy đủ các phương thức mã hóa số nguyên (một số trong đó thực sự đang được sử dụng trong thực tế).

Trong nén dữ liệu, mã phổ quát cho số nguyên là mã tiền tố ánh xạ các số nguyên dương vào từ mã nhị phân

Hoặc bạn chỉ có thể sử dụng 8 byte đầu tiên để lưu trữ độ dài của số trong một số đơn vị (rất có thể là byte) và sau đó đặt các byte dữ liệu. Nó sẽ rất dễ thực hiện, nhưng không hiệu quả đối với số lượng nhỏ. Và bạn sẽ có thể mã số nguyên đủ lâu để điền vào tất cả các ổ dữ liệu có sẵn cho nhân loại :)


Cảm ơn vì điều đó, điều đó rất thú vị. Tôi muốn đánh dấu đây là câu trả lời được chấp nhận, nhưng nó đã chiếm vị trí thứ 2. Đây là một câu trả lời rất tốt từ quan điểm lý thuyết, IMO.
Dmitri Shuralyov

4

Làm thế nào về việc để số lượng 1 hàng đầu cộng với 0 đầu tiên là kích thước (sizeSize) của kích thước số (numSize) theo bit. NumSize là một số nhị phân cung cấp kích thước của biểu diễn số theo byte bao gồm các bit kích thước. Các bit còn lại là số (num) trong nhị phân. Đối với sơ đồ nguyên dương, đây là một số số ví dụ mẫu:

Number              sizeSize  numSize    num
63:                 0 (1)     1 (1)      111111
1048575:            10 (2)    11 (3)     1111 11111111 11111111
1125899906842623:   110 (3)   111 (7)    11 11111111 11111111 11111111 11111111 11111111 11111111
5.19.. e+33:        1110 (4)  1111 (15)  11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111

4

Làm thế nào về điều đó: Một byte cho chiều dài, sau đó n byte cho số (byte đầu tiên ít quan trọng nhất). Lặp lại độ dài + số miễn là độ dài trước đó là 255.

Điều này cho phép số lượng lớn tùy ý, nhưng vẫn dễ xử lý và không lãng phí quá nhiều bộ nhớ.


fNek: Không có giới hạn trên. Ví dụ: nếu bạn cần 513 byte cho số đó, chuỗi byte là [255, b0, ..., b255,255, b256, ..., b511,2, b512, b513]
user281377

Lấy làm tiếc. Nên học cách đọc kỹ hơn.
fNek

3

Tại sao không chỉ sử dụng 7 bit trong mỗi byte và sử dụng bit thứ 8 để cho biết liệu có một byte khác để theo dõi không? Vì vậy, 1-127 sẽ ở một byte, 128 sẽ được biểu thị bằng 0x80 0x01, v.v.


1
Lược đồ này chỉ mã hóa 128 giá trị trong mỗi 8 bit, thực sự ít hiệu quả về không gian hơn so với sơ đồ mã hóa thứ hai do người hỏi đề xuất, trong đó 255 giá trị được mã hóa trong mỗi 8 bit. Cả hai lược đồ đều bị ảnh hưởng bởi thực tế là bạn cần đọc toàn bộ số để tìm hiểu xem bạn cần lưu trữ bao nhiêu.
Đánh dấu gian hàng

3
Vì vậy, bạn cần phải quét số hai lần để tạo một bản sao của nó, vậy thì sao? Nếu tôi có thể đợi một số lượng lớn vô hạn, tôi có thể đợi nó hai lần.
Russell Borogove

Mặc dù tôi không chỉ định nó rất cẩn thận, tôi đang tìm một giải pháp thực hiện hiệu quả nhất có thể (thay vì một giải pháp đơn giản phù hợp với yêu cầu; tôi đã mô tả một câu trả lời không hiệu quả trong câu hỏi của tôi).
Dmitri Shuralyov

3

Các hệ thống UUID dựa trên sức mạnh tính toán hữu hạn (nhưng lớn) trong một vũ trụ hữu hạn (nhưng lớn). Số lượng UUID lớn ngay cả khi so sánh với những thứ lớn một cách vô lý như số lượng hạt trong vũ trụ. Tuy nhiên, số lượng UUID, với bất kỳ số bit cố định nào, là nhỏ, so với vô cùng.

Vấn đề với việc sử dụng 0xFFFF để thể hiện cờ kết thúc số của bạn là nó làm cho mã hóa số của bạn kém hiệu quả hơn khi số lượng lớn. Tuy nhiên, có vẻ như chương trình UUID của bạn làm cho vấn đề này trở nên tồi tệ hơn. Thay vì một trong số 256 byte bị bỏ qua, giờ đây bạn đã lãng phí toàn bộ không gian UUID. Hiệu quả của tính toán / nhận dạng (thay vì không gian) phụ thuộc rất nhiều vào máy tính lý thuyết của bạn (mà tôi cho rằng bạn có nếu bạn đang nói về vô cực). Đối với TM có băng từ và bộ điều khiển trạng thái hữu hạn, mọi sơ đồ UUID đều không thể mở rộng hiệu quả (về cơ bản, bổ đề bơm giúp bạn không di chuyển vượt quá điểm đánh dấu cuối có độ dài bit cố định một cách hiệu quả). Nếu bạn không giả sử bộ điều khiển Trạng thái hữu hạn, điều này có thể không áp dụng, nhưng bạn phải suy nghĩ về việc các bit đi đâu trong quá trình giải mã / nhận dạng.

Nếu bạn chỉ muốn hiệu quả tốt hơn 1 trong số 256 byte, bạn có thể sử dụng bất kỳ độ dài bit nào của 1 giây bạn sẽ sử dụng cho sơ đồ UUID của mình. Đó là 1 trong 2 ^ chiều dài không hiệu quả.

Lưu ý rằng có các chương trình mã hóa khác, mặc dù. Mã hóa byte với các dấu phân cách chỉ là dễ thực hiện nhất.


2

Tôi khuyên bạn nên có một mảng byte (hoặc int hoặc long) và trường độ dài cho biết số đó dài bao nhiêu.

Đây gần như là cách tiếp cận được sử dụng bởi BigInteger của Java . Không gian địa chỉ có thể từ đây là rất lớn - đủ dễ dàng để cung cấp một UUID khác nhau cho mỗi nguyên tử riêng lẻ trong vũ trụ :-)

Trừ khi bạn có một lý do rất chính đáng để làm khác, tôi khuyên bạn chỉ nên sử dụng BigInteger trực tiếp (hoặc tương đương với các ngôn ngữ khác). Không có nhu cầu đặc biệt để phát minh lại bánh xe số lớn ....


Bạn không thể mã hóa độ dài của mảng khi số lượng trường có thể là vô hạn.
Slawek

Tôi đồng ý rằng sử dụng một giải pháp hiện có (đặc biệt là một giải pháp đã được kiểm tra chuyên nghiệp) cho một vấn đề nhất định, khi có thể, được ưu tiên. Cảm ơn.
Dmitri Shuralyov

@Slawek: đúng, nhưng trong trường hợp sử dụng, OP đang mô tả (ví dụ UUID), BigInteger thực sự là vô hạn. Bạn không thể mã hóa thông tin vô hạn trong bất kỳ máy tính nào có bộ nhớ có kích thước hữu hạn, vì vậy BigInteger cũng tốt như mọi thứ khác mà bạn có khả năng đạt được.
mikera

2

Trước hết, cảm ơn tất cả những người đã đóng góp câu trả lời tuyệt vời cho câu hỏi tương đối mơ hồ và trừu tượng của tôi.

Tôi muốn đóng góp một câu trả lời tiềm năng mà tôi đã nghĩ đến sau khi nghĩ về những câu trả lời khác. Đây không phải là câu trả lời trực tiếp cho câu hỏi được hỏi, nhưng nó có liên quan.

Như một số người đã chỉ ra, sử dụng số nguyên có kích thước 64/128/256 bit đã cung cấp cho bạn một không gian rất lớn cho UUID. Rõ ràng nó không phải là vô hạn, nhưng ...

Có lẽ nên sử dụng một int có kích thước cố định (giả sử, 64 bit để bắt đầu) cho đến khi 64 bit là không đủ (hoặc gần với nó). Sau đó, giả sử bạn có quyền truy cập như vậy vào tất cả các phiên bản trước của UUID, chỉ cần nâng cấp tất cả chúng lên int 128 bit và lấy đó làm kích thước số nguyên cố định của bạn.

Nếu hệ thống cho phép tạm dừng / gián đoạn dịch vụ như vậy và do các hoạt động "xây dựng lại" như vậy xảy ra khá ít khi xảy ra, có lẽ các lợi ích (một hệ thống rất đơn giản, nhanh chóng, dễ thực hiện) sẽ khắc phục được các nhược điểm (phải xây dựng lại tất cả các số nguyên được phân bổ trước đó đến một kích thước bit số nguyên mới).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.