In che modo questa stampa "ciao mondo"?


163

Ho scoperto questa stranezza:

for (long l = 4946144450195624l; l > 0; l >>= 5)
    System.out.print((char) (((l & 31 | 64) % 95) + 32));

Produzione:

hello world

Come funziona?


14
Voglio dire, puoi capirlo da solo.
Sotirios Delimanolis,

30
Sì. Lo ammetto ... sto pescando cappelli :)
Boemia

6
Penso di aver visto questa domanda qui prima ...
Zavior,

6
@Oli Dovrebbe esserci un cappello per quello.
Sotirios Delimanolis,

12
Domande come questa, che non migliorano il database ma esistono esclusivamente come clickbait, sono un modo sicuro per far annullare il gioco Hat in futuro. Per favore, non rovinare il gioco prostituendoti.
Blazemonger,

Risposte:


256

Il numero si 4946144450195624adatta a 64 bit, la sua rappresentazione binaria è:

 10001100100100111110111111110111101100011000010101000

Il programma decodifica un carattere per ogni gruppo a 5 bit, da destra a sinistra

 00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
   d  |  l  |  r  |  o  |  w  |     |  o  |  l  |  l  |  e  |  h

Codificazione a 5 bit

Per 5 bit, è possibile rappresentare 2⁵ = 32 caratteri. L'alfabeto inglese contiene 26 lettere, questo lascia spazio a 32 - 26 = 6 simboli a parte le lettere. Con questo schema di codificazione puoi avere tutte e 26 le lettere inglesi (un caso) e 6 simboli (tra cui lo spazio).

Descrizione dell'algoritmo

Il >>= 5nel ciclo for salta da un gruppo all'altro, quindi il gruppo a 5 bit viene isolato ANDing il numero con la maschera 31₁₀ = 11111₂nella frasel & 31

Ora il codice mappa il valore a 5 bit sul corrispondente carattere ASCII a 7 bit. Questa è la parte difficile, controlla le rappresentazioni binarie per le lettere dell'alfabeto minuscole nella seguente tabella:

  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

Qui puoi vedere che i caratteri ASCII che vogliamo mappare iniziano con il settimo e il sesto bit set ( 11xxxxx₂) (ad eccezione dello spazio, che ha solo il sesto bit attivato), puoi ORcodificare a 5 bit con 96(96₁₀ = 1100000₂ ) e dovrebbe essere abbastanza per fare la mappatura, ma che non funzionerebbe per lo spazio (maledetto spazio!)

Ora sappiamo che è necessario prestare particolare attenzione a elaborare lo spazio contemporaneamente agli altri personaggi. Per ottenere ciò, il codice attiva il 7o bit (ma non il 6o) sul gruppo a 5 bit estratto con un OR 64 64₁₀ = 1000000₂( l & 31 | 64).

Finora il gruppo a 5 bit ha la forma: 10xxxxx₂(lo spazio sarebbe 1011111₂ = 95₁₀). Se siamo in grado di mappare lo spazio senza 0influenzare altri valori, allora possiamo attivare il sesto bit e dovrebbe essere tutto. Ecco a cosa serve la mod 95parte, lo spazio è 1011111₂ = 95₁₀, usando l'operazione mod (l & 31 | 64) % 95)torna solo lo spazio 0, e dopo questo, il codice attiva il sesto bit aggiungendo 32₁₀ = 100000₂ al risultato precedente, ((l & 31 | 64) % 95) + 32)trasformando il valore a 5 bit in un ASCII valido carattere

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

Il seguente codice esegue il processo inverso, data una stringa minuscola (max 12 caratteri), restituisce il valore lungo 64 bit che potrebbe essere utilizzato con il codice 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
Questa risposta non lascia mistero. Piuttosto, fa il tuo pensiero per te.

7
la risposta è ancora più difficile della domanda: D
Yazan,

1
La spiegazione è molto più pulita :)
Prashant

40

Aggiungendo un valore alle risposte sopra. Lo script groovy che segue stampa valori intermedi.

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

Ecco qui

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

Interessante!

I caratteri ASCII standard che sono visibili sono compresi tra 32 e 127.

Ecco perché vedi 32 e 95 (127 - 32) lì.

In effetti, ogni carattere è mappato su 5 bit qui (è possibile trovare una combinazione di 5 bit per ciascun carattere), quindi tutti i bit vengono concatenati per formare un numero elevato.

I long positivi sono numeri a 63 bit, abbastanza grandi da contenere una forma criptata di 12 caratteri. Quindi è abbastanza grande da contenere Hello word, ma per testi più grandi dovrai usare numeri più grandi o anche un BigInteger.


In un'applicazione volevamo trasferire caratteri inglesi, caratteri persiani e simboli visibili tramite SMS. Come vedi ci sono 32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127possibili valori, che possono essere rappresentati con 7 bit.

Abbiamo convertito ogni carattere UTF-8 (16 bit) in 7 bit e abbiamo ottenuto un rapporto di compressione superiore al 56%. Quindi potremmo inviare messaggi di lunghezza doppia nello stesso numero di SMS. (In qualche modo è successa la stessa cosa qui).


C'è molto altro da fare nel codice di OP. Ad esempio, questo non spiega davvero cosa | 64sta facendo.
Ted Hopp,

1
@Amir: in realtà 95 è lì perché devi ottenere un personaggio spaziale.
Bee,

17

Stai ottenendo un risultato che sembra essere la charrappresentazione dei valori seguenti

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

16

Hai codificato i caratteri come valori a 5 bit e ne hai confezionati 11 in 64 bit.

(packedValues >> 5*i) & 31 è l'i-esimo valore codificato con un intervallo 0-31.

La parte difficile, come dici tu, è codificare lo spazio. Le lettere inglesi minuscole occupano l'intervallo contiguo 97-122 in Unicode (e ascii e la maggior parte delle altre codifiche), ma lo spazio è 32.

Per ovviare a questo, hai usato un po 'di aritmetica. ((x+64)%95)+32è quasi uguale a x + 96(nota come OR bit per bit è equivalente all'aggiunta, in questo caso), ma quando x = 31, otteniamo 32.


6

Stampa "ciao mondo" per un motivo simile che fa:

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

ma per una ragione leggermente diversa da questa:

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
Dovresti spiegare cosa stai facendo, invece di pubblicare un altro indovinello
Aleksandr Dubinsky il

1
Ti suggerisco di investire qualche sforzo nella ricerca di un sito (forse un po 'di Beta StackExchange?) In cui contribuire con indovinelli divertenti è il benvenuto. Stack Overflow è un sito di domande e risposte con focus rigorosamente applicato.
Marko Topolnik,

1
@MarkoTopolnik Odierei vivere in un mondo in cui tutte le regole o il focus sono stati applicati in modo così rigoroso da non permettere mai eccezioni. Per non parlare del fatto che ci sono innumerevoli eccezioni di questo tipo.
גלעד ברקן

1
Vorrei anche io, ma SO è un mondo così insolitamente grande. Certo ci sono eccezioni anche qui, ma non sono benvenute .
Marko Topolnik,

1
Altri 15 condividevano il sentimento di Alexandr. E hai ragione nel sottolineare che la domanda stessa è inappropriata per SO, come commentato di seguito.
Marko Topolnik,

3

Senza un Oracletag, era difficile vedere questa domanda. La generosità attiva mi ha portato qui. Vorrei che la domanda avesse anche altri tag tecnologici rilevanti :-(

Lavoro principalmente con Oracle database, quindi vorrei usare alcune Oracleconoscenze per interpretare e spiegare :-)

Convertiamo il numero 4946144450195624in binary. Per questo uso un piccolo functionchiamato dec2bin, cioè da decimale a binario .

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>

Usiamo la funzione per ottenere il valore binario -

SQL> SELECT dec2bin(4946144450195624) FROM dual;

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

SQL>

Ora il trucco è la 5-bitconversione. Inizia a raggruppare da destra a sinistra con 5 cifre in ciascun gruppo. Noi abbiamo :-

100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000

Alla fine saremmo rimasti con solo 3 cifre alla fine a destra. Perché, abbiamo avuto un totale di 53 cifre nella conversione binaria.

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

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

SQL>

hello worldil totale ha 11 caratteri (incluso lo spazio), quindi dobbiamo aggiungere 2 bit all'ultimo gruppo dove eravamo rimasti con solo 3 bit dopo il raggruppamento.

Quindi, ora abbiamo: -

00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000

Ora, dobbiamo convertirlo in valore ASCII a 7 bit. Per i personaggi è facile, dobbiamo solo impostare il 6 ° e il 7 ° bit. Aggiungi 11a ciascun gruppo a 5 bit in alto a sinistra.

Questo dà: -

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

Interpretiamo i valori binari, userò 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>

Diamo un'occhiata a ciascun valore binario -

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>

Diamo un'occhiata a quali personaggi sono: -

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>

Quindi, cosa otteniamo nell'output?

dlrow ⌂ olleh

Questo è ciao-mondo al contrario. L'unico problema è lo spazio . E il motivo è ben spiegato da @higuaro nella sua risposta. Onestamente non ho potuto interpretare il problema dello spazio da solo al primo tentativo, fino a quando non ho visto la spiegazione fornita nella sua risposta.


1

Ho trovato il codice leggermente più semplice da capire quando tradotto in PHP, come segue:

<?php

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

Vedi codice live


0

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

Per farlo tappare: 3


1
potresti aggiungere qualche spiegazione su cosa stai facendo e perché.
fedorqui "SO smettere di danneggiare" il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.