Comment cette impression "bonjour le monde"?


163

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?


14
Je veux dire que vous pouvez comprendre cela vous-même.
Sotirios Delimanolis

30
Oui. J'avoue ... je pêche des chapeaux :)
Bohème

6
Je pense avoir déjà vu cette question posée ici ..
Zavior

6
@Oli Il devrait y avoir un chapeau pour ça.
Sotirios Delimanolis

12
Des questions comme celle-ci, qui n'améliorent pas la base de données mais n'existent que sous forme d'appât, sont un moyen infaillible d'annuler le jeu Hat à l'avenir. Veuillez ne pas ruiner le jeu en se prostituant.
Blazemonger

Réponses:


256

Le nombre 4946144450195624correspond à 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, cela 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 (une seule casse) et 6 symboles (en étant un espace parmi eux).

Description de l'algorithme

Le >>= 5dans la boucle for saute de groupe en groupe, puis le groupe de 5 bits est isolé ET en ajoutant le numéro avec le masque 31₁₀ = 11111₂dans la phrasel & 31

Maintenant, le code mappe la valeur 5 bits à son caractère ascii 7 bits correspondant. C'est la partie la plus délicate, vérifiez les représentations binaires pour les lettres minuscules de l'alphabet 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 le jeu de 7e et 6e bits ( 11xxxxx₂) (sauf pour l'espace, qui n'a que le 6e bit activé), vous pouvez ORla codification 5 bits avec 96( 96₁₀ = 1100000₂) et cela devrait être assez pour faire le mappage, mais cela ne fonctionnerait pas pour l'espace (sacré espace!)

Maintenant, nous savons qu'il faut prendre un soin particulier pour traiter l'espace en même temps que les autres caractères. 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 de 5 bits est de la forme: 10xxxxx₂(l'espace serait 1011111₂ = 95₁₀). Si nous pouvons mapper l'espace à d' 0autres valeurs qui n'affectent pas d'autres valeurs, alors nous pouvons activer le 6ème bit et ce devrait être tout. Voici ce que la mod 95partie vient de jouer, l'espace est 1011111₂ = 95₁₀, en utilisant l'opération de mod, (l & 31 | 64) % 95)seul l'espace retourne 0, et après cela, le code active le 6ème bit en ajoutant 32₁₀ = 100000₂ au résultat précédent, ((l & 31 | 64) % 95) + 32)transformant la valeur 5 bits en un ascii valide personnage

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), retourne la valeur 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);
    }
}    

11
Cette réponse ne laisse aucun mystère. Au contraire, cela fait votre réflexion pour vous.

7
la réponse est encore plus difficile que la question: D
Yazan

1
L'explication est beaucoup plus propre :)
Prashant

40

Ajout de la valeur aux réponses ci-dessus. Le script groovy suivant imprime les 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

26

Intéressant!

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

C'est pourquoi vous voyez 32 et 95 (127 - 32) ici.

En fait, chaque caractère est mappé ici sur 5 bits (vous pouvez trouver ce qu'est une 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 des textes plus volumineux, vous utiliserez des nombres plus grands, voire un BigInteger.


Dans une application, nous voulions transférer des caractères anglais visibles, des caractères persans et des symboles via SMS. Comme vous le voyez, il existe 32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127des 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 de plus de 56%. Nous pourrions donc envoyer des SMS avec deux fois la longueur dans le même nombre de SMS. (C'est en quelque sorte la même chose qui s'est produite ici).


Il y a beaucoup plus de choses dans le code d'OP. Par exemple, cela n'explique pas vraiment ce qu'il | 64fait.
Ted Hopp

1
@Amir: en fait 95 est là parce que vous avez besoin d'un espace.
Bee

17

Vous obtenez un résultat qui se trouve être une charrepré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

16

Vous avez encodé des caractères sous forme de valeurs 5 bits et en avez compressé 11 en 64 bits.

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

Le plus dur, comme vous le dites, est d'encoder l'espace. Les lettres minuscules anglaises occupent la plage contiguë 97-122 en Unicode (et ascii, et la plupart des autres encodages), mais l'espace est de 32.

Pour surmonter cela, vous avez utilisé une certaine arithmétique. ((x+64)%95)+32est presque le même que x + 96(notez à quel point OR au niveau du bit est équivalent à l'addition, dans ce cas), mais lorsque x = 31, nous obtenons 32.


6

Il imprime "hello world" 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));

18
Vous devriez expliquer ce que vous faites, au lieu de publier une autre énigme
Aleksandr Dubinsky

1
Je vous suggère d'investir un peu d'efforts dans la recherche d'un site (peut-être un Beta StackExchange?) Où contribuer des énigmes amusantes est le bienvenu. Stack Overflow est un site de questions-réponses avec un objectif strictement appliqué.
Marko Topolnik

1
@MarkoTopolnik Je détesterais vivre dans un monde où toutes les règles ou toutes les priorités étaient si strictement appliquées qu'elles ne permettent aucune exception. Sans oublier qu'il existe d'innombrables exceptions de ce type sur SO.
גלעד ברקן

1
Moi aussi, mais SO est un tel monde dans une mesure inhabituellement grande. Bien sûr, il y a des exceptions même ici, mais elles ne sont pas les bienvenues .
Marko Topolnik

1
Quinze autres partageaient le sentiment d'Alexandr. Et vous avez raison de souligner que la question elle-même est inappropriée pour SO, comme indiqué ci-dessous.
Marko Topolnik

3

Sans Oracleétiquette, il était difficile de voir cette question. La prime active m'a amené ici. Je souhaite que la question ait également d'autres balises technologiques pertinentes :-(

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

Convertissons le nombre 4946144450195624en binary. Pour cela, j'utilise un petit functiondec2bin appelé decimal-to-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 binaire -

SQL> SELECT dec2bin(4946144450195624) FROM dual;

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

SQL>

Maintenant, le hic, c'est la 5-bitconversion. Commencez à regrouper 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 laissés avec seulement 3 chiffres entiers à droite. Parce que nous avons eu un total de 53 chiffres dans la conversion binaire.

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

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

SQL>

hello worldle total a 11 caractères (y compris l'espace), nous devons donc ajouter 2 bits au dernier groupe où il ne nous restait 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 7 bits. Pour les personnages, c'est facile, il suffit de définir le 6ème et le 7ème bit. Ajoutez 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 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>

Regardons quels personnages ils sont: -

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?

dlrow ⌂ olleh

C'est hello⌂world à l'envers. 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 du premier coup, jusqu'à ce que je voie l'explication donnée dans sa réponse.


1

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 le code en direct


0

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

Pour faire des bouchons: 3


1
pensez à ajouter des explications sur ce que vous faites et pourquoi.
fedorqui 'SO arrêtez de nuire'
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.