如何压缩一个非重复数字大小的N位序列?
How to compress a sequence of non-repeated number size N bits?
我试图压缩一个非负数序列,其中:
- 每个数字的值范围是从 0 到 2^N-1
-
每个数字只出现一次(这意味着总共有 2^N 个数字)
N = 4 的示例:
[14, 1, 8, 2, 12, 6, 0, 10, 4, 13, 5, 7, 15, 9, 3, 11]
因此,通常每个数字的成本为 4 位,对于 16 个数字,我们将不得不使用 16x4 = 64 位来存储它们。
目前我刚刚想到将它们压缩如下:
- 对于前 8 个数字 ->使用 4 位来存储每个数字。
- 接下来的 4 个数字--->每个数字只有 3 位/个
- 对于接下来的 2 个数字--->每个数字只有 2 位/个
- 对于接下来的 1 个数字--->只有 1 位。 对于最后一个,
- 实际上没有必要存储(显然,如果我们知道所有其他 15 个数字,我们应该知道最后一个数字是什么)
因此,压缩后的数据大小将是:
Z = 8 * 4 + 4 * 3 + 2 * 2 + 1 * 1 + 1 * 0 = 49 bits
压缩比约为76%,这相当不错(我认为)。
但对于较大的 N 值,该比率似乎降低了(对于 N = 2048,该比率仅为 91%)
所以我想听听你对更好压缩的建议。
谢谢。
正如注释中指出的那样,如果所有排列的可能性相等,最佳编码是在排列枚举中用其索引替换整个排列。既然有n!可能的排列,索引需要对数2n!位,因此每个元素使用log 2n 位的朴素编码的压缩比为 (logn!/(n logn)。
使用斯特林的近似,我们可以将其重写为 (n log n - n + O(log n))/(n logn),即 1 - 1/(log n) + O(1/n),随着n的增长,它显然渐近接近 1。因此,对于较大的n,压缩比将不可避免地降低。
除非并非所有排列的可能性都相等(并且您有一些关于概率分布的信息),否则不可能实现更好的压缩。
目前您正在使用 N*2^N 位。
基本上你拥有的是数字的排列,每个排列都是唯一的,对于排列,你可以计算一个唯一的标识符。既然有(2^N)!排列,你只需要 ceil(log2((2^N)!)) 位。例如,这是 45 位。
对于这个特定问题,最有效的编码是在阶乘数系统中将[0 .. 2^N-1]
的排列视为数字,并存储该排列的Lehmer码。
这就要求ceil(log2((2^N)!))
位。对于 N = 4,这使用 45 位 (70.3%);对于 N = 11 (2^N = 2048),19581 位 (86.9%)。
压缩比随着N的增加而恶化;使用简单近似log x! >= (x log x) - x + 1
我们达到1 - ((2^N - 1)/(2^N))*(1 / (N * log(2)))
log2((2^N)!) / (N 2^N)
的最小值,当N
趋于无穷大时接近1
。
考虑到压缩比的绝对限制,任何你能找到的相当有效的方法都是值得的;对于小到N = 15的值,不可能超过90%。
- 比较并显示使用最小值(a,b)和最大值(a、b)升序排列的4个数字
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- 检查输入是否不是整数或数字
- C++中高效的大型稀疏块压缩线性方程
- 嵌入方指针压缩已禁用
- C++使用整数的压缩数组初始化对象
- 如何(从固定列表中)选择一个数字序列,该序列将与目标数字相加
- 如何用数字处理log(0)
- 最高有效数字侧的第N位
- 如何获取一个数字的前3位
- 在C++中将函数压缩为两种方式
- 查找最接近的大于当前数字的数字的索引
- 找到两对数字,使它们的乘积的绝对差最小化
- 我想做一个彼此不同但重复出现的数字
- 将数字转换为字母(例如:123 转换为一二三)
- 如何在 cpp 中解压缩数字,如果它们是使用 struct.pack(fmt, v1, v2, ..) 打包在 pyth
- 如何压缩一个非重复数字大小的N位序列?
- 高度压缩数字网格
- 寻求更好的编码和压缩数字的方法
- 如何压缩一串数字