Tôi đồng ý với keraba rằng bạn cần sử dụng thứ gì đó như mã Huffman hoặc có thể là thuật toán Lempel-Ziv-Welch. Vấn đề với việc đóng gói bit theo cách bạn đang nói đến là bạn có hai lựa chọn:
- Chọn một hằng số n sao cho số nguyên lớn nhất có thể được biểu diễn.
- Cho phép n thay đổi từ giá trị sang giá trị.
Tùy chọn đầu tiên tương đối dễ thực hiện, nhưng thực sự sẽ lãng phí nhiều không gian trừ khi tất cả các số nguyên là khá nhỏ.
Tùy chọn thứ hai có những bất lợi lớn mà bạn phải truyền đạt những thay đổi trong n bằng cách nào đó trong bitstream đầu ra. Ví dụ, mỗi giá trị sẽ phải có độ dài liên kết với nó. Điều này có nghĩa là bạn đang lưu trữ hai số nguyên (mặc dù số nguyên nhỏ hơn) cho mỗi giá trị đầu vào. Có một cơ hội tốt để bạn tăng kích thước tệp bằng phương thức này.
Lợi thế của Huffman hoặc LZW là chúng tạo các danh mục theo cách sao cho độ dài của các mã có thể xuất phát từ bitstream đầu ra mà không thực sự lưu trữ độ dài. Những kỹ thuật này cho phép bạn nhận được rất gần với giới hạn Shannon.
tôi quyết định từ bỏ ý tưởng ban đầu của bạn (không đổi n, loại bỏ bit không sử dụng và đóng gói) thử cho vui và đây là việc thực hiện ngây thơ tôi đến với:
#include <sys/types.h>
#include <stdio.h>
int pack(int64_t* input, int nin, void* output, int n)
{
int64_t inmask = 0;
unsigned char* pout = (unsigned char*)output;
int obit = 0;
int nout = 0;
*pout = 0;
for(int i=0; i<nin; i++)
{
inmask = (int64_t)1 << (n-1);
for(int k=0; k<n; k++)
{
if(obit>7)
{
obit = 0;
pout++;
*pout = 0;
}
*pout |= (((input[i] & inmask) >> (n-k-1)) << (7-obit));
inmask >>= 1;
obit++;
nout++;
}
}
return nout;
}
int unpack(void* input, int nbitsin, int64_t* output, int n)
{
unsigned char* pin = (unsigned char*)input;
int64_t* pout = output;
int nbits = nbitsin;
unsigned char inmask = 0x80;
int inbit = 0;
int nout = 0;
while(nbits > 0)
{
*pout = 0;
for(int i=0; i<n; i++)
{
if(inbit > 7)
{
pin++;
inbit = 0;
}
*pout |= ((int64_t)((*pin & (inmask >> inbit)) >> (7-inbit))) << (n-i-1);
inbit++;
}
pout++;
nbits -= n;
nout++;
}
return nout;
}
int main()
{
int64_t input[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
int64_t output[21];
unsigned char compressed[21*8];
int n = 5;
int nbits = pack(input, 21, compressed, n);
int nout = unpack(compressed, nbits, output, n);
for(int i=0; i<=20; i++)
printf("input: %lld output: %lld\n", input[i], output[i]);
}
này rất hiệu quả vì là bước một chút tại một thời điểm, nhưng đó là cách dễ nhất để thực hiện nó mà không phải đối phó với các vấn đề của endianess. Tôi đã không thử nghiệm điều này hoặc với một loạt các giá trị, chỉ là những người trong thử nghiệm. Ngoài ra, không có kiểm tra giới hạn và giả định rằng bộ đệm đầu ra đủ dài. Vì vậy, những gì tôi nói là mã này có lẽ chỉ tốt cho mục đích giáo dục để giúp bạn bắt đầu.
hết sức tò mò, bạn đã sử dụng cái gì cuối cùng? –
Không có gì thực sự, dự án nó có nghĩa là đã chết :). Nhưng từ những câu trả lời ở đây và nhu cầu ban đầu của tôi, tôi có lẽ sẽ kết thúc bằng cách sử dụng một số mặt nạ và tính toán offsets bằng tay. Có thể sử dụng một số mẫu thông minh. – pajton
3 năm sau khi bạn hỏi, cuối cùng tôi đã trả lời câu hỏi của bạn bằng cách triển khai vùng chứa truy cập ngẫu nhiên nơi các thành phần được đóng gói chặt chẽ. Xem câu trả lời của tôi: http://stackoverflow.com/a/18038506/216063 –