Bagaimana cara mencetak “hello world”?

163

Saya menemukan keanehan ini:

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

Keluaran:

hello world

Bagaimana cara kerjanya?

Bohemian
sumber
14
Maksud saya, Anda bisa memikirkannya sendiri.
Sotirios Delimanolis
30
Iya. Saya akui ... Saya sedang memancing topi :)
Bohemian
6
Saya pikir saya telah melihat pertanyaan ini diajukan di sini sebelumnya ..
Zavior
6
@ Oli Seharusnya ada topi untuk itu.
Sotirios Delimanolis
12
Pertanyaan seperti ini, yang tidak meningkatkan database tetapi hanya ada sebagai clickbait, adalah cara yang pasti untuk membuat game Hat dibatalkan di masa depan. Tolong jangan merusak permainan dengan mengemis untuk itu.
Blazemonger

Jawaban:

256

Jumlahnya 4946144450195624pas 64 bit, representasi binernya adalah:

 10001100100100111110111111110111101100011000010101000

Program mendekode karakter untuk setiap grup 5-bit, dari kanan ke kiri

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

Kodifikasi 5-bit

Untuk 5 bit, dimungkinkan mewakili 2⁵ = 32 karakter. Alfabet bahasa Inggris berisi 26 huruf, ini menyisakan ruang untuk 32 - 26 = 6 simbol terpisah dari huruf. Dengan skema kodifikasi ini Anda dapat memiliki semua 26 (satu huruf) huruf bahasa Inggris dan 6 simbol (menjadi spasi di antara mereka).

Deskripsi algoritma

The >>= 5dalam for-loop melompat dari grup ke grup, maka grup 5-bit terisolasi DAN nomor dengan topeng 31₁₀ = 11111₂dalam kalimatl & 31

Sekarang kode memetakan nilai 5-bit ke karakter ascii 7-bit yang sesuai. Ini adalah bagian yang sulit, periksa representasi biner untuk huruf alfabet huruf kecil dalam tabel berikut:

  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

Di sini Anda dapat melihat bahwa karakter ascii yang ingin kami petakan dimulai dengan set bit ke-7 dan ke-6 ( 11xxxxx₂) (kecuali untuk ruang, yang hanya memiliki bit ke-6), Anda dapat ORmenggunakan kodifikasi 5-bit dengan 96( 96₁₀ = 1100000₂) dan itu seharusnya cukup untuk melakukan pemetaan, tetapi itu tidak akan berhasil untuk ruang (ruang sialan!)

Sekarang kita tahu bahwa perhatian khusus harus diambil untuk memproses ruang pada saat yang sama dengan karakter lain. Untuk mencapai ini, kode mengaktifkan bit ke-7 (tetapi bukan ke-6) pada grup 5-bit yang diekstraksi dengan OR 64 64₁₀ = 1000000₂( l & 31 | 64).

Sejauh ini kelompok 5-bit berbentuk: 10xxxxx₂(ruang akan 1011111₂ = 95₁₀). Jika kita dapat memetakan ruang untuk 0menghapus nilai-nilai lain, maka kita dapat mengaktifkan bit ke-6 dan itu saja. Inilah mod 95bagian yang harus dimainkan, space 1011111₂ = 95₁₀, menggunakan operasi mod, (l & 31 | 64) % 95)hanya ruang yang kembali 0, dan setelah ini, kode menghidupkan bit ke-6 dengan menambahkan 32₁₀ = 100000₂ ke hasil sebelumnya, ((l & 31 | 64) % 95) + 32)mengubah nilai 5-bit menjadi ascii yang valid 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

Kode berikut melakukan proses invers, diberi string huruf kecil (maks 12 karakter), mengembalikan nilai panjang 64 bit yang dapat digunakan dengan kode 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);
    }
}    
higuaro
sumber
11
Jawaban ini tidak meninggalkan misteri. Sebaliknya, itu membuat Anda berpikir untuk Anda.
7
jawabannya bahkan lebih sulit daripada pertanyaan: D
Yazan
1
Penjelasannya jauh lebih bersih :)
Prashant
40

Menambahkan beberapa nilai ke jawaban di atas. Script groovy berikut mencetak nilai-nilai perantara.

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

Ini dia

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
Jayan
sumber
26

Menarik!

Karakter ASCII standar yang terlihat berada dalam kisaran 32 hingga 127.

Itu sebabnya Anda melihat 32, dan 95 (127 - 32) di sana.

Sebenarnya setiap karakter dipetakan ke 5 bit di sini, (Anda dapat menemukan apa kombinasi 5 bit untuk setiap karakter), dan kemudian semua bit digabungkan untuk membentuk angka yang besar.

Panjang positif adalah angka 63 bit, cukup besar untuk menampung bentuk terenkripsi 12 karakter. Jadi cukup besar untuk menampung Hello word, tetapi untuk teks yang lebih besar Anda harus menggunakan angka yang lebih besar, atau bahkan BigInteger.


Dalam suatu aplikasi kami ingin mentransfer Karakter Bahasa Inggris, Karakter Persia dan Simbol yang terlihat melalui SMS. Seperti yang Anda lihat ada 32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127nilai yang mungkin, yang dapat direpresentasikan dengan 7 bit.

Kami mengkonversi setiap karakter UTF-8 (16 bit) menjadi 7 bit, dan mendapatkan lebih dari rasio kompresi 56%. Jadi kami dapat mengirim teks dengan panjang dua kali lipat dalam jumlah SMS yang sama. (Entah bagaimana hal yang sama terjadi di sini).

Amir Pashazadeh
sumber
Ada banyak hal yang terjadi dalam kode OP. Misalnya, ini tidak benar-benar menjelaskan apa yang | 64dilakukan.
Ted Hopp
1
@ Ammir: sebenarnya 95 ada di sana karena Anda perlu mendapatkan karakter luar angkasa.
Bee
17

Anda mendapatkan hasil yang merupakan charrepresentasi dari nilai di bawah ini

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

Anda telah menyandikan karakter sebagai nilai 5-bit dan mengemas 11 di antaranya menjadi panjang 64 bit.

(packedValues >> 5*i) & 31 adalah nilai enkode ke-i dengan rentang 0-31.

Bagian yang sulit, seperti yang Anda katakan, adalah pengkodean ruang. Huruf bahasa Inggris kecil menempati kisaran yang berdekatan 97-122 di Unicode (dan ascii, dan sebagian besar penyandian lainnya), tetapi ruangnya 32.

Untuk mengatasinya, Anda menggunakan aritmatika. ((x+64)%95)+32hampir sama dengan x + 96(perhatikan bagaimana bitwise OR setara dengan penambahan, dalam kasus ini), tetapi ketika x = 31, kita dapatkan 32.

Aleksandr Dubinsky
sumber
6

Mencetak "hello world" untuk alasan yang sama seperti ini:

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

tetapi untuk alasan yang agak berbeda dari ini:

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));
גלעד ברקן
sumber
18
Anda harus menjelaskan apa yang Anda lakukan, alih-alih memposting teka
Aleksandr Dubinsky
1
Saya sarankan Anda menginvestasikan beberapa upaya dalam menemukan situs (mungkin beberapa Beta StackExchange?) Di mana kontribusi teka-teki yang menyenangkan diterima. Stack Overflow adalah situs tanya jawab dengan fokus ketat.
Marko Topolnik
1
@MarkoTopolnik Saya akan benci hidup di dunia di mana semua aturan atau fokus diberlakukan dengan ketat sehingga tidak pernah mengizinkan pengecualian. Belum lagi ada begitu banyak pengecualian pada SO.
גלעד ברקן
1
Saya juga akan melakukannya, tetapi SO adalah dunia yang luar biasa besarnya. Tentu ada pengecualian bahkan di sini, tetapi mereka tidak diterima .
Marko Topolnik
1
15 lainnya membagikan sentimen Alexandr. Dan Anda benar dalam menunjukkan bahwa pertanyaan itu sendiri tidak pantas untuk SO, seperti yang dikomentari di bawahnya.
Marko Topolnik
3

Tanpa Oracletag, sulit untuk melihat pertanyaan ini. Karunia aktif membawaku ke sini. Saya berharap pertanyaannya memiliki tag teknologi lain yang relevan juga :-(

Saya kebanyakan bekerja dengan Oracle database, jadi saya akan menggunakan beberapa Oraclepengetahuan untuk menafsirkan dan menjelaskan :-)

Mari kita konversi nomornya 4946144450195624menjadi binary. Untuk itu saya menggunakan functiondec2bin kecil bernama 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>

Mari kita gunakan fungsi untuk mendapatkan nilai biner -

SQL> SELECT dec2bin(4946144450195624) FROM dual;

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

SQL>

Sekarang tangkapannya adalah 5-bitkonversi. Mulai pengelompokan dari kanan ke kiri dengan 5 digit di setiap grup. Kita mendapatkan :-

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

Kami akhirnya akan dibiarkan dengan hanya 3 digit di ujungnya di kanan. Karena, kami memiliki total 53 digit dalam konversi biner.

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

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

SQL>

hello worldtotal memiliki 11 karakter (termasuk spasi), jadi kita perlu menambahkan 2 bit ke grup terakhir yang tersisa hanya 3 bit setelah pengelompokan.

Jadi, sekarang kita memiliki: -

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

Sekarang, kita perlu mengubahnya menjadi nilai ascii 7-bit. Untuk karakter itu mudah, kita hanya perlu mengatur bit 6 dan 7. Tambahkan 11ke setiap grup 5-bit di atas ke kiri.

Itu memberi: -

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

Mari kita menafsirkan nilai-nilai biner, saya akan gunakan 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>

Mari kita lihat setiap nilai biner -

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>

Mari kita lihat karakter apa mereka: -

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>

Jadi, apa yang kita dapatkan di output?

dlrow ⌂ olleh

Itu hello⌂world secara terbalik. Satu-satunya masalah adalah ruang . Dan alasannya dijelaskan dengan baik oleh @ higuaro dalam jawabannya. Jujur saya tidak bisa menafsirkan masalah ruang angkasa pada upaya pertama, sampai saya melihat penjelasan yang diberikan dalam jawabannya.

Lalit Kumar B
sumber
1

Saya menemukan kode sedikit lebih mudah dimengerti ketika diterjemahkan ke dalam PHP, sebagai berikut:

<?php

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

Lihat kode langsung

slevy1
sumber
0

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

Untuk membuatnya tutup: 3


sumber
1
pertimbangkan untuk menambahkan beberapa penjelasan tentang apa yang Anda lakukan dan mengapa.
fedorqui 'SO berhenti merugikan'