Bu nasıl “merhaba dünya” yazdırıyor?


163

Bu tuhaflığı keşfettim:

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

Çıktı:

hello world

Bu nasıl çalışıyor?


14
Yani bunu kendiniz anlayabilirsiniz.
Sotirios Delimanolis

30
Evet. Kabul ediyorum ... Şapka için balık tutuyorum :)
Bohemian

6
Sanırım bu soruyu daha önce burada sordum.
Zavior

6
@Oli Bunun için bir şapka olmalı.
Sotirios Delimanolis

12
Veritabanını iyileştirmeyen ancak yalnızca clickbait olarak mevcut olan bu gibi sorular, Şapka oyununun gelecekte iptal edilmesini sağlamanın kesin bir yoludur. Lütfen oyunu fahişeyle mahvetmeyin.
Blazemonger

Yanıtlar:


256

Sayı 494614445019562464 bite uyar, ikili gösterimi:

 10001100100100111110111111110111101100011000010101000

Program, sağdan sola her 5 bitlik grup için bir karakteri çözer

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

5 bit kodlama

5 bit için 2⁵ = 32 karakteri temsil etmek mümkündür. İngilizce alfabe 26 harf içerir, bu harfler dışında 32-26 = 6 semboller için yer bırakır. Bu kodlama şeması ile tüm 26 (bir vaka) ingilizce harf ve 6 sembol (aralarında boşluk olmak) olabilir.

Algoritma açıklaması

>>= 5Dongu olarak gruptan gruba atlar, daha sonra 5-bit grubu maskesi ile sayı operatörü kullanılarak izole olur 31₁₀ = 11111₂cümledel & 31

Şimdi kod 5 bit değerini karşılık gelen 7 bit ascii karakteriyle eşler. Bu zor kısmı, aşağıdaki tabloda küçük harf alfabe harfleri için ikili sunumları kontrol edin:

  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

Burada, eşlemek istediğimiz ascii karakterlerinin 7. ve 6. bit seti ( 11xxxxx₂) ile başladığını görebilirsiniz (yalnızca 6. bit açık olan alan hariç), OR5 bit kodlamayı96 (96₁₀ = 1100000₂ ) ve bu haritalama yapmak için yeterli, ancak bu alan için işe yaramaz (darn space!)

Artık alanı diğer karakterlerle aynı anda işlemek için özel dikkat gösterilmesi gerektiğini biliyoruz. Bunu başarmak için kod çıkarılmış 5 bitlik grupta 7. biti (6. değil) OR 64 64₁₀ = 1000000₂(l & 31 | 64 ) .

Şimdiye kadar 5 bitlik grup şu şekildedir: 10xxxxx₂(boşluk olurdu 1011111₂ = 95₁₀). Alanı 0diğer değerlerden etkilenmeyecek şekilde eşleyebilirsek, 6. biti açabiliriz ve hepsi bu olmalıdır. Parçanın mod 95oynamaya geldiği yer, boşluk, 1011111₂ = 95₁₀mod işlemini kullanarak (l & 31 | 64) % 95)sadece alan geri gider 0ve bundan sonra kod 32₁₀ = 100000₂ , önceki sonuca ekleyerek ((l & 31 | 64) % 95) + 32), 5 bit değerini geçerli bir ascii'ye dönüştürerek 6. biti açar karakter

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

Aşağıdaki kod, küçük harfli bir dize (maks. 12 karakter) verildiğinde ters işlemi yapar, OP koduyla kullanılabilecek 64 bit uzun değerini döndürür:

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
Bu cevap hiçbir gizem bırakmıyor. Aksine, sizin için düşüncelerinizi yapar.

7
cevap şu sorudan bile daha zordur: D
Yazan

1
Açıklama çok daha temiz :)
Prashant

40

Yukarıdaki cevaplara biraz değer katıyoruz. Mükemmel komut dosyasının ardından ara değerler yazdırılır.

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

İşte burada

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

İlginç!

Görünür olan standart ASCII karakterleri 32 ila 127 arasındadır.

Bu yüzden orada 32 ve 95'i (127 - 32) görüyorsunuz.

Aslında, her karakter burada 5 bit ile eşleştirilir, (her karakter için 5 bit kombinasyonun ne olduğunu bulabilirsiniz) ve daha sonra tüm bitler çok sayıda oluşturmak için birleştirilir.

Pozitif uzunlar 63 bit sayıdır, 12 karakterden oluşan şifreli formu tutacak kadar büyüktür. Bu yüzden tutacak kadar büyük Hello word, ancak daha büyük metinler için daha büyük sayılar, hatta bir BigInteger kullanmalısınız.


Bir uygulamada görünür İngilizce Karakterleri, Farsça Karakterleri ve Sembolleri SMS ile aktarmak istedik. Gördüğünüz gibi var32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127 , 7 bit ile temsil edilebilecek olası değerler .

Her UTF-8 (16 bit) karakteri 7 bite dönüştürdük ve% 56'dan fazla sıkıştırma oranı elde ettik. Böylece, aynı sayıda SMS'de iki kat uzunluktaki metinler gönderebiliriz. (Bir şekilde burada da aynı şey oldu).


OP kodunda çok daha fazlası var. Örneğin, bu gerçekten ne | 64yaptığını açıklamıyor .
Ted Hopp

1
@Amir: Aslında 95 orada çünkü bir boşluk karakteri almalısın.
Arı

17

charAşağıdaki değerlerin temsili olan bir sonuç alıyorsunuz

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

16

Karakterleri 5 bit değerleri olarak kodladınız ve 11 tanesini 64 bit uzunluğunda paketlediniz.

(packedValues >> 5*i) & 31 0-31 aralığında i-th kodlanmış değerdir.

Sabit kısım, dediğiniz gibi, alanı kodlamaktır. Küçük İngilizce harfleri Unicode (ve ascii ve diğer kodlamaların çoğunda) bitişik aralık 97-122'yi işgal eder, ancak boşluk 32'dir.

Bunun üstesinden gelmek için bazı aritmetik kullandınız. ((x+64)%95)+32neredeyse aynıdır x + 96(bu durumda OR'nin toplamaya ne kadar bit eşdeğer olduğuna dikkat edin), ancak x = 31 olduğunda, elde ederiz 32.


6

Benzer bir nedenden dolayı "merhaba dünya" yazdırır:

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

ama bundan biraz farklı bir nedenden dolayı:

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
Başka bir bilmece yayınlamak yerine ne yaptığınızı açıklamalısınız
Aleksandr Dubinsky

1
Ben eğlenceli bilmeceler katkıda bulunan bir site (belki bazı Beta StackExchange?) Bulmak için biraz çaba öneririz. Stack Overflow, kesinlikle uygulanmakta olan bir Soru-Cevap sitesidir.
Marko Topolnik

1
@MarkoTopolnik Tüm kuralların veya odaklanmanın hiçbir istisnaya asla izin vermeyecek kadar sıkı uygulandığı bir dünyada yaşamaktan nefret ederim. SO hakkında sayısız istisnalar olduğundan bahsetmiyorum bile.
ברקן ברקן

1
Ben de isterdim, ama SO alışılmadık derecede büyük bir dünya. Tabii burada bile istisnalar var, ama hoş karşılanmıyorlar .
Marko Topolnik

1
Bir diğer 15 kişi Alexandr'ın duygularını paylaştı. Ve aşağıda yorumlandığı gibi, sorunun kendisinin SO için uygun olmadığını belirtmekte haklısınız.
Marko Topolnik

3

Olmadan OracleEtiket bu soruyu görmek zordu. Aktif ödül beni buraya getirdi. Sorunun başka alakalı teknoloji etiketleri de olmasını isterdim :-(

Çoğunlukla çalışıyorum Oracle database, bu yüzden Oracleyorumlamak ve açıklamak için biraz bilgi kullanırdım :-)

Numarayı 4946144450195624dönüştürelim binary. Bunun için functiondec2bin denilen küçük bir ondalık sayıyı kullanıyorum .

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>

İkili değeri almak için işlevi kullanalım -

SQL> SELECT dec2bin(4946144450195624) FROM dual;

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

SQL>

Şimdi yakalama 5-bitdönüşüm. Her grupta 5 basamaklı sağdan sola gruplandırmaya başlayın. Biz: -

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

Sonunda sağda biten int ile sadece 3 basamak kalacaktı . Çünkü ikili dönüşümde toplam 53 hane vardı.

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

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

SQL>

hello worldtoplamda 11 karakter (boşluk dahil) olduğundan 2 eklememiz gerekir gruplandırmadan sonra yalnızca 3 bit ile kaldığımız son gruba bit .

Yani, şimdi: -

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

Şimdi, onu 7-bit ascii değerine dönüştürmemiz gerekiyor. Karakterler için kolaydır, sadece 6. ve 7. bitleri ayarlamamız gerekir. 11Soldaki her 5 bitlik gruba ekleyin .

Bu verir: -

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

İkili değerleri yorumlayalım, kullanacağım 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>

Her bir ikili değere bakalım -

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>

Hangi karakter olduklarına bakalım: -

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>

Peki, çıktıda ne elde ediyoruz?

dlrow ⌂ olleh

Bu tersine merhaba dünya . Tek sorun alan . Ve nedeni cevabında @higuaro tarafından iyi açıklanmış. Cevabında verilen açıklamayı görene kadar, uzay meselesini ilk denemede kendim yorumlayamadım.


1

Aşağıdaki gibi PHP'ye çevrildiğinde kodu anlamak biraz daha kolay buldum:

<?php

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

Canlı koda bakın


0

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

Kapaklar yapmak için: 3


1
ne yaptığınıza ve nedeniniz hakkında bir açıklama eklemeyi düşünebilirsiniz.
fedorqui 'SO
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.