web-dev-qa-db-fra.com

Comment ça imprime "bonjour 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));

Production:

hello world

Comment cela marche-t-il?

161
Bohemian

Le nombre 4946144450195624 s'adapte à 64 bits, sa représentation binaire 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 6 symboles (en étant un espace parmi eux).

Description de l'algorithme

Le >>= 5 dans les sauts de boucle de groupe à groupe, puis le groupe de 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 binaires pour les lettres de l'alphabet en minuscules 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 mapper commencent par les 7e et 6e bits (11xxxxx₂) (sauf pour l'espace, qui n'a que le 6ème bit activé), vous pourriez OR la codification 5 bits avec 96 (96₁₀ = 1100000₂) et cela devrait être suffisant pour faire le mapping, mais cela ne fonctionnerait pas pour l'espace (sacrément d'espace!)

Nous savons maintenant qu'il faut prendre un soin particulier à traiter l'espace en même temps que les autres personnages. Pour ce faire, le code active le 7e bit (mais pas le 6e) sur le groupe 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 sans affecter les autres valeurs, nous pouvons alors activer le 6ème bit et ce devrait être tout. Voici ce que le mod 95 une partie vient jouer, l'espace est 1011111₂ = 95₁₀, en utilisant l'opération mod (l & 31 | 64) % 95) seul l'espace revient à 0, et après cela, le code active le 6e bit en ajoutant 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
                  |          |
                  v          v
               (l & 31 | 64) % 95) + 32
                       ^           ^ 
       turns the       |           |
      7th bit on ------+           +--- turns the 6th bit on

Le code suivant effectue le processus inverse, étant donné une chaîne en minuscules (max 12 caractères), renvoie la valeur longue de 64 bits qui pourrait être utilisée avec le code de l'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);
    }
}    
254
higuaro

Ajouter de la valeur aux réponses ci-dessus. Le script groovy suivant imprime des valeurs intermédiaires.

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

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
39
Jayan

Intéressant!

Standard ASCII les caractères visibles sont compris entre 32 et 127.

C'est pourquoi vous voyez 32 et 95 (127 - 32) là-bas.

En fait, chaque caractère est mappé à 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 longs positifs sont des nombres de 63 bits, suffisamment grands pour contenir une forme cryptée de 12 caractères. Il est donc assez grand pour contenir Hello Word, Mais pour les textes plus gros, vous devez utiliser des nombres plus grands, ou même un BigInteger.


Dans une application, nous voulions transférer des caractères anglais, des caractères persans et des symboles visibles via SMS. Comme vous le voyez, il existe 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 avons obtenu un taux de compression supérieur à 56%. Nous avons donc pu envoyer des textes d'une longueur double dans le même nombre de SMS. (C'est en quelque sorte la même chose qui s'est produite ici).

26
Amir Pashazadeh

Vous obtenez un résultat qui se trouve être char représentation 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
17
Vikas V

Vous avez encodé des caractères en valeurs 5 bits et en avez regroupé 11 en 64 bits.

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

La partie difficile, comme vous le dites, est l'encodage de l'espace. Les lettres anglaises minuscules occupent la plage contiguë 97-122 en Unicode (et ascii, et la plupart des autres codages), mais l'espace est de 32.

Pour surmonter cela, vous avez utilisé de l'arithmétique. ((x+64)%95)+32 est presque identique à x + 96 (notez comment au niveau du bit OR est équivalent à l'addition, dans ce cas), mais lorsque x = 31, nous obtenons 32.

16
Aleksandr Dubinsky

Il imprime "bonjour 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));
6
גלעד ברקן

Sans balise Oracle, il était difficile de voir cette question. Une prime active m'a amené ici. Je souhaite que la question contienne également d'autres balises technologiques pertinentes :

Je travaille principalement avec Oracle database, donc j'utiliserais des connaissances Oracle pour interpréter et expliquer :-)

Convertissons le nombre 4946144450195624 en binary. Pour cela, j'utilise un petit function appelé dec2bin c'est-à-dire --- (décimal en binaire.

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 binaire -

SQL> SELECT dec2bin(4946144450195624) FROM dual;

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

SQL>

Maintenant, la capture est le 5-bit conversion. 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 nous retrouverions finalement avec juste chiffres dans la fin à droite. Parce que, nous avions un total de 53 chiffres dans la conversion binaire.

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

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

SQL>

hello world total a 11 caractères (espace compris), nous devons donc ajouter 2 bits au dernier groupe où nous nous sommes retrouvés avec seulement 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 7 bits. Pour les personnages c'est facile, il suffit de régler les 6e et 7e bits. Ajouter 11 à chaque groupe de 5 bits au-dessus à gauche.

Ça donne :-

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

Interprétons les valeurs binaires, j'utiliserai 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 binaire -

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>

Voyons quels sont ces 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
--------- --------- --------- --------- --------- --------- --------- --------- --------- --------- ---------
d         l         r         o         w         ⌂         o         l         l         e         h

SQL>

Alors, qu'obtenons-nous dans la sortie?

d l r o w ⌂ o l l e h

C'est hello⌂world à l'envers. Le seul problème est le espace. Et la raison est bien expliquée par @higuaro dans sa réponse. Honnêtement, je ne pouvais pas interpréter le problème d'espace moi-même à la première tentative, jusqu'à ce que je voie l'explication donnée dans sa réponse.

3
Lalit Kumar B

J'ai trouvé le code légèrement plus facile à comprendre lorsqu'il est 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 code en direct

1
slevy1

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

Pour faire des bouchons: 3

0
user4329506