Wie druckt dieser "Hallo Welt"?


163

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?


14
Ich meine, Sie können das selbst herausfinden.
Sotirios Delimanolis

30
Ja. Ich gebe zu ... ich fische nach Hüten :)
Bohemian

6
Ich glaube, ich habe diese Frage hier schon einmal gesehen.
Zavior

6
@Oli Dafür sollte es einen Hut geben.
Sotirios Delimanolis

12
Fragen wie diese, die die Datenbank nicht verbessern, sondern nur als Clickbait existieren, sind ein sicherer Weg, um das Hat-Spiel in Zukunft abzubrechen. Bitte ruinieren Sie das Spiel nicht, indem Sie es huren.
Blazemonger

Antworten:


256

Die Zahl 4946144450195624passt 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

5-Bit-Codierung

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.

Beschreibung des Algorithmus

Der >>= 5in 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 ORdie 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 0andere Werte abbilden können, können wir das 6. Bit einschalten, und das sollte alles sein. Hier ist, was die mod 95Rolle 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);
    }
}    

11
Diese Antwort lässt kein Geheimnis. Vielmehr erledigt es Ihr Denken für Sie.

7
Die Antwort ist noch schwieriger als die Frage: D
Yazan

1
Erklärung ist viel sauberer :)
Prashant

40

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

26

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) = 127mö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).


Im Code von OP ist noch viel mehr los. Zum Beispiel erklärt dies nicht wirklich, was der | 64tut.
Ted Hopp

1
@Amir: Eigentlich sind 95 da, weil du ein Leerzeichen brauchst.
Bee

17

Sie erhalten ein Ergebnis, das zufällig die folgenden charWerte darstellt

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

16

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)+32ist fast dasselbe wie x + 96(beachten Sie, wie bitweise ODER in diesem Fall der Addition entspricht), aber wenn x = 31 ist, erhalten wir 32.


6

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

18
Sie sollten erklären, was Sie tun, anstatt ein weiteres Rätsel zu stellen
Aleksandr Dubinsky

1
Ich schlage vor, dass Sie einige Anstrengungen unternehmen, um eine Site zu finden (vielleicht Beta StackExchange?), Auf der es Spaß macht, lustige Rätsel zu lösen. Stack Overflow ist eine Q & A-Site mit einem streng erzwungenen Fokus.
Marko Topolnik

1
@MarkoTopolnik Ich würde es hassen, in einer Welt zu leben, in der alle Regeln oder Schwerpunkte so streng durchgesetzt wurden, dass keine Ausnahmen zugelassen wurden. Ganz zu schweigen davon, dass es bei SO unzählige solcher Ausnahmen gibt.
ברקן

1
Ich würde auch, aber SO ist eine solche Welt in ungewöhnlich großem Maße. Sicher gibt es auch hier Ausnahmen, aber sie sind nicht willkommen .
Marko Topolnik

1
Weitere 15 teilten Alexandrs Meinung. Und Sie weisen zu Recht darauf hin, dass die Frage selbst für SO ungeeignet ist, wie unten kommentiert.
Marko Topolnik

3

Ohne ein OracleTag 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 OracleWissen verwenden, um zu interpretieren und zu erklären :-)

Lassen Sie uns die Zahl 4946144450195624in konvertieren binary. Dafür verwende ich ein kleines functionnamens 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-bitUmwandlung. 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 worldInsgesamt 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 11zu 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.


1

Ich fand den Code etwas einfacher zu verstehen, wenn er in PHP übersetzt wurde, wie folgt:

<?php

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

Siehe Live-Code


0

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

Um es zu verschließen: 3


1
Erwägen Sie eine Erklärung darüber, was Sie tun und warum.
Fedorqui 'SO hör auf zu schaden'
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.