我正在尝试获取21个字节的数据,这些数据唯一地标识交易并将其存储在16字节char
数组中。我无法为此提出正确的算法。
我正在尝试压缩的交易ID包含2个字段:
所以包含这些数据的C ++类看起来像这样:
class ID
{
public:
char trade_num_[18];
char broker_[3];
};
此数据需要存储在16 - char
数据结构中,如下所示:
class Compressed
{
public:
char sku_[16];
};
我试图利用这样一个事实,即trade_num_
中的字符只有0-127,每个字符中有1个未使用的位。类似地,999二进制是1111100111,它只有10位 - 比2字节字短6位。但是当我弄清楚我能把它压缩多少时,我能做到的最小值是17个字节;一个字节太大了。
有什么想法吗?
顺便说一下,trade_num_
是用词不当。它可以包含字母和其他字符。这就是规范所说的。
trade_num_
字段确实是18个字节,而不是16个。在我发布这个帖子后,我的网络连接已经死了,直到现在我才回到这个线程。
EDIT2:我认为对数据集做出假设是安全的。对于trade_num_字段,我们可以假设不存在不可打印的ASCII字符0-31。 ASCII码也不是127或126(〜)。所有其他人可能都在场,包括大写和小写字母,数字和标点符号。这将在trade_num_
组成的集合中共有94个字符,包括32到125的ASCII代码。
答案 0 :(得分:33)
如果您在0 - 127范围内有18个字符,并且在0 - 999范围内有一个数字并且尽可能地压缩它,那么它将需要17个字节。
>>> math.log(128**18 * 1000, 256)
16.995723035582763
您可以利用一些角色最有可能不被使用的事实。特别是,不可能存在低于值32的任何字符,并且也可能不使用127。如果你可以找到一个未使用的字符,那么你可以先将字符转换为base 94,然后尽可能地将它们打包成字节。
>>> math.log(94**18 * 1000, 256)
15.993547951857446
这个只是符合16个字节!
示例代码
这是一些用Python编写的示例代码(但是以非常强制的方式编写,以便非Python程序员可以轻松理解)。我假设输入中没有波浪号(~
)。如果有,你应该在编码字符串之前用另一个字符替换它们。
def encodeChar(c):
return ord(c) - 32
def encode(s, n):
t = 0
for c in s:
t = t * 94 + encodeChar(c)
t = t * 1000 + n
r = []
for i in range(16):
r.append(int(t % 256))
t /= 256
return r
print encode(' ', 0) # smallest possible value
print encode('abcdefghijklmnopqr', 123)
print encode('}}}}}}}}}}}}}}}}}}', 999) # largest possible value
输出:
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[ 59, 118, 192, 166, 108, 50, 131, 135, 174, 93, 87, 215, 177, 56, 170, 172]
[255, 255, 159, 243, 182, 100, 36, 102, 214, 109, 171, 77, 211, 183, 0, 247]
该算法使用Python处理非常大数字的能力。要将此代码转换为C ++,您可以使用大整数库。
您当然需要等效的解码功能,原理相同 - 操作以相反的顺序执行。
答案 1 :(得分:5)
这使得(18 * 7 + 10)= 136位,或17个字节。你写的trade_num
是字母数字吗?如果这意味着通常的[a-zA-Z0-9_]字符集,那么每个字符只有6位,整个事情需要(18 * 6 + 10)= 118位= 15字节。
假设8位= 1字节
或者,来自另一个方向:您有128位存储空间,数字部分需要~10位,因此trade_num需要118位。 18个字符表示每个字符118/18 = 6.555位,这意味着您只能有空格来编码2 6.555 = 94个不同的字符**除非在trade_num中有一个我们可以利用的隐藏结构保存更多位。
答案 2 :(得分:2)
这是应该有效的,假设您只需要来自allowedchars
的字符,那里最多有94个字符。这是python,但它写的是试图不使用花哨的快捷方式 - 这样你就可以更容易地将它翻译成目标语言。但是假设number
变量可能包含最多2 ** 128的整数 - 在C ++中你应该使用某种大数字类。
allowedchars=' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}'
alphabase = len(allowedchars)
def compress(code):
alphanumeric = code[0:18]
number = int(code[18:21])
for character in alphanumeric:
# find returns index of character on the allowedchars list
number = alphabase*number + allowedchars.find(character)
compressed = ''
for i in xrange(16):
compressed += chr(number % 256)
number = number/256
return compressed
def decompress(compressed):
number = 0
for byte in reversed(compressed):
number = 256*number + ord(byte)
alphanumeric = ''
for i in xrange(18):
alphanumeric = allowedchars[number % alphabase] + alphanumeric
number = number/alphabase
# make a string padded with zeros
number = '%03d' % number
return alphanumeric + number
答案 3 :(得分:1)
您可以在~~ 15字节(14字节和6位)中执行此操作。
对于trace_num_
中的每个字符,如果要将ascii保存为7位,则可以保存1位。
让我们获取数字信息,每个char可以是十个值(0到9)中的一个。 然后你必须有4位来保存这个字符,为了保存你必须有1字节和4位的数字,然后你节省了一半。
如果您只想使用qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPLKJHGFDSAZXCVBNM1234567890[]
您可以将每个char保存为6位。然后你有接下来的2个字节和2个比特。
如果您将数字保存为10个字节的整数。您可以将其设置为14个字节和6个位。
答案 4 :(得分:1)
关键问题是:
您的帖子中似乎存在一些矛盾,无论交易号码是16还是18个字符。你需要清除它。你说总数是21,包括16 + 3。 : - (
您说交易num字符在0x00-0x7f范围内。他们真的可以成为该范围内的任何角色,包括制表符,新行,control-C等吗?或者它们仅限于可打印字符,甚至可能是字母数字?
输出16个字节是否必须是可打印字符,还是基本上是二进制数?
编辑,更新原始帖子后:
在这种情况下,如果输出可以是字符集中的任何字符,则可以。如果它只能是可打印的字符,那就不是。
证明数学可能性很简单。 18个字符中的每一个都有94个可能的值,每个可能的值为10个。可能的组合总数= 94 ^ 18 * 10 ^ 3~ = 3.28E35。这需要128位。 2 ^ 127~ = 1.70e38,太小了,而2 ^ 128~ = 3.40e38,足够大。 128位是16个字节,因此如果我们可以使用每个可能的位组合,它几乎不适合。
鉴于紧密配合,我认为生成该值的最实用方法是将其视为双长数,然后通过算法运行输入以为每个可能的输入生成唯一的整数。
从概念上讲,让我们假设我们有一个16字节长的“巨大整数”数据类型。算法将是这样的:
huge out;
for (int p=0;p<18;++p)
{
out=out*94+tradenum[p]-32;
}
for (int p=0;p<3;++p)
{
out=out*10+broker[p]-'0';
}
// Convert output to char[16]
unsigned char[16] out16;
for (int p=15;p>=0;--p)
{
out16[p]=huge&0xff;
huge=huge>>8;
}
return out16;
当然我们在C中没有“巨大的”数据类型。您使用的是纯C还是C ++?在C ++中是不是有某种大数字类?对不起,我有一段时间没有做过C ++。如果没有,我们可以轻松创建一个小库来实现巨大的。
答案 5 :(得分:1)
空格(0x20)和波浪号(0x7e)之间有 95 个字符。 (其他答案中的94遇到了1分之一的错误)。
因此,不同ID的数量为95 18 ×1000 = 3.97×10 38 。
但该压缩结构只能保持(2 8 ) 16 = 3.40×10 38 不同的值。
因此,除非:
,否则无法用该结构表示所有IDtrade_num_
或trade_num_
或char
。答案 6 :(得分:0)
如果它只能包含字母,那么每个字符的可能性少于64个(26个大写,26个小写,留下12个空格,终结符,下划线等)。每个字符6位,你应该到达那里 - 15个字符。假设你不支持特殊字符。
答案 7 :(得分:0)
对于3个字符的数字字符串使用前10位(对它们表示一个数字进行编码,然后在解码时使用零填充)。
好的,这会留下118位和16个字母数字字符来存储。
0x00到0x7F(如果你的意思是包含)包含128个可能的字符来表示。这意味着每个字符可以通过7位的组合来识别。想出一个索引,将7位可以表示的每个数字映射到实际字符。要以这种方式表示16个“字母数字”字符,您需要总共112位。
我们现在有122位(或15.25字节)代表我们的数据。添加一个复活节彩蛋以填充剩余的未使用的位,并且您有16个字符的数组。