สิ่งที่พิมพ์นี้“ สวัสดีโลก”?


163

ฉันค้นพบสิ่งแปลกประหลาดนี้:

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

เอาท์พุท:

hello world

มันทำงานอย่างไร


14
ฉันหมายความว่าคุณสามารถคิดออกเอง
Sotirios Delimanolis

30
ใช่. ฉันยอมรับ ... ฉันกำลังหาหมวก :)
โบฮีเมียน

6
ฉันคิดว่าฉันเคยเห็นคำถามนี้ถามที่นี่มาก่อน ..
Zavior

6
@Oli ควรมีหมวกสำหรับสิ่งนั้น
Sotirios Delimanolis

12
คำถามเช่นนี้ที่ไม่ได้ปรับปรุงฐานข้อมูล แต่มีอยู่เพียงว่าเป็น Clickbait เป็นวิธีที่แน่นอนในการยกเลิกเกม Hat ในอนาคต กรุณาอย่าทำลายเกมโดยการเล่นมัน
Blazemonger

คำตอบ:


256

จำนวน4946144450195624เหมาะกับ 64 บิตการเป็นตัวแทนไบนารีคือ:

 10001100100100111110111111110111101100011000010101000

โปรแกรมถอดรหัสอักขระสำหรับกลุ่ม 5 บิตทุกกลุ่มจากขวาไปซ้าย

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

การเข้ารหัส 5 บิต

สำหรับ 5 บิตเป็นไปได้ที่จะแทน2⁵ = 32 ตัวอักษร ตัวอักษรภาษาอังกฤษมี 26 ตัวอักษรนี้ออกจากห้องสำหรับ 32 - 26 = 6 สัญลักษณ์นอกเหนือจากตัวอักษร ด้วยรูปแบบการประมวลผลนี้คุณสามารถมีตัวอักษรภาษาอังกฤษทั้งหมด 26 ตัว (หนึ่งตัว) และ 6 สัญลักษณ์ (เว้นระยะห่างกัน)

คำอธิบายอัลกอริทึม

>>= 5ในสำหรับวงกระโดดจากกลุ่มไปยังกลุ่มแล้วกลุ่ม 5 บิตได้รับการแยก Anding หมายเลขด้วยหน้ากาก31₁₀ = 11111₂ในประโยคl & 31

ตอนนี้โค้ดจะจับคู่ค่า 5 บิตกับอักขระ ASCII 7 บิตที่สอดคล้องกัน นี่คือส่วนที่ยุ่งยากตรวจสอบการเป็นตัวแทนไบนารีสำหรับตัวอักษรตัวพิมพ์เล็กในตารางต่อไปนี้:

  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

ที่นี่คุณจะเห็นว่าตัวละคร ASCII ที่เราต้องการแมปเริ่มต้นด้วยชุดบิตที่ 7 และ 6 ( 11xxxxx₂) (ยกเว้นช่องว่างซึ่งมีเพียงบิตที่ 6 บน) คุณสามารถORประมวลผลแบบ 5 บิตด้วย96( 96₁₀ = 1100000₂) และควรจะเป็น มากพอที่จะทำแผนที่ แต่นั่นจะไม่ทำงานสำหรับพื้นที่ (พื้นที่สาป!)

ตอนนี้เรารู้แล้วว่าต้องระมัดระวังเป็นพิเศษในการประมวลผลพื้นที่ในเวลาเดียวกันกับตัวละครอื่น ๆ เพื่อให้บรรลุนี้รหัสเปิดบิตที่ 7 (แต่ไม่ใช่ที่ 6) ในกลุ่ม 5 บิตที่แยกด้วย OR 64 64₁₀ = 1000000₂( l & 31 | 64)

จนถึงขณะนี้กลุ่ม 5 บิตเป็นรูปแบบ: 10xxxxx₂(ช่องว่างจะเป็น1011111₂ = 95₁₀) หากเราสามารถแมปพื้นที่ว่างเพื่อลด0ค่าอื่น ๆ จากนั้นเราสามารถเปิดบิตที่ 6 และควรจะเป็นทั้งหมด นี่คือสิ่งที่mod 95มาในการเล่นพื้นที่คือ1011111₂ = 95₁₀โดยใช้การดำเนินการ mod (l & 31 | 64) % 95)เพียงช่องว่างกลับไป0และหลังจากนี้รหัสเปิดบิตที่ 6 โดยการเพิ่ม32₁₀ = 100000₂ ผลก่อนหน้า((l & 31 | 64) % 95) + 32)เปลี่ยนค่า 5 บิตเป็น ASCII ที่ถูกต้อง ตัวละคร

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

รหัสต่อไปนี้ทำกระบวนการผกผันกำหนดสตริงตัวพิมพ์เล็ก (สูงสุด 12 ตัวอักษร) ส่งคืนค่าความยาว 64 บิตที่สามารถใช้กับรหัสของ 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
คำตอบนี้ไม่มีความลึกลับ แต่มันเป็นสิ่งที่คุณคิด

7
คำตอบนั้นยากยิ่งกว่าคำถาม: D
Yazan

1
คำอธิบายมากสะอาด :)
Prashant

40

การเพิ่มคุณค่าให้กับคำตอบข้างต้น สคริปต์ Groovy ต่อไปนี้จะพิมพ์ค่ากลาง

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

นี่มันคือ

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

! ที่น่าสนใจ

อักขระ ASCII มาตรฐานที่มองเห็นได้อยู่ในช่วง 32 ถึง 127

นั่นเป็นเหตุผลที่คุณเห็น 32 และ 95 (127 - 32) ที่นั่น

ในความเป็นจริงอักขระแต่ละตัวถูกแมปถึง 5 บิตที่นี่ (คุณสามารถค้นหาว่าชุดค่าผสม 5 บิตสำหรับอักขระแต่ละตัวคืออะไร) จากนั้นบิตทั้งหมดจะถูกต่อกันเพื่อสร้างจำนวนมาก

ความยาวเชิงบวกคือตัวเลข 63 บิตมีขนาดใหญ่พอที่จะรองรับรูปแบบการเข้ารหัสที่มีอักขระ 12 ตัว ดังนั้นจึงมีขนาดใหญ่พอที่จะเก็บไว้Hello wordแต่สำหรับข้อความที่มีขนาดใหญ่กว่าคุณจะต้องใช้ตัวเลขที่มากขึ้นหรือแม้แต่ BigInteger


ในแอปพลิเคชันเราต้องการถ่ายโอนอักขระภาษาอังกฤษที่มองเห็นได้อักขระเปอร์เซียและสัญลักษณ์ผ่าน SMS ตามที่คุณเห็นมี32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127ค่าที่เป็นไปได้ซึ่งสามารถแสดงด้วย 7 บิต

เราแปลงอักขระ UTF-8 (16 บิต) แต่ละตัวเป็น 7 บิตและรับอัตราการบีบอัดมากกว่า 56% ดังนั้นเราสามารถส่งข้อความที่มีความยาวสองเท่าในจำนวน SMS ที่เท่ากัน (มันเป็นสิ่งเดียวกันที่เกิดขึ้นที่นี่)


มีมากขึ้นในรหัสของ OP ตัวอย่างเช่นนี่ไม่ได้อธิบายสิ่งที่| 64กำลังทำอยู่จริงๆ
Ted Hopp

1
@Air: จริง 95 อยู่ที่นั่นเพราะคุณต้องได้รับช่องว่าง
Bee

17

คุณได้รับผลลัพธ์ที่เกิดขึ้นซึ่งcharแสดงถึงค่าที่ต่ำกว่า

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

16

คุณเข้ารหัสอักขระเป็นค่า 5 บิตและเก็บ 11 อักขระไว้ในความยาว 64 บิต

(packedValues >> 5*i) & 31 คือค่าที่เข้ารหัส i-th ด้วยช่วง 0-31

ส่วนที่ยากอย่างที่คุณพูดคือการเข้ารหัสพื้นที่ ตัวอักษรภาษาอังกฤษตัวพิมพ์เล็กใช้ช่วงที่ต่อเนื่องกัน 97-122 ใน Unicode (และ ascii และการเข้ารหัสอื่น ๆ ส่วนใหญ่) แต่พื้นที่คือ 32

เพื่อเอาชนะสิ่งนี้คุณต้องใช้เลขคณิต ((x+64)%95)+32เกือบจะเป็นเช่นเดียวกับx + 96(หมายเหตุวิธีบิตหรือเทียบเท่ากับนอกจากนี้ในกรณีนี้) แต่เมื่อ x = 31 32เราได้รับ


6

มันพิมพ์ "hello world" ด้วยเหตุผลที่คล้ายกันนี้:

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

แต่ด้วยเหตุผลที่แตกต่างกว่านี้:

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
คุณควรอธิบายสิ่งที่คุณกำลังทำแทนที่จะโพสต์ปริศนาอื่น
Aleksandr Dubinsky

1
ฉันขอแนะนำให้คุณลงทุนกับความพยายามในการค้นหาไซต์ (อาจเป็น Beta StackExchange หรือไม่?) ซึ่งยินดีต้อนรับปริศนาที่ให้ความสนุกสนาน Stack Overflow เป็นเว็บไซต์ถาม - ตอบที่มีการบังคับใช้อย่างเข้มงวด
Marko Topolnik

1
@MarkoTopolnik ฉันเกลียดที่จะอยู่ในโลกที่กฎหรือโฟกัสทั้งหมดถูกบังคับใช้อย่างเคร่งครัดจนไม่มีข้อยกเว้นใด ๆ ไม่ต้องพูดถึงว่ามีข้อยกเว้นดังกล่าวนับไม่ถ้วนใน SO
גלעדברקן

1
ฉันก็ต้องการเช่นกัน แต่ดังนั้นเป็นเช่นโลกในระดับที่ดีผิดปกติ ตรวจสอบว่ามีข้อยกเว้นแม้ที่นี่ แต่พวกเขาไม่ได้รับการต้อนรับ
Marko Topolnik

1
อีก 15 คนแบ่งปันความเชื่อมั่นของ Alexandr และคุณมีสิทธิ์ชี้ให้เห็นว่าคำถามนั้นไม่เหมาะสมสำหรับ SO ดังที่ได้แสดงความคิดเห็นไว้ด้านล่าง
Marko Topolnik

3

หากไม่มีOracleแท็กจะเป็นการยากที่จะเห็นคำถามนี้ ค่าหัวที่ใช้งานนำฉันมาที่นี่ ฉันหวังว่าคำถามจะมีแท็กเทคโนโลยีที่เกี่ยวข้องอื่น ๆ ด้วย :-(

ฉันทำงานด้วยOracle databaseดังนั้นฉันจะใช้Oracleความรู้ในการตีความและอธิบาย :-)

ลองแปลงจำนวนเข้า4946144450195624 binaryสำหรับการที่ผมใช้เล็ก ๆfunctionที่เรียกว่า DEC2BIN คือทศนิยมเพื่อไบนารี

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>

ลองใช้ฟังก์ชั่นเพื่อรับค่าไบนารี -

SQL> SELECT dec2bin(4946144450195624) FROM dual;

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

SQL>

ตอนนี้ที่จับได้คือการ5-bitแปลง เริ่มการจัดกลุ่มจากขวาไปซ้ายด้วยตัวเลข 5 หลักในแต่ละกลุ่ม เราได้รับ :-

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

ในที่สุดเราก็จะเหลือเพียง3หลัก int เขาสิ้นสุดที่ด้านขวา เพราะเรามีทั้งหมด 53 หลักในการแปลงเลขฐานสอง

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

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

SQL>

hello worldทั้งหมดมี11ตัวอักษร (รวมถึงช่องว่าง) ดังนั้นเราต้องเพิ่ม2บิตในกลุ่มสุดท้ายที่เราเหลือเพียง 3 บิตหลังจากจัดกลุ่ม

ดังนั้นตอนนี้เรามี: -

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

ตอนนี้เราต้องแปลงเป็นค่า ASCII 7 บิต สำหรับตัวละครมันง่ายเราต้องตั้งค่าบิตที่ 6 และ 7 เพิ่ม11ลงในกลุ่ม 5 บิตแต่ละกลุ่มด้านบนทางซ้าย

ที่ให้: -

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

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>

ลองดูที่แต่ละค่าไบนารี -

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>

เรามาดูกันว่าตัวละครพวกนี้คือ: -

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>

ดังนั้นเราจะได้อะไรในผลลัพธ์

dlrow ⌂ olleh

นั่นคือสวัสดีโลกในทางกลับกัน ปัญหาเดียวที่เป็นพื้นที่ และเหตุผลถูกอธิบายอย่างดีโดย @higuaro ในคำตอบของเขา ฉันไม่สามารถตีความประเด็นอวกาศได้ด้วยตัวเองตั้งแต่แรกพยายามจนกระทั่งฉันเห็นคำอธิบายที่ให้ไว้ในคำตอบของเขา


1

ฉันพบว่าโค้ดเข้าใจง่ายขึ้นเล็กน้อยเมื่อแปลเป็น PHP ดังนี้:

<?php

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

ดูรหัสสด


0

out.println ((ตัวอักษร) (((& & 31 | 64)% 95) + 32/1002439 * 1002439));

วิธีทำให้เป็นตัวพิมพ์ใหญ่: 3


1
พิจารณาเพิ่มคำอธิบายบางอย่างเกี่ยวกับสิ่งที่คุณทำและทำไม
fedorqui 'ดังนั้นหยุดทำอันตราย'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.