我们有会员卡(类似于信用卡/借记卡,但由我们的定制代码处理,而不是通过与银行接口处理的卡)。我们需要在卡上存储交易数据,因为许多交易将使用离线设备进行,并且仅在下次在在线终端上点击该卡时才上传。
\n卡存储空间如果有限(通常最大为 8Kb,除非您为非常智能的卡支付愚蠢的价格),所以我需要尽可能地压缩数据。
\n我们的交易数据由三部分组成,所有部分都只涉及数字(即不涉及字母或特殊字符)...
\nyyMMddhhmmssfff如果将其表示为一串数字,则每笔交易有 37 个数字。
\n我尝试使用System.IO.Compression(遵循本博客文章中的代码以及随附的 GitHub 存储库,未包含在此处,因为它是类的沼泽标准用法)中的算法。
这给出了一些相当令人印象深刻的结果,使用最佳 Gzip 算法减少了大约 72%。
\n然而,我想知道,鉴于我们对交易数据的形状有所了解,是否可以对此进行改进。例如,数据的日期/时间部分细分如下......
\n任何人对这些限制是否有助于我改进这种压缩发表评论。谢谢
\n我们可以将数据压缩为118位(或15字节)。到目前为止,我们有范围:
1 Jan 2000 0:0:0.000最多1 Jan 2100 0:0:0.000为3_155_760_000_000毫秒1_000_000_000_000_000_000可能的数字1_000_00以便士为单位所以我们总共有:
double dt = (new DateTime(2100, 1, 1) - new DateTime(2000, 1, 1)).TotalMilliseconds;
double sn = 1_000_000_000_000_000_000L;
double amount = 1_000_00;
Console.Write(Math.Log2(dt * sn * amount));
Run Code Online (Sandbox Code Playgroud)
结果是117.925470...位,118位,因为我们不能部分使用位
编辑:压缩和解压缩例程:
private static byte[] MyCompress(DateTime date, long serial, decimal amount) {
BigInteger ms = (long)(date - new DateTime(2000, 1, 1)).TotalMilliseconds;
BigInteger value =
ms * 1_000_000_000_000_000_000L * 1_000_00 +
(BigInteger)serial * 1_000_00 +
(BigInteger)(amount * 100);
byte[] result = new byte[15];
for (int i = result.Length - 1; i >= 0; --i, value /= 256)
result[i] = (byte)(value % 256);
return result;
}
private static (DateTime date, long serial, decimal amount) MyDecomress(byte[] data) {
BigInteger value = data.Aggregate(BigInteger.Zero, (s, a) => s * 256 + a);
BigInteger amount = value % 1_000_00;
BigInteger serial = (value / 1_000_00) % 1_000_000_000_000_000_000L;
BigInteger dt = value / 1_000_00 / 1_000_000_000_000_000_000L;
return (
new DateTime(2000, 1, 1).AddMilliseconds((double)dt),
(long)serial,
(decimal)amount / 100M
);
}
Run Code Online (Sandbox Code Playgroud)
演示:
var data = MyCompress(new DateTime(2023, 1, 25, 21, 06, 45), 12345, 345.87m);
Console.WriteLine(string.Join(" ", data.Select(b => b.ToString("X2"))));
var back = MyDecomress(data);
Console.Write(back);
Run Code Online (Sandbox Code Playgroud)
输出:
00 0E 05 4C 23 D7 34 A8 BD E8 F7 CC 3D 95 80 BB
(25.01.2023 21:06:45, 12345, 345.87)
Run Code Online (Sandbox Code Playgroud)
编辑:如果我们可以存储高达1/10秒(而不是毫秒)的日期和时间,我们只能使用14字节:
private static byte[] MyCompress(DateTime date, long serial, decimal amount) {
BigInteger ms = (long)(date - new DateTime(2000, 1, 1)).TotalMilliseconds / 100;
BigInteger value =
ms * 1_000_000_000_000_000_000L * 1_000_00 +
(BigInteger)serial * 1_000_00 +
(BigInteger)(amount * 100);
byte[] result = new byte[14];
for (int i = result.Length - 1; i >= 0; --i, value /= 256)
result[i] = (byte)(value % 256);
return result;
}
private static (DateTime date, long serial, decimal amount) MyDecomress(byte[] data) {
BigInteger value = data.Aggregate(BigInteger.Zero, (s, a) => s * 256 + a);
BigInteger amount = value % 1_000_00;
BigInteger serial = (value / 1_000_00) % 1_000_000_000_000_000_000L;
BigInteger dt = value / 1_000_00 / 1_000_000_000_000_000_000L;
return (
new DateTime(2000, 1, 1).AddMilliseconds((double)dt * 100),
(long)serial,
(decimal)amount / 100M
);
}
Run Code Online (Sandbox Code Playgroud)
解决方案#1(旧的,16 字节):
\n您可以通过使用上述限制来保存两位数字(字节):
\nmonth+day成dayOfYear(000-365 )(为了保持一致,假设 2 月始终有 29 天);hours+minutes+seconds成timeInSeconds(00000-86399 )。请注意,您可能可以使用一些其他技术来减小字符串的大小。
\n之后,您可以将字符串中的数字从 转换base 10为base 256。因此你得到16 个字节而不是 37 个字节。没有数学证明,只是通过链接在代码中得到实际结果(输出在页面底部)。\n https://ideone.com/SMKb6S
结果:
\ninitial: 39 999912312359599999999999999999999999999\nbase10: 37 9999365863999999999999999999999999999\nbase256: 16 [7, 133, 206, 204, 233, 237, 90, 213, 156, 154, 224, 34, 63, 255, 255, 255]\nbase62: 21 EC5zRr0FV71hggqe73b0J\nRun Code Online (Sandbox Code Playgroud)\n之后你可以尝试一些压缩方法。然而,正如评论中指出的,它可能不适用于少量数据。
\n解决方案#2(15 字节):
\n实际上,您最终可以得到15 个字节。Dmitry Bychenko 在他的回答中使用了微秒而不是毫秒(我没有足够的声誉在评论中指出这一点)。 固定的。那么,128 years将会是4_047_667_200_000 milliseconds(或类似的东西)。
所有数据都容纳在 15 个字节中,有些位甚至是空闲的。例如,您可以使用它们来增加最大金额。以下是Python中的计算:https://ideone.com/37Bie3
\n结果:
\nTarget bytes: 15 (120 bits)\nYears: 64\n Total bits: 120\n Max amount: \xc2\xa341943.04 (22 bits, 5 free bits used)\nYears: 128\n Total bits: 120\n Max amount: \xc2\xa320971.52 (21 bits, 4 free bits used)\nYears: 256\n Total bits: 120\n Max amount: \xc2\xa310485.76 (20 bits, 3 free bits used)\nYears: 512\n Total bits: 120\n Max amount: \xc2\xa35242.88 (19 bits, 2 free bits used)\nYears: 1024\n Total bits: 120\n Max amount: \xc2\xa32621.44 (18 bits, 1 free bits used)\nYears: 2048\n Total bits: 120\n Max amount: \xc2\xa31310.72 (17 bits, 0 free bits used)\nRun Code Online (Sandbox Code Playgroud)\n编辑:对解决方案 #1 执行一些格式化,添加解决方案 #2。
\n| 归档时间: |
|
| 查看次数: |
163 次 |
| 最近记录: |