nén dữ liệu là một lĩnh vực lớn (bạn có thể start here), và không có câu trả lời dứt khoát cho câu hỏi của bạn. Nếu chúng tôi biết cách nén dữ liệu ở mức tối ưu, sẽ không có thuật toán nén mới mỗi năm;)
Điều đó được nói, ý tưởng hay là chọn một thuật toán tốt nhất trong bộ sưu tập và xác định nó trong tiêu đề. Trên thực tế, hầu hết các định dạng nén đều sử dụng loại lược đồ này.
Để trả lời câu hỏi của bạn:
gì các thuật toán:
- Sử dụng exising định dạng: PNG, GIF, hoặc 7zip đến tâm.
- Không nén: bitmap, rõ ràng, nhưng chỉ để đảm bảo chúng tôi đang nói về cùng một điều, bạn cần mã hóa 1 bit (và không phải 1 byte) cho mỗi phần tử. Bit truy cập không phải là dễ dàng có sẵn trong hầu hết các ngôn ngữ (đối với hầu hết các loại
bool
/boolean
được lưu trữ trên một byte đầy đủ). Bạn phải sử dụng các hoạt động bitwise, tính năng ngôn ngữ chuyên biệt chẳng hạn như bitfields
/bitsets
hoặc thư viện. Ví dụ: C++
, std::vector<bool>
triển khai thực hiện bitmap thực (đơn giản bool[]
thì không).
- Mã hóa Entropy: ý tưởng mã hóa hầu hết các cấu hình có thể xảy ra bằng cách sử dụng ít bộ nhớ hơn so với ít lưu trữ hơn.
- lưu trữ thưa thớt, những gì bạn gọi là chỉ mục, nhưng trái với những gì bạn ngụ ý, nó hữu ích khi có rất nhiều, nhưng cũng rất nhiều số không (chỉ phủ nhận kết quả!). Trường hợp khi chỉ có một
0
hoặc chỉ một 1
là đối xứng và phải được xử lý theo cùng một cách. Trường hợp xấu nhất cho lưu trữ dự phòng là có nhiều trường hợp như số không.
- mã hóa khối, nghĩa là bạn có thể lưu trữ
1111
trong ít hơn 4 bit nếu bạn biết đó là mẫu có khả năng. Nhưng điều này có nghĩa là một mô hình 4 bit khác (ít có khả năng) sẽ được lưu trữ bằng cách sử dụng hơn 4 bit, vì vậy hãy cẩn thận trong việc lựa chọn các mẫu của bạn. Có nhiều cách để thực hiện điều đó: các từ có kích thước cố định hoặc kích thước biến và mã hóa có thể là phụ thuộc vào dữ liệu tĩnh hoặc dữ liệu. Một số ví dụ là: RLE, Huffman, LZ biến thể (thuật toán dựa trên từ điển được sử dụng trong chương trình nén yêu thích của bạn)
- dựa trên ý tưởng tương tự về không gian lưu trữ trên mỗi biểu tượng/khối dựa trên xác suất của nó, nhưng mã hóa toàn bộ dữ liệu trong một lần, thay vì tách nó thành các khối, thường dẫn đến các lược đồ mã hóa tốt hơn.
- Vì dữ liệu của bạn là hai chiều, nên xử lý nó theo khối 2D thay vì khối 1D, ví dụ bạn có thể mã hóa các ô 8x8. JPEG để thực hiện điều đó.
- chuẩn bị dữ liệu: trước khi áp dụng entropy của bạn mã hóa thuật toán (một trong đó thực sự làm giảm kích thước dữ liệu), nó thường thú vị để áp dụng một song ánh (chức năng hai chiều, điều đó không làm giảm kích thước dữ liệu) để mã hóa kiến thức của bạn về mô hình dữ liệu.Trong trường hợp của bạn, bạn nói dữ liệu của bạn đại diện cho các sự kiện và những dữ liệu đó thường được nhóm đặc biệt. Có cách nào để thể hiện điều đó như một danh sách các sự kiện + cách họ truyền bá không? Hoặc bạn có thể sử dụng một số sơ đồ nén hình ảnh như Fourier transform hoặc một số loại wavelet transform. Hoặc bạn chỉ có thể sử dụng contour matrix (một khi sự thay đổi giá trị giữa hai tế bào, bằng không khi giá trị là hằng số), mà có thể làm tăng thưa thớt của ma trận của bạn (nó ở bạn dụ Một)
Chứng minh tối thiểu:
Không có thuật toán nén tối ưu chung, vì nén thực sự phụ thuộc vào phân bố xác suất dữ liệu của bạn. Thật vậy, bạn biết ma trận của bạn luôn giống nhau, bạn thậm chí không cần mã hóa nó. Bạn biết đó là một trong hai ma trận có thể, chỉ một chút là đủ để mã hóa cái nào.
Trong trường hợp tổng quát, của Shanon entropy ước tính số lượng tối thiểu lý thuyết của các bit cần thiết để mã hóa một thông điệp:
min = E(-log2(P(message)))
nơi E
là sự mong đợi trên tất cả các thư có thể và P
là hàm xác suất. Tuy nhiên, trong thực tế, bạn không biết P
, vì vậy tốt nhất bạn có thể làm tốt hơn thuật toán tốt nhất trước đó;)
Nói chung, bạn càng cố nén dữ liệu, chương trình càng đắt tiền , cả về tài nguyên thời gian chạy (chu kỳ CPU và bộ nhớ) và nỗ lực phát triển.
Một ví dụ
Chỉ cần đặt nó trong thực tế trên bạn dụ 1, để cung cấp cho bạn nguồn cảm hứng - ngay cả khi nó là một ý tưởng rất xấu để thiết kế từ 1 dụ, bởi vì nó hầu như không mang lại cho bạn đại diện xác suất!
dữ liệu ban đầu (tôi sẽ luôn luôn bỏ qua các tiêu đề kích thước, vì nó sẽ không thay đổi) - bitmap, 84 bit (25 người):
000000000000
000000011000
001100111100
001111111100
000000111100
000001111100
000000000000
Trong chương trình index
của bạn, bạn có đầu ra một danh sách position
của những người. Nếu bạn khái quát hóa, bạn có thể làm việc với danh sách position
+ pattern
. Ví dụ, pattern
có thể là số lượng liên tiếp, do đó bạn không cần xuất nhiều vị trí cho các khối của các vị trí đó. Chúng ta hãy đi xa hơn, và nói rằng chúng ta mã hóa mô hình 2D, được xác định bởi:
- một kích thước 3-bit (0-7)
một 1-bit hình dạng:
Sau đó, chúng ta hãy nói đi tương đối thay vì vị trí tuyệt đối, có nghĩa là mỗi position
mã hóa bao nhiêu bạn tiến từ vị trí trước đó. Ví dụ của bạn, tôi chọn vị trí 5 bit. Sau đây là cách giải mã hoạt động:
-- block #1
00001 position +1
absolute position from top-left, since this is the first block
001 pattern-size 1
0 pattern-shape is row
for size 1, square and row are the same
(which means my coding isn't optimal)
-- block #2
00100 position +4
relative to last position 1, this means position 5
010 pattern-size 2
1 pattern-shape is square
-- decoded output below
0100011000...
0000011000...
0000000000...
.............
Vì vậy, với chương trình này bạn có thể mã hóa dữ liệu của bạn sử dụng 45 bit:
10100 (position [0]+20 from top-left)
010 0 (size: 2, shape: row)
00110 (position [20]+6)
010 1 (size: 2, shape: square)
00100 (position +4)
100 1 (size: 4, shape: square)
01000 (position +8)
100 0 (size: 4, shape: row)
11011 (position +27)
001 0 (size 1, shape: row)
Ghi chú: Thông thường bạn muốn lưu trữ một tiêu đề để biết số các khối (5 trong trường hợp này), nhưng bạn có thể suy ra nó từ kích thước tải trọng của tệp/mạng. Cũng trong ví dụ này, chúng ta chỉ cần 3 kích thước mẫu (1,2,4) để chúng ta có thể lưu trữ kích thước trên hai bit và nâng cấp lên sức mạnh 2, làm cho tải trọng 40 bit, nhưng điều đó làm cho lược đồ quá chuyên biệt. Ngoài ra, cần phải có ít nhất một số vô nghĩa shape
(ở đây có hai: 000/0 và 000/1), trong trường hợp bạn không có đủ bit trong position
để mã hóa một "lỗ" lớn giữa các mẫu. Ví dụ ma trận này 20 x 3:
10000000000000000000
00000000000000000000
00000000000000000001
Có những người ở vị trí 0 và 59. Vì tôi không thể mã hóa 59 trên như một bước nhảy 5-bit, chúng ta phải nhảy hai lần, và chúng tôi sẽ mã hóa nó như:
00000 (+0) 001 0 (a single 1)
11111 (+31) 000 0 (nothing)
11100 (+28) 001 0 (a single 1)
Bạn không thể chứng minh điều đó là tối thiểu (bạn có thể ước tính nếu bạn có mô hình xác suất chính xác của dữ liệu). Thời gian tính toán cũng thường là một yếu tố quan trọng, cũng như sự phức tạp của mã/nỗ lực phát triển. Giao dịch tốt nhất tùy thuộc vào đơn đăng ký của bạn, vui lòng mô tả trường hợp sử dụng của bạn. – Antoine
Hãy thử các thói quen nén chuẩn như gzip và xem chúng mất bao nhiêu. –
@Antoine - trường hợp sử dụng được thêm vào, cho phép Bỏ qua bây giờ các vấn đề về độ phức tạp của mã & dev. nỗ lực – yossico