Algoritmo de compresión de cadenas de desarrollo propio

Visión de conjunto

En el desarrollo, a menudo hay escenarios en los que se informa de las pilas en línea para analizar y tratar los problemas en línea. Por lo tanto, la compresión y el cifrado de la pila también son esenciales. Cifrado: se puede usar el algoritmo de cifrado simétrico AES, compresión: las cadenas se pueden comprimir usando la compresibilidad nativa de protobuf al cargar.

Sin embargo, para ahorrar tráfico y mejorar la eficiencia de la transmisión, se puede garantizar comprimiendo los datos una vez antes de cargar la pila. Lo siguiente presentará un algoritmo que el autor ha explorado por sí mismo para comprimir cadenas y tiene su propio efecto de encriptación.

Introducción al algoritmo

Este algoritmo utiliza un escenario: compresión de cadenas con juegos de caracteres limitados .

Por ejemplo, la compresión del nombre completamente calificado del método Java, para el método completamente calificado, los componentes: letras mayúsculas y minúsculas en inglés, números, caracteres especiales. En el proceso de desarrollo, un nombre de clase estándar y calificado y un nombre de método deben estar familiarizados con el nombre. Según estadísticas efectivas, más del 99% de los métodos están completamente calificados con letras mayúsculas y minúsculas en inglés.

Implementación de algoritmos

Breve introducción al principio de compresión

Utilice los bits libres de un carácter char para almacenar datos válidos. Por ejemplo, asignando a ~ z a números del 1 al 26 y dividiendo el Chartipo en tres grupos de alto, medio y bajo con 5 bits como grupo, un número se almacena por separado (este número representa un carácter)

Cree una estructura de encabezado de cadena: Head

En el proceso de escritura de código Java, la proporción de letras mayúsculas en una cadena completamente calificada es relativamente pequeña, por lo tanto, las letras mayúsculas en la cadena completamente calificada se registran utilizando los caracteres complementarios anteriores. Si una cadena es finita e inmutable, se determinan las posiciones relativas de los caracteres que la componen. El algoritmo de implementación es el siguiente:

public char[] build(String s) {
            ...
    for (int i = 0; i < len; i++) {
        c = s.charAt(i);
        b = Character.isUpperCase(c);
        if (b || c == FILL) {
            if (i - lastIndex >= maxDistance) {
                maxDistance = i - lastIndex;
            }
            upCharIndex.add(i - lastIndex);
            lastIndex = i;
       }
    if (b) upCharCount++;
    }
    ...
    return handleHead(type);
}

复制代码

El primer paso antes de la compresión: al comienzo de la cadena, guarde y registre la posición de las letras mayúsculas y la distancia entre cada letra mayúscula. (El punto decimal se considera una letra mayúscula).


private char[] handleHead(int type) {
        ...
    int k, j;
    //记录大写字母位置与char中
    for (int i = 0; i < chars.length; i++) {
        if (i == 0) {
            for (j = 0, k = 1; j < ch1; j++, k++) {
                ch = bitToLeft(ch, upCharIndex.get(j), 12 - (k * stepDistance));
            }
            chars[i] = ch;
        } else {
            char emptyCh = FILL;
            emptyCh &= 0;
            int start = (i - 1) * sizeOfChar + ch1;
            for (j = start, k = 1; j < start + sizeOfChar; j++, k++) {
                if (j == upCharIndex.size())
                    break;
                emptyCh = bitToLeft(emptyCh, upCharIndex.get(j), 16 - (k * stepDistance));
            }
            chars[i] = emptyCh;
        }
    }
    return chars;
}

复制代码

La longitud mínima de Head es: 1 Char, que es de 16 bits. El tamaño del paso se almacena en los 2 bits superiores de 16 bits . Los siguientes 2 bits registran la longitud real de la cabeza .

head长度:Head最小的长度是1个Char,其中记录步长和Head长度的信息。目前,填充长度最长为 3+1,可通过步长算法完成Head长度的扩展。扩展方法:getTypeBySize、getSizeByType

  • 存储大写字母的位置时,按照步长来填充。例如:步长为3,那么就意味着每3个bit存储一个大写字母位置。
  • Head的长度取决于填充了多少个步长。例如:填充10个步长为3的位置,需要16%3等于5,那么就需要两个Char.

步长: 步长是一个可变的量,在算法设计中,提供如下几种步长类型:(据统计最长英文单词:45个字符)

  • STEP_0:表示没有大写字母
  • STEP_3:表示大写字母距离(0,8),步长为3
  • STEP_15:表示大写字母间距离[8,16),步长为4
  • STEP_OVER_15:表示大写字母间距离[16,63),步长为6

建立压缩字符串内容:Content

Content压缩是按照1个Char的高、中、低三位中分别存储一个字符的算法完成的。具体的实现FormatUtil.ContentBuilder

填充: 由于字符串并不都是3的倍数。为了保证原字符串的完整性,在分割字符串之前先给原来字符串填充一定数量的字符,保证其在分割的时候可以每3个字符为一组。


public String handleString(String s) {
    int f;
    if ((f = s.length() % 3) != 0) {
        StringBuilder sBuilder = new StringBuilder(s);
        for (f = 3 - f; f > 0; f--)
            sBuilder.append(FILL);
        s = sBuilder.toString();
    }
    return s.toLowerCase();
}

复制代码

分割替换: 在完成填充以后,将原来的字符串以三个为一组分割成多个组。对于字符串中的数字或者特殊字符,我们在mapping文件中并没有形成映射,因此,一旦出现,那么就通过“MASK”去代替。

public short buildShort(char high, char mid, char low) {
    short b = 0;

    b |= getShortFromMapping(high) << 10;
    b |= getShortFromMapping(mid) << 5;
    b |= getShortFromMapping(low);
    return b;
}

public short getShortFromMapping(char ch) {
    if (mapping.containsKey(ch))
        return mapping.get(ch);
    return mapping.get(MASK);
}
复制代码

建立完成压缩后字符串

Head + content = 压缩完成后的字符串。

总结

在算法构思前期,理论压缩效率可达66%:将三个Char存储在一个Char中,不过从最后包大小的总压缩率来计算,压缩率应该只有50%左右。出现这种的情况的原因如下:

  • 字符串长度不都是3的整数倍,有多余的字符填充
  • 压缩完以后的字符并不是一个正确的ASCII码,在Java底层对字符集的编解码过程中,将其认为是汉字,一次一个字符会被解码成两个字符大小。

完整代码 欢迎大家评论留言,指导学习!

Supongo que te gusta

Origin juejin.im/post/7079610382548992013
Recomendado
Clasificación