将两个或多个数字压缩为一个字节

12

我认为这并不是真正可能的,但还是值得问一下。假设我有两个小数(每个数的范围从0到11)。有一种方法可以将它们压缩成一个字节,并稍后获取它们吗?如果有类似大小的四个数字呢?

我需要的是:a1 + a2 = x。我只知道x,然后从中得到a1,a2。
对于第二部分:a1 + a2 + a3 + a4 = x。我只知道x,然后从中得到a1,a2,a3,a4。
注意:我知道你无法解除加法,只是在阐述我的问题。

x必须是一个字节。a1,a2,a3,a4的范围为[0,11]。


1
11在二进制中是1011,所以它只需要4位。是的,这是可能的。你需要将其左移四次,然后相加。之后,要取回它们,请获取前四位和最后四位。 - Umang
1
这让我有点儿像是在做作业。 - Esteban Araya
2
不,我向您保证这是我的独立研究成果,学校直到九月才开学;-) - Dave
@Esteban,不是的,更像是数据压缩。我在数据压缩中也遇到了这个问题,并解决了它,请看我的帖子。 - Tomas
11个回答

12

使用位掩码相对容易些。思路是将一个字节分成更小的单元,并将它们分配给不同的元素。

对于两个数字,可以按如下方式:前4位为数字1,其余为数字2。您可以使用number1 = (x & 0b11110000) >> 4number2 = (x & 0b00001111)来检索值,并使用x = (number1 << 4) | number2来压缩它们。


对于那些新手来说,位移只适用于可以用4位(2 ^ 4)表示的数字,即0-15号数字。原因是因为你在这里做的是存储一个值(在4位中),然后将它们向左移动4个位置并将下一个数字存储在该位置。由于字节只有8位,因此您只有两个4位数字的空间,从而限制了这种方法仅适用于小数字。 - newshorts

9

对于两个数字,当然可以。每个数字有12个可能的值,所以这一对数字总共有12^2 = 144个可能的值,而这比一个字节的256个可能的值要少。因此,您可以执行以下操作:

x = 12*a1 + a2
a1 = x / 12
a2 = x % 12

(如果你只有有符号字节,比如在Java中,那么就有点棘手了)

对于从0到11的四个数字,有12^4 = 20736个值,因此你不能将它们放入一个字节中,但是你可以用两个字节。

x = 12^3*a1 + 12^2*a2 + 12*a3 + a4
a1 = x / 12^3
a2 = (x / 12^2) % 12
a3 = (x / 12) % 12
a4 = x % 12

编辑:其他答案提到了每四位存储一个数字并使用位移的方法,这样更快。


1
在这种特定情况下,移位速度更快,但我欣赏这里的简单熵计算,因为它更通用 :) 值得注意的是,移位是一种优化,只有在确定可能存在解决方案时才会应用。 - Matthieu M.

2

一般来说,假设您想要混合 N 个数字 a1、a2、... aN,其中 a1 的范围为 0..k1-1,a2 的范围为 0..k2-1,... 而 aN 的范围为 0..kN-1。

那么,编码后的数字为:

encoded = a1 + k1*a2 + k1*k2*a3 + ... k1*k2*..*k(N-1)*aN

解码过程较为复杂,需要逐步进行:
rest = encoded
a1 = rest mod k1
rest = rest div k1

a2 = rest mod k2
rest = rest div k2

...

a(N-1) = rest mod k(N-1)
rest = rest div k(N-1)

aN = rest # rest is already < kN

2

0-11的例子相对简单--每个数字可以用四位二进制数存储,因此将它们放入一个字节中只需要将其中一个左移4位,然后使用“或”运算符将两个数字合并即可。

四个大小相似的数字无法放入同一个字节中--每个数字四位二进制数乘以四个数字至少需要16位来存储。


吹毛求疵 - 16位不是保存4个0-11数字所需的最小位数,您可以使用更少的位数,但代价是无法像快速编码/解码它们那样进行。 - jk.
@jk:嗯,是的,你可以用少于16个,但仍需要超过8个。 - Jerry Coffin

1
如果数字0-11不是均匀分布的,您可以通过为常见值使用较短的位序列,并为罕见值使用较长的位序列来进一步提高效率。编码使用哪种长度至少需要一个比特,因此计算机科学有一个完整的分支致力于证明何时值得这样做。

0

@Mike Caron

你最后的例子(4个介于0-3之间的整数)使用位移运算要快得多。不需要使用floor()函数。

value = (a << 6) | (b << 4) | (c << 2) | d;

a = (value >> 6);
b = (value >> 4) % 4;
c = (value >> 2) % 4;
d = (value) % 4;

0
将四个值打包成一个数字至少需要15位。这不能适应单个字节,而是需要两个字节。
你需要做的是从12进制到65536进制的转换,以及相反的转换。
B = A1 + 12.(A2 + 12.(A3 + 12.A4))

A1 = B % 12
A2 = (B / 12) % 12
A3 = (B / 144) % 12
A4 = B / 1728

由于占用了2字节,因此从十二进制到(紧缩的)十六进制的转换显然更可取。

B1 = A1 + 256.A2
B2 = A3 + 256.A4

A1 = B1 % 256
A2 = B1 / 256
A3 = B2 % 256
A4 = B2 / 256

模数和除法是通过位掩码和移位实现的。


0

一个字节可以容纳256个值或十六进制中的FF。因此,您可以在一个字节中编码0-16之间的两个数字。

byte a1 = 0xf;
byte a2 = 0x9;
byte compress = a1 << 4 | (0x0F & a2);  // should yield 0xf9 in one byte.

如果你将它缩小到0-8范围内,你可以做出4个数字。


如果将其缩小到0-8范围,您可以处理4个数字。嗯......也许是0-3范围?每个数字2位... 4个数字,8位。 - Dan

0

由于一个字节是8位,您可以轻松地将其细分为更小的值范围。这种极限情况是当您有8个单一位整数时,即所谓的位域。

如果您想存储两个4位整数(每个整数取值范围为0-15),您只需执行以下操作:

value = a * 16 + b;

只要进行适当的边界检查,您就不会丢失任何信息。
要获取两个值,您只需执行以下操作:
a = floor(value / 16)
b = value MOD 15

MOD是模数,它是除法的“余数”。

如果您想存储四个2位整数(0-3),可以这样做:

value = a * 64 + b * 16 + c * 4 + d

而要将它们取回:

a = floor(value / 64)
b = floor(value / 16) MOD 4
c = floor(value / 4) MOD 4
d = value MOD 4

我把最后一部分留给读者作为练习;)

0

使用位掩码或位移操作。后者更快。

尝试一下二叉树,会很有趣的。(在开发中涉及数据和各种开发技巧方面,它将非常有用)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接