这个打印“你好世界”怎么样?5 位编纂算法描述

2022-08-31 07:01:41

我发现了这个奇怪的地方:

for (long l = 4946144450195624l; l > 0; l >>= 5)
    System.out.print((char) (((l & 31 | 64) % 95) + 32));

输出:

hello world

这是如何工作的?


答案 1

该数字拟合 64 位,其二进制表示形式为:4946144450195624

 10001100100100111110111111110111101100011000010101000

该程序从右到左为每个5位组解码一个字符

 00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
   d  |  l  |  r  |  o  |  w  |     |  o  |  l  |  l  |  e  |  h

5 位编纂

对于 5 位,可以表示 2⁵ = 32 个字符。英语字母表包含26个字母,这为32 - 26 = 除字母外的6个符号留出了空间。使用此编纂方案,您可以拥有所有26个(一种情况)英文字母和6个符号(其中空格)。

算法描述

for 循环中的 从一个组跳到另一个组,然后 5 位组被隔离,用句子中的掩码对数字进行 AND。>>= 531₁₀ = 11111₂l & 31

现在,代码将 5 位值映射到其相应的 7 位 ASCII 字符。这是棘手的部分。检查下表中小写字母的二进制表示形式:

  ASCII   |     ASCII     |    ASCII     |    Algorithm
character | decimal value | binary value | 5-bit codification
--------------------------------------------------------------
  space   |       32      |   0100000    |      11111
    a     |       97      |   1100001    |      00001
    b     |       98      |   1100010    |      00010
    c     |       99      |   1100011    |      00011
    d     |      100      |   1100100    |      00100
    e     |      101      |   1100101    |      00101
    f     |      102      |   1100110    |      00110
    g     |      103      |   1100111    |      00111
    h     |      104      |   1101000    |      01000
    i     |      105      |   1101001    |      01001
    j     |      106      |   1101010    |      01010
    k     |      107      |   1101011    |      01011
    l     |      108      |   1101100    |      01100
    m     |      109      |   1101101    |      01101
    n     |      110      |   1101110    |      01110
    o     |      111      |   1101111    |      01111
    p     |      112      |   1110000    |      10000
    q     |      113      |   1110001    |      10001
    r     |      114      |   1110010    |      10010
    s     |      115      |   1110011    |      10011
    t     |      116      |   1110100    |      10100
    u     |      117      |   1110101    |      10101
    v     |      118      |   1110110    |      10110
    w     |      119      |   1110111    |      10111
    x     |      120      |   1111000    |      11000
    y     |      121      |   1111001    |      11001
    z     |      122      |   1111010    |      11010

在这里,您可以看到我们要映射的 ASCII 字符以第 7 位和第 6 位集 () 开头()(空格除外,它只打开了第 6 位)。你可以用()进行5位编码,这应该足以进行映射,但这对空间不起作用(的空间!11xxxxx₂OR9696₁₀ = 1100000₂

现在我们知道,必须特别注意与其他字符同时处理空间。为此,代码在提取的 5 位组上使用 OR 64 () 打开第 7 位(但不是第 6 位)。64₁₀ = 1000000₂l & 31 | 64

到目前为止,5 位组的形式为:(空格为 )。10xxxxx₂1011111₂ = 95₁₀

如果我们可以将空间映射到不受影响的其他值,那么我们可以打开第6位,这应该就是全部。0

这是该角色的作用。空间是,使用模运算。只有空格返回到 ,在此之后,代码通过添加到上一个结果来打开第 6 位,将 5 位值转换为有效的 ASCII 字符。mod 951011111₂ = 95₁₀(l & 31 | 64) % 95)032₁₀ = 100000₂((l & 31 | 64) % 95) + 32)

isolates 5 bits --+          +---- takes 'space' (and only 'space') back to 0
                  |          |
                  v          v
               (l & 31 | 64) % 95) + 32
                       ^           ^
       turns the       |           |
      7th bit on ------+           +--- turns the 6th bit on

下面的代码在给定小写字符串(最多 12 个字符)的情况下执行反向过程,返回可与 OP 代码一起使用的 64 位长值:

public class D {
    public static void main(String... args) {
        String v = "hello test";
        int len = Math.min(12, v.length());
        long res = 0L;
        for (int i = 0; i < len; i++) {
            long c = (long) v.charAt(i) & 31;
            res |= ((((31 - c) / 31) * 31) | c) << 5 * i;
        }
        System.out.println(res);
    }
}

答案 2

以下 Groovy 脚本打印中间值。

String getBits(long l) {
    return Long.toBinaryString(l).padLeft(8, '0');
}

for (long l = 4946144450195624l; l > 0; l >>= 5) {
    println ''
    print String.valueOf(l).toString().padLeft(16, '0')
    print '|' + getBits((l & 31))
    print '|' + getBits(((l & 31 | 64)))
    print '|' + getBits(((l & 31 | 64) % 95))
    print '|' + getBits(((l & 31 | 64) % 95 + 32))

    print '|';
    System.out.print((char) (((l & 31 | 64) % 95) + 32));
}

在这里:

4946144450195624|00001000|01001000|01001000|01101000|h
0154567014068613|00000101|01000101|01000101|01100101|e
0004830219189644|00001100|01001100|01001100|01101100|l
0000150944349676|00001100|01001100|01001100|01101100|l
0000004717010927|00001111|01001111|01001111|01101111|o
0000000147406591|00011111|01011111|00000000|00100000|
0000000004606455|00010111|01010111|01010111|01110111|w
0000000000143951|00001111|01001111|01001111|01101111|o
0000000000004498|00010010|01010010|01010010|01110010|r
0000000000000140|00001100|01001100|01001100|01101100|l
0000000000000004|00000100|01000100|01000100|01100100|d