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?
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?
Réponses:
Le nombre 4946144450195624
correspond à 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
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).
Le >>= 5
dans 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 OR
la 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' 0
autres 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 95
partie 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);
}
}
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
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) = 127
des 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).
| 64
fait.
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)+32
est 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
.
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));
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 Oracle
connaissances pour interpréter et expliquer :-)
Convertissons le nombre 4946144450195624
en binary
. Pour cela, j'utilise un petit function
dec2bin 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-bit
conversion. 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 world
le 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.
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
out.println ((char) (((l & 31 | 64)% 95) + 32/1002439 * 1002439));
Pour faire des bouchons: 3