Làm thế nào để in này xin chào thế giới?


163

Tôi phát hiện ra điều kỳ lạ này:

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

Đầu ra:

hello world

Cái này hoạt động ra sao?


14
Tôi có nghĩa là bạn có thể tự tìm ra điều này.
Sotirios Delimanolis

30
Đúng. Tôi thừa nhận ... Tôi đang câu cá cho mũ :)
Bohemian

6
Tôi nghĩ rằng tôi đã thấy câu hỏi này được hỏi ở đây trước đây ..
Zavior

6
@Oli Nên có một cái mũ cho điều đó.
Sotirios Delimanolis

12
Những câu hỏi như thế này, không cải thiện cơ sở dữ liệu mà chỉ tồn tại dưới dạng clickbait, là một cách chắc chắn để khiến trò chơi Hat bị hủy trong tương lai. Xin đừng phá hỏng trò chơi bằng cách làm cho nó vui vẻ.
Blazemonger

Câu trả lời:


256

Số 4946144450195624phù hợp với 64 bit, biểu diễn nhị phân của nó là:

 10001100100100111110111111110111101100011000010101000

Chương trình giải mã một ký tự cho mỗi nhóm 5 bit, từ phải sang trái

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

Mã hóa 5 bit

Đối với 5 bit, có thể biểu thị 2⁵ = 32 ký tự. Bảng chữ cái tiếng Anh chứa 26 chữ cái, điều này để lại khoảng 32 - 26 = 6 ký hiệu ngoài các chữ cái. Với sơ đồ mã hóa này, bạn có thể có tất cả 26 (một trường hợp) chữ cái tiếng Anh và 6 ký hiệu (là khoảng trắng trong số chúng).

Mô tả thuật toán

Các >>= 5trong cho vòng nhảy từ nhóm này sang nhóm, sau đó nhóm 5-bit bị cô lập ANDing số với mặt nạ 31₁₀ = 11111₂trong câul & 31

Bây giờ mã ánh xạ giá trị 5 bit thành ký tự ascii 7 bit tương ứng của nó. Đây là phần khó khăn, kiểm tra các biểu diễn nhị phân cho các chữ cái trong bảng chữ cái trong bảng sau:

  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

Ở đây bạn có thể thấy rằng các ký tự ascii mà chúng ta muốn ánh xạ bắt đầu bằng tập bit thứ 7 và thứ 6 ( 11xxxxx₂) (ngoại trừ không gian, chỉ có bit thứ 6 trên), bạn có thể ORmã hóa 5 bit bằng 96( 96₁₀ = 1100000₂) và đó phải là mã hóa đủ để thực hiện ánh xạ, nhưng điều đó sẽ không hiệu quả đối với không gian (không gian chết tiệt!)

Bây giờ chúng tôi biết rằng phải được chăm sóc đặc biệt để xử lý không gian cùng lúc với các nhân vật khác. Để đạt được điều này, mã bật bit thứ 7 (nhưng không phải thứ 6) trên nhóm 5 bit được trích xuất với OR 64 64₁₀ = 1000000₂( l & 31 | 64).

Cho đến nay, nhóm 5 bit có dạng: 10xxxxx₂(không gian sẽ là 1011111₂ = 95₁₀). Nếu chúng ta có thể ánh xạ không gian để 0không ảnh hưởng đến các giá trị khác, thì chúng ta có thể bật bit thứ 6 và đó là tất cả. Đây là mod 95phần chơi, không gian là gì 1011111₂ = 95₁₀, sử dụng thao tác mod (l & 31 | 64) % 95)chỉ có không gian quay trở lại 0và sau đó, mã này bật bit thứ 6 bằng cách thêm 32₁₀ = 100000₂ vào kết quả trước đó, ((l & 31 | 64) % 95) + 32)biến giá trị 5 bit thành ascii hợp lệ tính cách

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

Đoạn mã sau thực hiện quy trình nghịch đảo, được đưa ra một chuỗi chữ thường (tối đa 12 ký tự), trả về giá trị dài 64 bit có thể được sử dụng với mã của 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
Câu trả lời này không để lại bí ẩn. Thay vào đó, nó làm suy nghĩ của bạn cho bạn.

7
câu trả lời thậm chí còn khó hơn câu hỏi: D
Yazan 31/12/14

1
Giải thích sạch sẽ hơn nhiều :)
Prashant

40

Thêm một số giá trị cho câu trả lời trên. Theo kịch bản Groovy in các giá trị trung gian.

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

Nó đây rồi

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

Hấp dẫn!

Các ký tự ASCII tiêu chuẩn có thể nhìn thấy nằm trong khoảng từ 32 đến 127.

Đó là lý do tại sao bạn thấy 32 và 95 (127 - 32) ở đó.

Trong thực tế, mỗi ký tự được ánh xạ tới 5 bit ở đây, (bạn có thể tìm thấy sự kết hợp 5 bit cho mỗi ký tự) và sau đó tất cả các bit được nối với nhau để tạo thành một số lớn.

Độ dài dương là số 63 bit, đủ lớn để giữ dạng mã hóa gồm 12 ký tự. Vì vậy, nó đủ lớn để chứa Hello word, nhưng đối với các văn bản lớn hơn, bạn sẽ sử dụng số lượng lớn hơn hoặc thậm chí là BigInteger.


Trong một ứng dụng, chúng tôi muốn chuyển các ký tự tiếng Anh, ký tự và ký hiệu tiếng Ba Tư qua SMS. Như bạn thấy có 32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127các giá trị có thể, có thể được biểu diễn bằng 7 bit.

Chúng tôi đã chuyển đổi mỗi ký tự UTF-8 (16 bit) thành 7 bit và đạt tỷ lệ nén hơn 56%. Vì vậy, chúng tôi có thể gửi văn bản với độ dài gấp đôi trong cùng một số SMS. (Đó là bằng cách nào đó điều tương tự đã xảy ra ở đây).


Có rất nhiều điều đang diễn ra trong mã của OP. Ví dụ, điều này không thực sự giải thích những gì | 64đang làm.
Ted Hopp

1
@Amir: thực sự là 95 vì bạn cần có một nhân vật không gian.
Ong

17

Bạn đang nhận được một kết quả có thể charđại diện cho các giá trị dưới đây

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

16

Bạn đã mã hóa các ký tự dưới dạng các giá trị 5 bit và đóng gói 11 ký tự thành một bit dài 64 bit.

(packedValues >> 5*i) & 31 là giá trị được mã hóa thứ i với phạm vi 0-31.

Phần khó, như bạn nói, là mã hóa không gian. Các chữ cái tiếng Anh viết thường chiếm phạm vi tiếp giáp 97-122 bằng Unicode (và ascii, và hầu hết các bảng mã khác), nhưng khoảng trắng là 32.

Để khắc phục điều này, bạn đã sử dụng một số số học. ((x+64)%95)+32gần giống như x + 96(lưu ý cách bitwise OR tương đương với phép cộng, trong trường hợp này), nhưng khi x = 31, chúng ta nhận được 32.


6

Nó in "hello world" vì một lý do tương tự như vậy:

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

nhưng vì một lý do hơi khác so với điều này:

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
Bạn nên giải thích những gì bạn đang làm, thay vì đăng một câu đố khác
Aleksandr Dubinsky

1
Tôi khuyên bạn nên đầu tư một số nỗ lực vào việc tìm kiếm một trang web (có lẽ là một số Beta StackExchange?), Nơi đóng góp những câu đố vui được chào đón. Stack Overflow là một trang web Hỏi & Đáp với trọng tâm được thi hành nghiêm ngặt.
Marko Topolnik

1
@MarkoTopolnik Tôi ghét sống trong một thế giới nơi tất cả các quy tắc hoặc trọng tâm được thực thi nghiêm ngặt đến mức không bao giờ cho phép bất kỳ ngoại lệ nào. Chưa kể rằng có vô số trường hợp ngoại lệ như vậy trên SO.
גלעד

1
Tôi cũng sẽ như vậy, nhưng SO là một thế giới như vậy đến một mức độ lớn khác thường. Chắc chắn có những ngoại lệ ngay cả ở đây, nhưng chúng không được chào đón .
Marko Topolnik

1
15 tình cảm khác của Alexandr chia sẻ. Và bạn đã đúng khi chỉ ra rằng chính câu hỏi không phù hợp với SO, như đã bình luận bên dưới nó.
Marko Topolnik

3

Không có Oraclethẻ, thật khó để xem câu hỏi này. Tiền thưởng tích cực đã đưa tôi đến đây. Tôi muốn câu hỏi cũng có các thẻ công nghệ liên quan khác :-(

Tôi chủ yếu làm việc với Oracle database, vì vậy tôi sẽ sử dụng một số Oraclekiến thức để giải thích và giải thích :-)

Hãy chuyển đổi số 4946144450195624thành binary. Cho rằng tôi sử dụng một functiondec2bin nhỏ gọi là thập phân đến nhị phân .

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>

Hãy sử dụng hàm để lấy giá trị nhị phân -

SQL> SELECT dec2bin(4946144450195624) FROM dual;

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

SQL>

Bây giờ bắt là 5-bit chuyển đổi. Bắt đầu nhóm từ phải sang trái với 5 chữ số trong mỗi nhóm. Chúng tôi nhận được :-

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

Cuối cùng chúng tôi sẽ bị bỏ lại chỉ với 3 chữ số int anh kết thúc ở bên phải. Bởi vì, chúng tôi đã có tổng cộng 53 chữ số trong chuyển đổi nhị phân.

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

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

SQL>

hello world tổng cộng có 11 ký tự (bao gồm cả không gian), vì vậy chúng tôi cần thêm 2 bit vào nhóm cuối cùng, nơi chúng tôi chỉ còn lại 3 bit sau khi nhóm.

Vì vậy, bây giờ chúng ta có: -

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

Bây giờ, chúng ta cần chuyển đổi nó thành giá trị ascii 7 bit. Đối với các ký tự thật dễ dàng, chúng ta chỉ cần đặt bit thứ 6 và thứ 7. Thêm 11vào mỗi nhóm 5 bit ở trên bên trái.

Điều đó mang lại: -

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

Hãy giải thích các giá trị nhị phân, tôi sẽ sử dụng 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>

Hãy xem xét từng giá trị nhị phân -

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>

Hãy xem họ là nhân vật nào: -

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>

Vì vậy, những gì chúng ta nhận được trong đầu ra?

dlrow olleh

Đó là helloworld ngược lại. Vấn đề duy nhất là không gian . Và lý do được giải thích tốt bởi @higuaro trong câu trả lời của mình. Tôi thực sự không thể tự giải thích vấn đề không gian trong lần thử đầu tiên, cho đến khi tôi thấy lời giải thích được đưa ra trong câu trả lời của anh ấy.


1

Tôi thấy mã dễ hiểu hơn một chút khi dịch sang PHP, như sau:

<?php

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

Xem mã trực tiếp


0

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

Để làm cho nó mũ: 3


1
xem xét thêm một số giải thích về những gì bạn đang làm và tại sao.
fedorqui 'SO ngừng làm hại'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.