¿Cómo se imprime "hola mundo"?


Descubrí esta rareza:

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

Salida:

hello world

¿Cómo funciona esto?

Author: Bohemian, 2013-12-21

9 answers

El número 4946144450195624 se ajusta a 64 bits, su representación binaria es:

 10001100100100111110111111110111101100011000010101000

El programa decodifica un carácter para cada grupo de 5 bits, de derecha a izquierda

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

Codificación de 5 bits

Para 5 bits, es posible representar 2 characters = 32 caracteres. Alfabeto inglés contiene 26 letras, esto deja espacio para 32-26 = 6 símbolos aparte de las cartas. Con este esquema de codificación puede tener todas las 26 (un caso) letras inglesas y 6 símbolos (siendo el espacio entre ellos).

Descripción del algoritmo

El >>= 5 en el bucle for salta de grupo en grupo, luego el grupo de 5 bits se aísla y el número con la máscara 31₁₀ = 11111₂ en la oración l & 31

Ahora el código asigna el valor de 5 bits a su correspondiente carácter ascii de 7 bits. Esta es la parte difícil, compruebe las representaciones binarias para las minúsculas letras del alfabeto en la siguiente tabla:

  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

Aquí puedes ver que los caracteres ascii que queremos mapear comienzan con el 7º y 6º bit establecido (11xxxxx₂) (a excepción del espacio, que solo tiene el 6º bit activado), podrías OR el 5-bit codificación con 96 (96₁₀ = 1100000₂) y eso debería ser suficiente para hacer el mapeo, pero eso no funcionaría para el espacio (¡maldito espacio!)

Ahora sabemos que hay que tener especial cuidado para procesar el espacio al mismo tiempo que los otros caracteres. Para lograr esto, el código activa el bit 7 (pero no el 6) el grupo de 5 bits extraído con un OR 64 64₁₀ = 1000000₂ (l & 31 | 64).

Hasta ahora el grupo de 5 bits es de la forma: 10xxxxx₂ (el espacio sería 1011111₂ = 95₁₀). Si podemos asignar espacio a 0 sin afectar a otros valores, entonces podemos activar el bit 6 y eso debería ser todo. Esto es lo que viene a jugar la parte mod 95, el espacio es 1011111₂ = 95₁₀, usando el mod operación (l & 31 | 64) % 95) solo el espacio vuelve a 0, y después de esto, el código enciende el 6to bit agregando 32₁₀ = 100000₂ al resultado anterior, ((l & 31 | 64) % 95) + 32) transformando el valor de 5 bits en un ascii válido carácter

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

El siguiente código hace el proceso inverso, dada una cadena minúscula (máximo 12 caracteres), devuelve el valor de 64 bits de largo que podría usarse con el código del OP:

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);
    }
}    
 249
Author: higuaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-24 07:36:17

Añadiendo algún valor a las respuestas anteriores. Siguiendo groovy script imprime valores intermedios.

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));
}

Aquí está

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
 39
Author: Jayan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-21 17:01:00

Interesante!

Los caracteres ASCII estándar que son visibles están en un rango de 32 a 127.

Es por eso que ves 32, y 95 (127 - 32) allí.

De hecho, cada carácter se asigna a 5 bits aquí, (puede encontrar lo que es una combinación de 5 bits para cada carácter), y luego todos los bits se concatenan para formar un gran número.

Los longs positivos son números de 63 bits, lo suficientemente grandes como para contener una forma cifrada de 12 caracteres. Por lo tanto, es lo suficientemente grande como para contener Hello word, pero para textos más grandes usted debe utilizar números más grandes, o incluso un BigInteger.


En una aplicación queríamos transferir caracteres visibles en Inglés, Caracteres Persas y Símbolos a través de SMS. Como puede ver, hay 32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127 valores posibles, que se pueden representar con 7 bits.

Convertimos cada carácter UTF-8 (16 bits) a 7 bits, y ganamos más del 56% de relación de compresión. Así que podríamos enviar textos con dos veces de longitud en el mismo número de SMS. (Es de alguna manera lo mismo sucedió aquí).

 26
Author: Amir Pashazadeh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-21 15:16:55

Está obteniendo un resultado que resulta ser char representación de los valores inferiores

104 -> h
101 -> e
108 -> l
108 -> l
111 -> o
32  -> (space)
119 -> w
111 -> o
114 -> r
108 -> l
100 -> d
 17
Author: Vikas V,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-21 15:17:40

Ha codificado caracteres como valores de 5 bits y empaquetado 11 de ellos en un largo de 64 bits.

(packedValues >> 5*i) & 31 es el i-ésimo valor codificado con un rango 0-31.

La parte difícil, como usted dice, es codificar el espacio. Las letras inglesas minúsculas ocupan el rango contiguo 97-122 en Unicode (y ascii, y la mayoría de las otras codificaciones), pero el espacio es 32.

Para superar esto, usaste algo de aritmética. ((x+64)%95)+32 es casi lo mismo que x + 96 (tenga en cuenta cómo bitwise OR es equivalente a la suma, en este case), pero cuando x=31, obtenemos 32.

 16
Author: Aleksandr Dubinsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-24 00:03:27

Imprime "hola mundo" por una razón similar:

for (int k=1587463874; k>0; k>>=3)
     System.out.print((char) (100 + Math.pow(2,2*(((k&7^1)-1)>>3 + 1) + (k&7&3)) + 10*((k&7)>>2) + (((k&7)-7)>>3) + 1 - ((-(k&7^5)>>3) + 1)*80));

Pero por una razón algo diferente a esta:

for (int k=2011378; k>0; k>>=2)
    System.out.print((char) (110 + Math.pow(2,2*(((k^1)-1)>>21 + 1) + (k&3)) - ((k&8192)/8192 + 7.9*(-(k^1964)>>21) - .1*(-((k&35)^35)>>21) + .3*(-((k&120)^120)>>21) + (-((k|7)^7)>>21) + 9.1)*10));
 6
Author: גלעד ברקן,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-16 00:11:30

Sin una etiqueta Oracle, era difícil ver esta pregunta. Active bounty me trajo aquí. Desearía que la pregunta tuviera otras etiquetas tecnológicas relevantes también : - (

Trabajo principalmente con Oracle database, así que usaría algo de Oracle conocimiento para interpretar y explicar : -)

Vamos a convertir el número 4946144450195624 en binary. Para eso utilizo un pequeño functionllamado dec2bin, es decir, decimal a binario .

SQL> CREATE OR REPLACE FUNCTION dec2bin (N in number) RETURN varchar2 IS
  2    binval varchar2(64);
  3    N2     number := N;
  4  BEGIN
  5    while ( N2 > 0 ) loop
  6       binval := mod(N2, 2) || binval;
  7       N2 := trunc( N2 / 2 );
  8    end loop;
  9    return binval;
 10  END dec2bin;
 11  /

Function created.

SQL> show errors
No errors.
SQL>

Usemos la función para obtener el valor binario -

SQL> SELECT dec2bin(4946144450195624) FROM dual;

DEC2BIN(4946144450195624)
--------------------------------------------------------------------------------
10001100100100111110111111110111101100011000010101000

SQL>

Ahora la captura es la conversión 5-bit. Comience a agrupar de derecha a izquierda con 5 dígitos en cada grupo. Obtenemos :-

100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000

Finalmente nos quedaríamos con solo 3 dígitos en el final a la derecha. Porque, tuvimos un total de 53 dígitos en la conversión binaria.

SQL> SELECT LENGTH(dec2bin(4946144450195624)) FROM dual;

LENGTH(DEC2BIN(4946144450195624))
---------------------------------
                               53

SQL>

hello world total ha 11 caracteres (incluyendo el espacio), por lo que tenemos que añadir 2 bits al último grupo donde nos quedamos con solo 3 bits después de la agrupación.

So, ahora tenemos :-

00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000

Ahora, necesitamos convertirlo al valor ascii de 7 bits. Para los personajes es fácil, solo tenemos que establecer el bit 6 y 7. Agregue 11 a cada grupo de 5 bits arriba a la izquierda.

Que da: -

1100100|1101100|1110010|1101111|1110111|1111111|1101111|1101100|1101100|1100101|1101000

Interpretemos los valores binarios, usaré binary to decimal conversion function.

SQL> CREATE OR REPLACE FUNCTION bin2dec (binval in char) RETURN number IS
  2    i                 number;
  3    digits            number;
  4    result            number := 0;
  5    current_digit     char(1);
  6    current_digit_dec number;
  7  BEGIN
  8    digits := length(binval);
  9    for i in 1..digits loop
 10       current_digit := SUBSTR(binval, i, 1);
 11       current_digit_dec := to_number(current_digit);
 12       result := (result * 2) + current_digit_dec;
 13    end loop;
 14    return result;
 15  END bin2dec;
 16  /

Function created.

SQL> show errors;
No errors.
SQL>

Veamos cada valor binario -

SQL> set linesize 1000
SQL>
SQL> SELECT bin2dec('1100100') val,
  2    bin2dec('1101100') val,
  3    bin2dec('1110010') val,
  4    bin2dec('1101111') val,
  5    bin2dec('1110111') val,
  6    bin2dec('1111111') val,
  7    bin2dec('1101111') val,
  8    bin2dec('1101100') val,
  9    bin2dec('1101100') val,
 10    bin2dec('1100101') val,
 11    bin2dec('1101000') val
 12  FROM dual;

       VAL        VAL        VAL        VAL        VAL        VAL        VAL        VAL        VAL     VAL           VAL
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
       100        108        114        111        119        127        111        108        108     101           104

SQL>

Veamos qué caracteres son: -

SQL> SELECT chr(bin2dec('1100100')) character,
  2    chr(bin2dec('1101100')) character,
  3    chr(bin2dec('1110010')) character,
  4    chr(bin2dec('1101111')) character,
  5    chr(bin2dec('1110111')) character,
  6    chr(bin2dec('1111111')) character,
  7    chr(bin2dec('1101111')) character,
  8    chr(bin2dec('1101100')) character,
  9    chr(bin2dec('1101100')) character,
 10    chr(bin2dec('1100101')) character,
 11    chr(bin2dec('1101000')) character
 12  FROM dual;

CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER
--------- --------- --------- --------- --------- --------- --------- --------- --------- --------- ---------
d         l         r         o         w         ⌂         o         l         l         e         h

SQL>

Entonces, ¿qué obtenemos en la salida?

D l r o w {o l l e h

Eso es hola world mundo al revés. El único problema es el espacio . Y la razón está bien explicada por @higuaro en su respuesta. Honestamente no pude interpretar el tema del espacio yo mismo al primer intento, hasta que vi la explicación dada en su respuesta.

 2
Author: Lalit Kumar B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-30 12:31:08

Encontré el código un poco más fácil de entender cuando se traduce a PHP, de la siguiente manera:

<?php

$result=0;
$bignum = 4946144450195624;
for (; $bignum > 0; $bignum >>= 5){
    $result = (( $bignum & 31 | 64) % 95) + 32;
    echo chr($result);
}

Ver código vivo

 1
Author: slevy1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-24 21:16:07

Fuera.println ((char) ((l & 31 | 64) % 95) + 32 / 1002439 * 1002439));

Para hacer que las tapas: 3

 0
Author: Elliot Bewey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-05 18:47:54