Ich habe diese Kuriosität entdeckt:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
Ausgabe:
hello world
Wie funktioniert das?
Ich habe diese Kuriosität entdeckt:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
Ausgabe:
hello world
Wie funktioniert das?
Antworten:
Die Zahl 4946144450195624
passt auf 64 Bit, ihre binäre Darstellung lautet:
10001100100100111110111111110111101100011000010101000
Das Programm decodiert ein Zeichen für jede 5-Bit-Gruppe von rechts nach links
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
d | l | r | o | w | | o | l | l | e | h
Für 5 Bits ist es möglich, 2⁵ = 32 Zeichen darzustellen. Das englische Alphabet enthält 26 Buchstaben, dies lässt Platz für 32 - 26 = 6 Symbole außer Buchstaben. Mit diesem Kodifizierungsschema können Sie alle 26 (ein Fall) englischen Buchstaben und 6 Symbole (Leerzeichen zwischen ihnen) haben.
Der >>= 5
in der for-Schleife springt von Gruppe zu Gruppe, dann wird die 5-Bit-Gruppe isoliert UND die Zahl mit der Maske 31₁₀ = 11111₂
im Satz UND-verknüpftl & 31
Jetzt ordnet der Code den 5-Bit-Wert dem entsprechenden 7-Bit-ASCII-Zeichen zu. Dies ist der schwierige Teil. Überprüfen Sie die binären Darstellungen für die Kleinbuchstaben in der folgenden Tabelle:
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
Hier können Sie sehen, dass die ASCII-Zeichen, die wir zuordnen möchten, mit dem 7. und 6. Bit set ( 11xxxxx₂
) beginnen (mit Ausnahme des Leerzeichens, für das nur das 6. Bit aktiviert ist). Sie könnten OR
die 5-Bit-Codierung mit 96
( 96₁₀ = 1100000₂
) durchführen, und das sollte sein genug, um das Mapping durchzuführen, aber das würde für den Weltraum nicht funktionieren (verdammter Weltraum!)
Jetzt wissen wir, dass besondere Sorgfalt darauf verwendet werden muss, den Raum gleichzeitig mit den anderen Charakteren zu verarbeiten. Um dies zu erreichen, schaltet der Code das 7. Bit (aber nicht das 6.) der extrahierten 5-Bit-Gruppe mit einem ODER 64 64₁₀ = 1000000₂
( l & 31 | 64
) ein.
Bisher hat die 5-Bit-Gruppe die Form: 10xxxxx₂
(Leerzeichen wäre 1011111₂ = 95₁₀
). Wenn wir den Raum auf 0
andere Werte abbilden können, können wir das 6. Bit einschalten, und das sollte alles sein. Hier ist, was die mod 95
Rolle zu spielen kommt, Leerzeichen ist 1011111₂ = 95₁₀
, bei Verwendung der Mod-Operation (l & 31 | 64) % 95)
geht nur Leerzeichen zurück 0
, und danach schaltet der Code das 6. Bit ein, indem er 32₁₀ = 100000₂
zum vorherigen Ergebnis addiert ((l & 31 | 64) % 95) + 32)
und den 5-Bit-Wert in ein gültiges ASCII umwandelt Charakter
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
Der folgende Code führt den inversen Prozess aus, wobei bei einer Zeichenfolge in Kleinbuchstaben (max. 12 Zeichen) der 64-Bit-Wert zurückgegeben wird, der mit dem OP-Code verwendet werden kann:
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);
}
}
Hinzufügen eines Werts zu den obigen Antworten. Das folgende groovige Skript druckt Zwischenwerte.
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));
}
Hier ist es
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
Interessant!
Sichtbare Standard-ASCII-Zeichen liegen im Bereich von 32 bis 127.
Deshalb sehen Sie dort 32 und 95 (127 - 32).
Tatsächlich wird jedes Zeichen hier auf 5 Bits abgebildet (Sie können die 5-Bit-Kombination für jedes Zeichen ermitteln), und dann werden alle Bits zu einer großen Zahl verkettet.
Positive Longs sind 63-Bit-Zahlen, die groß genug sind, um eine verschlüsselte Form von 12 Zeichen aufzunehmen. Es ist also groß genug, um es aufzunehmen Hello word
, aber für größere Texte sollten Sie größere Zahlen oder sogar eine BigInteger verwenden.
In einer Anwendung wollten wir sichtbare englische Zeichen, persische Zeichen und Symbole per SMS übertragen. Wie Sie sehen, gibt es 32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127
mögliche Werte, die mit 7 Bits dargestellt werden können.
Wir haben jedes UTF-8-Zeichen (16 Bit) in 7 Bit konvertiert und ein Komprimierungsverhältnis von mehr als 56% erzielt. So konnten wir Texte mit doppelter Länge in der gleichen Anzahl von SMS senden. (Es ist irgendwie das gleiche passiert hier).
| 64
tut.
Sie haben Zeichen als 5-Bit-Werte codiert und 11 davon in eine 64-Bit-Länge gepackt.
(packedValues >> 5*i) & 31
ist der i-te codierte Wert mit einem Bereich von 0 bis 31.
Der schwierige Teil ist, wie Sie sagen, die Codierung des Raums. Die englischen Kleinbuchstaben belegen den zusammenhängenden Bereich 97-122 in Unicode (und ASCII und den meisten anderen Codierungen), aber der Abstand beträgt 32.
Um dies zu überwinden, haben Sie eine Arithmetik verwendet. ((x+64)%95)+32
ist fast dasselbe wie x + 96
(beachten Sie, wie bitweise ODER in diesem Fall der Addition entspricht), aber wenn x = 31 ist, erhalten wir 32
.
Es druckt "Hallo Welt" aus einem ähnlichen Grund:
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));
aber aus einem etwas anderen Grund als diesem:
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));
Ohne ein Oracle
Tag war diese Frage schwer zu erkennen. Aktives Kopfgeld brachte mich hierher. Ich wünschte, die Frage hätte auch andere relevante Technologie-Tags :-(
Ich arbeite hauptsächlich mit Oracle database
, also würde ich etwas Oracle
Wissen verwenden, um zu interpretieren und zu erklären :-)
Lassen Sie uns die Zahl 4946144450195624
in konvertieren binary
. Dafür verwende ich ein kleines function
namens dec2bin, dh 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>
Verwenden wir die Funktion, um den Binärwert zu erhalten -
SQL> SELECT dec2bin(4946144450195624) FROM dual;
DEC2BIN(4946144450195624)
--------------------------------------------------------------------------------
10001100100100111110111111110111101100011000010101000
SQL>
Jetzt ist der Haken die 5-bit
Umwandlung. Beginnen Sie mit der Gruppierung von rechts nach links mit 5 Ziffern in jeder Gruppe. Wir bekommen :-
100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
Wir würden endlich nur noch 3 Ziffern haben, wenn er rechts endet. Weil wir insgesamt 53 Stellen in der Binärkonvertierung hatten.
SQL> SELECT LENGTH(dec2bin(4946144450195624)) FROM dual;
LENGTH(DEC2BIN(4946144450195624))
---------------------------------
53
SQL>
hello world
Insgesamt hat 11 Zeichen (einschließlich Leerzeichen), daher müssen wir der letzten Gruppe, in der wir nach der Gruppierung nur noch 3 Bits übrig hatten , 2 Bits hinzufügen .
Also, jetzt haben wir: -
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
Jetzt müssen wir es in einen 7-Bit-ASCII-Wert konvertieren. Für die Charaktere ist es einfach, wir müssen nur das 6. und 7. Bit setzen. Fügen Sie 11
zu jeder 5-Bit-Gruppe oben links hinzu.
Das gibt :-
1100100|1101100|1110010|1101111|1110111|1111111|1101111|1101100|1101100|1100101|1101000
Lassen Sie uns die Binärwerte interpretieren, die ich verwenden werde 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>
Schauen wir uns jeden Binärwert an -
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>
Schauen wir uns an, welche Charaktere sie sind: -
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>
Also, was bekommen wir in der Ausgabe?
dlrow ⌂ olleh
Das ist Hallo Welt in umgekehrter Richtung. Das einzige Problem ist der Platz . Und der Grund wird von @higuaro in seiner Antwort gut erklärt. Ich konnte das Raumproblem ehrlich gesagt beim ersten Versuch nicht selbst interpretieren, bis ich die Erklärung in seiner Antwort sah.
out.println ((char) (((l & 31 | 64)% 95) + 32/1002439 * 1002439));
Um es zu verschließen: 3