Ответ 1
Да. GZIP - это алгоритм сжатия, который требует сжимаемых данных и имеет накладные расходы (кадрирование и словари и т.д.). Вместо этого следует использовать алгоритм кодирования.
"Простой" метод заключается в использовании кодировки base-64.
То есть, преобразуйте число (которое представлено в качестве основы 10 в строке) к фактической серии байтов, которые представляют число (5 байтов будут содержать десятизначное десятичное число), а затем результат base-64. Каждый символ base-64 хранит 6 бит информации (до десятичных знаков ~ 3.3 бит/символ) и, таким образом, приводит к размеру примерно чуть более половины (в этом случае требуются выходные символы 6 * base-64).
Кроме того, поскольку длины ввода/вывода могут быть получены из самих данных, "123" может быть изначально (до кодирования с базой 64), преобразованного как 1 байт, "30000" как 2 байта и т.д. Это было бы выгодно если не все числа примерно одинаковой длины.
Счастливое кодирование.
* Использование base-64 требует 6 выходных символов.
Изменить: я ошибался вначале, когда я сказал "2.3 бит / char" для десятичного числа и предложил, чтобы было меньше половины символов. Я обновил ответ выше и покажу математику (должен быть правильной) здесь, где lg(n)
зарегистрирован на базе 2.
Количество входных битов, необходимых для представления входного номера, составляет bits/char * chars
→ lg(10) * 10
(или просто lg(9999999999)
) → ~33.2 bits
. Используя jball-манипуляцию, чтобы сначала сдвинуть номер, требуется количество бит: lg(8999999999)
→ ~33.06 bits
. Однако это преобразование не способно повысить эффективность в этом конкретном случае (количество входных бит должно быть уменьшено до 30 или ниже, чтобы иметь здесь значение).
Итак, мы пытаемся найти x (количество символов в кодировке base-64), чтобы:
lg(64) * x = 33.2
→ 6 * x = 33.2
→ x ~ 5.53
. Конечно, пять с половиной символов бессмысленно, поэтому мы выбираем 6 как максимальное количество символов, необходимых для кодирования значения до 999999999 в кодировке base-64. Это немного больше, чем половина из 10 символов.
Однако следует отметить, что для получения только 6 символов в выводе base-64 требуется нестандартный кодер base-64 или немного манипуляции (большинство кодов base-64 работают только на целых байтах). Это работает, потому что из исходных 5 "обязательных байтов" используется только 34 из 40 бит (верхние 6 бит всегда равны 0). Для кодирования всех 40 бит потребуется 7 символов base-64.
Ниже приведена модификация кода, который Guffa опубликовал в своем ответе (если вам это нравится, попробуйте дать ему голосование), для которого требуется только 6 символов base-64. См. Другие примечания в ответе Гуффа и Base64 для приложений URL, поскольку нижеприведенный метод не использует URL-совместимое сопоставление.
byte[] data = BitConverter.GetBytes(value);
// make data big-endian if needed
if (BitConverter.IsLittleEndian) {
Array.Reverse(data);
}
// first 5 base-64 character always "A" (as first 30 bits always zero)
// only need to keep the 6 characters (36 bits) at the end
string base64 = Convert.ToBase64String(data, 0, 8).Substring(5,6);
byte[] data2 = new byte[8];
// add back in all the characters removed during encoding
Convert.FromBase64String("AAAAA" + base64 + "=").CopyTo(data2, 0);
// reverse again from big to little-endian
if (BitConverter.IsLittleEndian) {
Array.Reverse(data2);
}
long decoded = BitConverter.ToInt64(data2, 0);
Сделать это "красивее"
Поскольку base-64 было определено, чтобы использовать 6 символов, тогда любой вариант кодирования, который все еще кодирует входные биты на 6 символов, создаст такой же небольшой результат. Использование base-32 encoding не совсем сделает разрез, так как в кодировке base-32 6 символ может хранить только 30 бит информации (lg(32) * 6
).
Однако такой же размер вывода может быть достигнут с помощью специальной кодировки base-48 (или 52/62). (Преимущество базы 48-62 состоит в том, что они требуют только подмножества буквенно-цифровых символов и не нуждаются в символах; необязательно "двусмысленные" символы, такие как 1, и "I" можно избежать для вариантов). В системе base-48 6 символов могут кодировать ~ 33,5 бит (lg(48) * 6
) информации, которая находится чуть выше бит ~ 33.2 (или ~ 33.06) (lg(10) * 10
).
Вот доказательство концепции:
// This does not "pad" values
string Encode(long inp, IEnumerable<char> map) {
Debug.Assert(inp >= 0, "not implemented for negative numbers");
var b = map.Count();
// value -> character
var toChar = map.Select((v, i) => new {Value = v, Index = i}).ToDictionary(i => i.Index, i => i.Value);
var res = "";
if (inp == 0) {
return "" + toChar[0];
}
while (inp > 0) {
// encoded least-to-most significant
var val = (int)(inp % b);
inp = inp / b;
res += toChar[val];
}
return res;
}
long Decode(string encoded, IEnumerable<char> map) {
var b = map.Count();
// character -> value
var toVal = map.Select((v, i) => new {Value = v, Index = i}).ToDictionary(i => i.Value, i => i.Index);
long res = 0;
// go in reverse to mirror encoding
for (var i = encoded.Length - 1; i >= 0; i--) {
var ch = encoded[i];
var val = toVal[ch];
res = (res * b) + val;
}
return res;
}
void Main()
{
// for a 48-bit base, omits l/L, 1, i/I, o/O, 0
var map = new char [] {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't',
'u', 'v', 'x', 'y', 'z', '2', '3', '4',
};
var test = new long[] {0, 1, 9999999999, 4294965286, 2292964213, 1000000000};
foreach (var t in test) {
var encoded = Encode(t, map);
var decoded = Decode(encoded, map);
Console.WriteLine(string.Format("value: {0} encoded: {1}", t, encoded));
if (t != decoded) {
throw new Exception("failed for " + t);
}
}
}
Результат:
value: 0 encoded: A value: 1 encoded: B value: 9999999999 encoded: SrYsNt value: 4294965286 encoded: ZNGEvT value: 2292964213 encoded: rHd24J value: 1000000000 encoded: TrNVzD
Вышеприведенное рассматривает случай, когда цифры являются "случайными и непрозрачными"; то есть нет ничего, что можно было бы определить о внутренностях числа. Однако, если есть определенная структура (например, 7-й, 8-й и 9-й бит всегда равны нулю, а 2-й и 15-й бит всегда совпадают), тогда - тогда и только тогда, когда из входного сигнала можно исключить 4 или более бит информации - - потребуется только 5 базовых 64 символов. Добавленные сложности и зависимость от структуры, скорее всего, перевешивают любой маржинальный выигрыш.