java - reales - libro de android studio en español pdf
¿Cómo se imprime este "hola mundo"? (9)
Descubrí esta rareza:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
Salida:
hello world
¿Como funciona esto?
¡Interesante!
Los caracteres ASCII estándar que son visibles están en el rango de 32 a 127.
Es por eso que ves 32 y 95 (127 - 32) allí.
De hecho, cada carácter está mapeado 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 largos positivos son números de 63 bits, lo suficientemente grandes como para contener una forma encriptada de 12 caracteres. Por lo tanto, es lo suficientemente grande como para contener Hello word
, pero para textos más grandes debe usar números más grandes, o incluso un BigInteger.
En una aplicación, queríamos transferir caracteres ingleses visibles, 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) en 7 bits y obtuvimos una relación de compresión superior al 56%. Así que podríamos enviar mensajes de texto con el doble de duración en la misma cantidad de SMS. (De alguna manera, lo mismo sucedió aquí).
Agregar algún valor a las respuestas anteriores. Seguir una secuencia de comandos groovy 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
El código me resultó 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 en vivo
El número 4946144450195624
adapta a 64 bits, su representación binaria es:
10001100100100111110111111110111101100011000010101000
El programa decodifica un personaje por 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⁵ = 32 caracteres. El alfabeto inglés contiene 26 letras, esto deja espacio para 32 - 26 = 6 símbolos aparte de las letras. Con este esquema de codificación puede tener las 26 (un caso) letras inglesas y 6 símbolos (siendo espacio entre ellos).
Descripción del algoritmo
El >>= 5
en el for-loop salta de un grupo a otro, luego se aisla el grupo de 5 bits ANDing 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 ascii de 7 bits. Esta es la parte difícil, revise las representaciones binarias para las letras minúsculas 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í puede ver que los caracteres ascii que queremos asignar comienzan con el 7º y el 6º conjunto de bits ( 11xxxxx₂
) (excepto el espacio, que solo tiene el 6º bit 11xxxxx₂
), puede OR
la codificación de 5 bits 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 se debe tener especial cuidado para procesar el espacio al mismo tiempo que los otros personajes. Para lograr esto, el código activa el 7mo bit (pero no el 6to) en el grupo de 5 bits extraído con un OR 64 64₁₀ = 1000000₂
( l & 31 | 64
).
Hasta ahora, el grupo de 5 bits tiene la forma: 10xxxxx₂
(el espacio sería 1011111₂ = 95₁₀
). Si podemos mapear el espacio a 0
afectar otros valores, entonces podemos activar el 6to bit y eso debería ser todo. Esto es lo que viene a ser la parte del mod 95
, el espacio es 1011111₂ = 95₁₀
, usando la operación de mod (l & 31 | 64) % 95)
solo el espacio vuelve a 0
, y después de esto, el código activa el 6º bit agregando 32₁₀ = 100000₂
al resultado anterior, ((l & 31 | 64) % 95) + 32)
transformando el valor de 5 bits en un personaje ascii válido
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 que el proceso inverso, con una cadena en minúscula (máximo 12 caracteres), devuelva el valor largo de 64 bits 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);
}
}
Ha codificado caracteres como valores de 5 bits y ha empaquetado 11 de ellos en una longitud de 64 bits.
(packedValues >> 5*i) & 31
es el valor codificado en i-ésimo con un rango de 0-31.
La parte difícil, como dices, 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 demás 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
(nótese cómo es OR bit a bit es equivalente a la suma, en este caso), pero cuando x = 31, obtenemos 32
.
Imprime "hello world" 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 esto:
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));
Obtiene un resultado que resulta ser una representación de caracteres 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
Sin una etiqueta de Oracle
, era difícil ver esta pregunta. La recompensa activa me trajo aquí. Me gustaría que la pregunta tuviera también otras etiquetas tecnológicas relevantes :-(
Principalmente trabajo con la Oracle database
, así que usaré algunos conocimientos de Oracle
para interpretar y explicar :-)
Vamos a convertir el número 4946144450195624
en binary
. Para eso uso una pequeña function
llamada 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 de 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 de 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 tiene 11 caracteres (incluido el espacio), así que tenemos que agregar 2 bits al último grupo donde nos quedamos con solo 3 bits después de agruparlos.
Entonces, ahora tenemos:
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
Ahora, necesitamos convertirlo a un valor de ascii de 7 bits. Para los personajes es fácil, solo tenemos que establecer el 6º y el 7º bit. Agregue 11
a cada grupo de 5 bits arriba a la izquierda.
Eso da :-
1100100|1101100|1110010|1101111|1110111|1111111|1101111|1101100|1101100|1100101|1101000
Interpretemos los valores binarios, 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>
Miremos 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é personajes 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?
dlrow ⌂ olleh
Eso es hello⌂world en reversa. El único problema es el espacio . Y la razón está bien explicada por @higuaro en su respuesta. Honestamente, no pude interpretar el problema del espacio al primer intento, hasta que vi la explicación dada en su respuesta.
out.println ((char) (((l & 31 | 64)% 95) + 32/1002439 * 1002439));
Para hacerlo tapas: 3