Comment cela imprime “Bonjour tout le monde”?

J’ai découvert cette bizarrerie:

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

Sortie:

 hello world 

Comment cela marche-t-il?

Le nombre 4946144450195624 correspond à 64 bits, sa représentation binary est:

  10001100100100111110111111110111101100011000010101000 

Le programme décode un caractère pour chaque groupe de 5 bits, de droite à gauche

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

Codification 5 bits

Pour 5 bits, il est possible de représenter 2⁵ = 32 caractères. L’alphabet anglais contient 26 lettres, ce qui laisse de la place pour 32 – 26 = 6 symboles en dehors des lettres. Avec ce schéma de codification, vous pouvez avoir les 26 lettres anglaises (un cas) et les 6 symboles (y compris les espaces).

Description de l’algorithme

Le >>= 5 dans la boucle for saute de groupe à groupe, puis le groupe à 5 bits est isolé ET le nombre avec le masque 31₁₀ = 11111₂ dans la phrase l & 31

Maintenant, le code mappe la valeur 5 bits à son caractère ascii 7 bits correspondant. C’est la partie délicate, vérifiez les représentations binarys pour les lettres de l’alphabet minuscule dans le tableau suivant:

  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 

Ici, vous pouvez voir que les caractères ascii que nous voulons cartographier commencent avec le 7ème et le 6ème bit mis ( 11xxxxx₂ ) (sauf pour l’espace qui n’a que le 6ème bit 11xxxxx₂ ), vous pouvez OR la codification 5 bits avec 96 ( 96₁₀ = 1100000₂ ) et cela devrait être suffisant pour faire la cartographie, mais cela ne fonctionnerait pas pour l’espace (sacré!)

Maintenant, nous soaps que des précautions particulières doivent être sockets pour traiter l’espace en même temps que les autres personnages. Pour ce faire, le code active le 7ème bit (mais pas le 6ème) sur le groupe de 5 bits extrait avec un OR 64 64₁₀ = 1000000₂ ( l & 31 | 64 ).

Jusqu’à présent, le groupe à 5 bits est de la forme: 10xxxxx₂ (l’espace serait 1011111₂ = 95₁₀ ). Si nous pouvons mapper l’espace sur 0 autres valeurs non affectantes, alors nous pouvons activer le 6ème bit et cela devrait être tout. Voici ce que la partie mod 95 vient jouer, l’espace est 1011111₂ = 95₁₀ , en utilisant l’opération mod (l & 31 | 64) % 95) seul l’espace revient à 0 , après quoi le code active le 6ème bit de append 32₁₀ = 100000₂ au résultat précédent, ((l & 31 | 64) % 95) + 32) transformer la valeur de 5 bits en un caractère ascii valide

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

Le processus suivant, avec une chaîne de caractères minuscule (max 12 caractères), renvoie la valeur de 64 bits pouvant être utilisée avec le code de l’OP:

 public class D { public static void main(Ssortingng... args) { Ssortingng 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); } } 

Ajout d’une valeur aux réponses ci-dessus. Le script groovy suivant imprime les valeurs intermédiaires.

 Ssortingng getBits(long l) { return Long.toBinarySsortingng(l).padLeft(8,'0'); } for (long l = 4946144450195624l; l > 0; l >>= 5){ println '' print Ssortingng.valueOf(l).toSsortingng().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)); } 

C’est ici

 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 

Intéressant!

Les caractères ASCII standard visibles sont compris entre 32 et 127.

C’est pour cela que vous voyez 32 et 95 (127 – 32).

En fait, chaque caractère est mappé sur 5 bits ici (vous pouvez trouver la combinaison de 5 bits pour chaque caractère), puis tous les bits sont concaténés pour former un grand nombre.

Les positions longues positives sont des nombres de 63 bits, suffisamment grands pour contenir une forme cryptée de 12 caractères. Donc, il est assez grand pour contenir Hello word , mais pour les textes plus volumineux, vous utiliserez des nombres plus importants, ou même un BigInteger.


Dans une application, nous voulions transférer des caractères anglais, des caractères persans et des symboles visibles par SMS. Comme vous le voyez, il y a 32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127 valeurs possibles, qui peuvent être représentées avec 7 bits.

Nous avons converti chaque caractère UTF-8 (16 bits) en 7 bits et obtenu un taux de compression supérieur à 56%. Nous avons donc pu envoyer des textes avec une longueur double dans le même nombre de SMS. (C’est en quelque sorte la même chose qui s’est passée ici).

Vous obtenez un résultat qui se trouve être la représentation en char des valeurs ci-dessous

 104 -> h 101 -> e 108 -> l 108 -> l 111 -> o 32 -> (space) 119 -> w 111 -> o 114 -> r 108 -> l 100 -> d 

Vous avez encodé des caractères en tant que valeurs de 5 bits et en avez ajouté 11 en 64 bits.

(packedValues >> 5*i) & 31 est la ième valeur codée avec une plage de (packedValues >> 5*i) & 31 .

La partie difficile, comme vous le dites, est en train d’encoder l’espace. Les lettres anglaises minuscules occupent la plage contiguë 97-122 dans Unicode (et ascii, et la plupart des autres encodages), mais l’espace est 32.

Pour surmonter cela, vous avez utilisé de l’arithmétique. ((x+64)%95)+32 est presque identique à x + 96 (notez à quel point OR est équivalent à l’addition, dans ce cas), mais quand x = 31, nous obtenons 32 .

Il affiche “Bonjour tout le monde” pour une raison similaire:

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

mais pour une raison quelque peu différente de celle-ci:

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

Sans une balise Oracle , il était difficile de voir cette question. La prime active m’a amené ici. Je souhaite que la question ait également d’autres étiquettes technologiques pertinentes 🙁

Je travaille principalement avec la Oracle database , donc j’utiliserais des connaissances Oracle pour interpréter et expliquer 🙂

Convertissons le nombre 4946144450195624 en binary . Pour cela, j’utilise une petite function appelée dec2bin, c’est -à- dire décimal à binary .

 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> 

Utilisons la fonction pour obtenir la valeur binary –

 SQL> SELECT dec2bin(4946144450195624) FROM dual; DEC2BIN(4946144450195624) -------------------------------------------------------------------------------- 10001100100100111110111111110111101100011000010101000 SQL> 

Maintenant, la capture est la conversion de 5-bit . Commencez à grouper de droite à gauche avec 5 chiffres dans chaque groupe. On a :-

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

Nous serions finalement repartis avec seulement 3 chiffres à la fin. Parce que nous avions au total 53 chiffres dans la conversion binary.

 SQL> SELECT LENGTH(dec2bin(4946144450195624)) FROM dual; LENGTH(DEC2BIN(4946144450195624)) --------------------------------- 53 SQL> 

hello world total a 11 caractères (espace compris), nous devons donc append 2 bits au dernier groupe où il ne nous restait plus que 3 bits après le regroupement.

Donc, maintenant nous avons: –

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

Maintenant, nous devons le convertir en valeur ascii de 7 bits. Pour les personnages, c’est facile, il suffit de définir le 6ème et le 7ème bit. Ajouter 11 à chaque groupe de 5 bits ci-dessus à gauche.

Ça donne :-

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

Interprétons les valeurs binarys, j’utiliserai la 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> 

Regardons chaque valeur binary –

 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> 

Regardons quels sont les personnages: –

 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 --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- dlrow ⌂ olleh SQL> 

Alors, qu’est-ce qu’on obtient dans la sortie?

la peine

C’est bonjour le monde en sens inverse. Le seul problème est l’ espace . Et la raison est bien expliquée par @higuaro dans sa réponse. Honnêtement, je ne pouvais pas interpréter moi-même la question de l’espace au premier abord, jusqu’à ce que je voie l’explication donnée dans sa réponse.

J’ai trouvé le code légèrement plus facile à comprendre lorsqu’il était traduit en PHP, comme suit:

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

Voir le code en direct

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

Pour le rendre casquette: 3