[java] 이것은 “hello world”를 어떻게 인쇄합니까?

나는이 이상한 것을 발견했다 :

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

산출:

hello world

어떻게 작동합니까?



답변

이 숫자는 494614445019562464 비트에 해당하며 이진 표현은 다음과 같습니다.

 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 자 (1 개의 경우)의 영어 문자와 6 개의 기호 (그 사이의 공간)를 가질 수 있습니다.

알고리즘 설명

>>= 5에 대한 루프에서이 그룹에 그룹에서 점프 후 5 비트 그룹은 마스크 수를 AND 연산 격리됩니다 31₁₀ = 11111₂문장의를l & 31

이제 코드는 5 비트 값을 해당 7 비트 ASCII 문자에 매핑합니다. 이것은 까다로운 부분이므로 다음 표에서 소문자 알파벳 문자의 이진 표현을 확인하십시오.

  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 비트를 가지고 공간 제외)), 당신은 할 수 OR5 비트와 성문화 96( 96₁₀ = 1100000₂) 및 그이어야한다 매핑을 수행하기에 충분하지만 공간 (다크 스페이스)에서는 작동하지 않습니다.

이제 우리는 다른 캐릭터들과 동시에 공간을 처리하기 위해 특별한주의를 기울여야한다는 것을 알고 있습니다. 이를 달성하기 위해 코드는 추출 된 5 비트 그룹에서 OR 64 64₁₀ = 1000000₂( l & 31 | 64)로 7 번째 비트 (6 번째는 아님)를 켭니다 .

지금까지 5 비트 그룹의 형식은 다음과 같습니다 10xxxxx₂(공백은 1011111₂ = 95₁₀). 공간을 0다른 값 에 영향 을 미치지 않는 곳에 매핑 할 수 있다면 6 번째 비트를 켤 수 있으며 그게 전부입니다. 다음은 mod 95파트가 재생 하는 부분이며, space is 1011111₂ = 95₁₀, mod 연산 (l & 31 | 64) % 95)만 사용하여 space 만 되돌아갑니다. 0이 후에 코드는 32₁₀ = 100000₂
이전 결과 에 추가하여 6 번째 비트를 켜고 ((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 자)이 주어지면 역 프로세스를 수행하며 OP 코드와 함께 사용할 수있는 64 비트 길이 값을 반환합니다.

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


답변

위의 답변에 가치를 더하십시오. 다음 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


답변

흥미 롭습니다!

표시되는 표준 ASCII 문자는 32-127입니다.

이것이 32와 95 (127-32)가 보이는 이유입니다.

실제로 각 문자는 여기에서 5 비트로 매핑되며 (각 문자마다 5 비트 조합을 찾을 수 있음) 모든 비트가 연결되어 많은 수를 형성합니다.

양의 long은 63 비트 숫자이며 암호화 된 12 문자 형식을 보유 할 수있을만큼 큽니다. 따라서을 보유하기에는 충분히 크지 Hello word만 더 큰 텍스트의 경우 더 큰 숫자 또는 BigInteger를 사용해야합니다.


응용 프로그램에서 SMS를 통해 보이는 영어 문자, 페르시아 문자 및 기호를 전송하려고했습니다. 보시다시피 32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 1277 비트로 표현할 수있는 가능한 값이 있습니다.

각 UTF-8 (16 비트) 문자를 7 비트로 변환했으며 56 % 이상의 압축률을 얻습니다. 따라서 같은 수의 SMS로 길이가 두 배인 텍스트를 보낼 수 있습니다. (여기서도 같은 일이 일어났습니다).


답변

char아래 값을 나타내는 결과가 나타 납니다.

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


답변

문자를 5 비트 값으로 인코딩하고 11 비트를 64 비트 길이로 압축했습니다.

(packedValues >> 5*i) & 31 0-31 범위의 i 번째 인코딩 된 값입니다.

어려운 부분은 공간을 인코딩하는 것입니다. 소문자 영문자는 유니 코드 (및 ASCII 및 대부분의 다른 인코딩)에서 연속 범위 97-122를 차지하지만 공백은 32입니다.

이를 극복하기 위해 산술을 사용했습니다. ((x+64)%95)+32는 거의 동일하지만 x + 96(이 경우 비트 OR이 덧셈에 해당하는 방법에 유의하십시오) x = 31이면을 얻습니다 32.


답변

비슷한 이유로 “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));


답변

없이 Oracle태그가 이 질문을보기가 어려웠습니다. 활발한 현상금이 나를 여기로 데려 왔습니다. 질문에 다른 관련 기술 태그가 있었으면 좋겠습니다.

나는 주로와 함께 작업 Oracle database하므로 Oracle지식을 사용 하여 해석하고 설명합니다 🙂

숫자 4946144450195624를 로 변환합시다 binary. 이를 위해 functiondec2bin이라는 작은 값을 사용합니다. 즉 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>

이진 값을 얻기 위해 함수를 사용합시다-

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 자리 숫자 만 남았습니다 . 이진 변환에는 총 53 자리가 있었기 때문입니다.

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

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

SQL>

hello world11 자리 (공백 포함)이므로 2 를 더해야합니다 그룹화 후 3 비트 만 남은 마지막 그룹에 비트를 .

이제 우리는 :-

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

이제 7 비트 ASCII 값으로 변환해야합니다. 문자는 쉬우므로 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

그것은 반대로 hello⌂world 입니다. 유일한 문제는 공간 입니다. 그리고 그 이유는 @higuaro가 그의 대답에 잘 설명되어 있습니다. 나는 그의 대답에 주어진 설명을 볼 때까지 처음 시도 할 때 우주 문제를 스스로 해석 할 수 없었다.