我有一段数据,可以是 1 到 7 之间的数字。我可以将其中多少压缩成一个字节,如何压缩?
I have a piece of data that can be a number from 1 to 7. How many of these can I compress into a single byte and how?
由于单个字节最多可以容纳 256 个值,因此每个字节应该可以唯一映射多个值。但是我不确定我可以存储多少而不丢失。
如果我需要涵盖特定的值范围,是否有通用算法来执行此操作?
如果映射到更大的字号,我可以改进映射吗?
理想化的答案是将大小为 n 的 x(独立)范围映射到 m 位的算法。
也许我完全误解你了,一个字节包含8位。每一位都可以是零或一。现在一个字节可以有256种不同的状态,最多可以容纳255个数。要表示 1 到 7(或 0 到 6)之间的数字,您需要三位。喜欢 001 = 1; 010 = 2; 011 = 3 ...等等。所以通常你可以在一个字节中存储 1 到 7 之间的两个数字。但是这样你就浪费了两位。也许你可以使用重叠字节。在三个字节中,您可以存储 8 个值,并且不会浪费任何位。也许你可以想出一些奇特的算法来在一个字节中存储三个值并有一定的限制。例如,如果您知道至少有一个值与其他值不同,则可以尝试对字节内的值进行排序。你想到一个假想的第九位。如果第一个值小于第二个值,则为 1,否则为 0。但要更深入地了解有关数据和用例的一些额外信息,那就太棒了。
用最一般的术语来说,当你有一个从 1 到 7(我假设包括在内)的值时,它相当于范围 [0..6],你可以
将其视为 base 7.
中的数字
这意味着它们中的一串以 7 为底数形成一个数字。然后,您可以做的最简单的事情就是将该数字存储在计算机变量中(实际上,将其转换为以 2 为底数,但您没有通常不必考虑这一点,因为数字就是数字。)
现在让我们计算一下您的值中有多少适合多少位。显然,一个值将适合 3 位(并且您将浪费 8 个值中的 1 个,或 3 位的 12.5%。)
两个值——相当于以 7 为基数的 two-digit 数字——的范围为 0 到 48,并且适合 6 个整数位(并且将浪费 64 个二进制值中的 15 个,即 23.4%。)
三个值的取值范围为 0 到 342,将占满 9 个整数位,浪费很多值。
我想你可以想出剩下的。请注意,根据定义,存储每个数字(在本例中为 7)所需的实际(分数)位数是其以 2 为底的 对数 。
要以这种方式实际编码和解码,您只需连续乘以 7(编码)或除以 7(解码),就像您将数字转换为任何其他基数一样。例如,如果您的值是 x
、y
和 z
,则以下内容将成立:
// x,y,z must be in [0..6], so probably have to subtract 1 from each first...
// then:
encoded = x * 1 + y * 7 + z * 49; // encoded will fit in 9 bits.
decoded_x = (encoded / 1) % 7; // also need a plus one.
decoded_y = (encoded / 7) % 7; // same
decoded_z = (encoded / 49) % 7; // ditto
一般来说,要将 [0..m-1] 范围内的任意数量的值映射并打包成多个位,您可以执行与上述相同的操作。将您的每个值视为 base m
中(可能很大)数字中的单个数字,然后将该数字存储在可以容纳它的任何变量中。
注 1:通常,您将越多的值打包到单个数字中,您得到的浪费位就越少。例如,一些评论和其他答案建议将每个以 7 为底的值打包成 3 位。如果这样做,您只能将其中的 10 个存储在一个 32 位变量中。但是您实际上可以在 32 位中打包 11 个(完整的)值。不可否认,每 3 位存储 1 个值的编码和解码速度更快(仅使用移位和掩码),这使得选择取决于您的需要。
注释 2:有很多方法可以讨论(和推理)分数 位计数。例如,每个 base-7 数字将占用 2.81 位。还有其他方法可以将它们编码并存储为一个集合。但这在编码和压缩领域是一个更复杂的问题。好奇的话可以看看“算术编码”。
注释 3:谈到压缩,有 far 更好的方法可以将多个值打包成多个位,如果你对他们了解更多。也就是说,如果它们中的一些更有可能是特定值,或者彼此相等,或者其他什么。此外,如果有足够多的值,您可以从数据本身提取这些关系。这实际上是通用无损压缩器的作用。
注意 4:请记住,如果您的值范围不是相同的。例如,如果 x
和 z
在 [0..6] 范围内,但 y
在 [0..4] 范围内,那么你可以像这样编码和解码它们:
encoded = x * 1 + y * 7 + z * (7 * 5); // encoded will fit in 8 bits now!
decoded_x = (encoded / 1) % 7;
decoded_y = (encoded / 7) % 5;
decoded_z = (encoded / 35) % 7;
n 个概率相同的符号将占用 lg n 位,其中 lg 是以 2 为底的对数。您可以将其中的 floor(m / lg n) 存储在 m 位中,将它们编码为 m 位作为基数 n 整数的数字。
对于n=7和m=8,可以存储2。通常,您会希望选择一个 m 来减少未使用的位数。 m=48(6个字节)在这里是个不错的选择,里面可以存放17个符号。 717=232,630,513,987,207,接近但小于248= 281,474,976,710,656。只有大约四分之一的比特被浪费了,或者说 48 比特的一半。也可以使用 64 位算法高效地完成基本转换。
由于单个字节最多可以容纳 256 个值,因此每个字节应该可以唯一映射多个值。但是我不确定我可以存储多少而不丢失。
如果我需要涵盖特定的值范围,是否有通用算法来执行此操作?
如果映射到更大的字号,我可以改进映射吗?
理想化的答案是将大小为 n 的 x(独立)范围映射到 m 位的算法。
也许我完全误解你了,一个字节包含8位。每一位都可以是零或一。现在一个字节可以有256种不同的状态,最多可以容纳255个数。要表示 1 到 7(或 0 到 6)之间的数字,您需要三位。喜欢 001 = 1; 010 = 2; 011 = 3 ...等等。所以通常你可以在一个字节中存储 1 到 7 之间的两个数字。但是这样你就浪费了两位。也许你可以使用重叠字节。在三个字节中,您可以存储 8 个值,并且不会浪费任何位。也许你可以想出一些奇特的算法来在一个字节中存储三个值并有一定的限制。例如,如果您知道至少有一个值与其他值不同,则可以尝试对字节内的值进行排序。你想到一个假想的第九位。如果第一个值小于第二个值,则为 1,否则为 0。但要更深入地了解有关数据和用例的一些额外信息,那就太棒了。
用最一般的术语来说,当你有一个从 1 到 7(我假设包括在内)的值时,它相当于范围 [0..6],你可以 将其视为 base 7.
中的数字这意味着它们中的一串以 7 为底数形成一个数字。然后,您可以做的最简单的事情就是将该数字存储在计算机变量中(实际上,将其转换为以 2 为底数,但您没有通常不必考虑这一点,因为数字就是数字。)
现在让我们计算一下您的值中有多少适合多少位。显然,一个值将适合 3 位(并且您将浪费 8 个值中的 1 个,或 3 位的 12.5%。) 两个值——相当于以 7 为基数的 two-digit 数字——的范围为 0 到 48,并且适合 6 个整数位(并且将浪费 64 个二进制值中的 15 个,即 23.4%。) 三个值的取值范围为 0 到 342,将占满 9 个整数位,浪费很多值。
我想你可以想出剩下的。请注意,根据定义,存储每个数字(在本例中为 7)所需的实际(分数)位数是其以 2 为底的 对数 。
要以这种方式实际编码和解码,您只需连续乘以 7(编码)或除以 7(解码),就像您将数字转换为任何其他基数一样。例如,如果您的值是 x
、y
和 z
,则以下内容将成立:
// x,y,z must be in [0..6], so probably have to subtract 1 from each first...
// then:
encoded = x * 1 + y * 7 + z * 49; // encoded will fit in 9 bits.
decoded_x = (encoded / 1) % 7; // also need a plus one.
decoded_y = (encoded / 7) % 7; // same
decoded_z = (encoded / 49) % 7; // ditto
一般来说,要将 [0..m-1] 范围内的任意数量的值映射并打包成多个位,您可以执行与上述相同的操作。将您的每个值视为 base m
中(可能很大)数字中的单个数字,然后将该数字存储在可以容纳它的任何变量中。
注 1:通常,您将越多的值打包到单个数字中,您得到的浪费位就越少。例如,一些评论和其他答案建议将每个以 7 为底的值打包成 3 位。如果这样做,您只能将其中的 10 个存储在一个 32 位变量中。但是您实际上可以在 32 位中打包 11 个(完整的)值。不可否认,每 3 位存储 1 个值的编码和解码速度更快(仅使用移位和掩码),这使得选择取决于您的需要。
注释 2:有很多方法可以讨论(和推理)分数 位计数。例如,每个 base-7 数字将占用 2.81 位。还有其他方法可以将它们编码并存储为一个集合。但这在编码和压缩领域是一个更复杂的问题。好奇的话可以看看“算术编码”。
注释 3:谈到压缩,有 far 更好的方法可以将多个值打包成多个位,如果你对他们了解更多。也就是说,如果它们中的一些更有可能是特定值,或者彼此相等,或者其他什么。此外,如果有足够多的值,您可以从数据本身提取这些关系。这实际上是通用无损压缩器的作用。
注意 4:请记住,如果您的值范围不是相同的。例如,如果 x
和 z
在 [0..6] 范围内,但 y
在 [0..4] 范围内,那么你可以像这样编码和解码它们:
encoded = x * 1 + y * 7 + z * (7 * 5); // encoded will fit in 8 bits now!
decoded_x = (encoded / 1) % 7;
decoded_y = (encoded / 7) % 5;
decoded_z = (encoded / 35) % 7;
n 个概率相同的符号将占用 lg n 位,其中 lg 是以 2 为底的对数。您可以将其中的 floor(m / lg n) 存储在 m 位中,将它们编码为 m 位作为基数 n 整数的数字。
对于n=7和m=8,可以存储2。通常,您会希望选择一个 m 来减少未使用的位数。 m=48(6个字节)在这里是个不错的选择,里面可以存放17个符号。 717=232,630,513,987,207,接近但小于248= 281,474,976,710,656。只有大约四分之一的比特被浪费了,或者说 48 比特的一半。也可以使用 64 位算法高效地完成基本转换。