Як це друкує "привіт світ"?


163

Я виявив цю дивацтво:

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

Вихід:

hello world

Як це працює?


14
Я маю на увазі, що ви можете самі це зрозуміти.
Сотіріос Деліманоліс

30
Так. Зізнаюся ... я ловлю шапки :)
богем

6
Я думаю, я вже бачив це запитання, яке тут задавали.
Захід

6
@Oli Для цього має бути капелюх.
Сотіріос Деліманоліс

12
Такі питання, які не покращують базу даних, але існують виключно як клік, є надійним способом скасувати гру 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циклі for-loop переходить з групи в групу, тоді 5-бітна група виокремлюється ANDing число з маскою 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₂), і це повинно бути достатньо, щоб зробити карти, але це не буде працювати для космосу (чорт космосу!)

Тепер ми знаємо, що потрібно особливо обережно обробляти простір одночасно з іншими персонажами. Щоб досягти цього, код вмикає 7-й біт (але не 6-й) на вилучену 5-бітну групу з АБО 64 64₁₀ = 1000000₂( l & 31 | 64).

Поки 5-бітна група має вигляд: 10xxxxx₂(пробіл був би 1011111₂ = 95₁₀). Якщо ми можемо відобразити простір, не 0впливаючи на інші значення, тоді ми можемо включити 6-й біт, і це повинно бути все. Ось у чому mod 95грає частина, простір - це 1011111₂ = 95₁₀, з використанням модної операції (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 біт, яке може використовуватися з кодом ОП:

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
відповідь ще складніша за запитання: Д
Язан

1
Пояснення набагато чистіше :)
Прашант

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 бітами.

Ми перетворили кожен (16-ти бітовий) символ UTF-8 в 7 біт і отримаємо більше 56% коефіцієнта стиснення. Тож ми могли надсилати тексти з подвійною довжиною в однаковій кількості SMS-повідомлень. (Тут якось те саме сталося).


У коді ОП набагато більше відбувається. Наприклад, це насправді не пояснює, що | 64робиться.
Тед Хопп

1
@Amir: насправді 95 є, тому що вам потрібно отримати пробіл.
Бджола

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-е закодоване значення з діапазоном 0-31.

Важка частина, як ви кажете, - кодування простору. Малі літери англійської мови займають суміжний діапазон 97-122 в Unicode (і ascii, і більшості інших кодувань), але простір - 32.

Для подолання цього ви використовували деяку арифметику. ((x+64)%95)+32майже такий же, як x + 96(зауважте, наскільки побітовий АБО еквівалентний додаванню в цьому випадку), але коли x = 31, ми отримуємо 32.


6

Він друкує "привіт світ" з аналогічної причини:

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
Ви повинні пояснити, що ви робите, замість того, щоб опублікувати ще одну загадку
Олександр Дубінський,

1
Я пропоную вам вкласти певні зусилля, щоб знайти сайт (можливо, якийсь Beta StackExchange?), Де можна побачити цікаві загадки. Переповнення стека - це питання з питань відповідей та суворої уваги.
Марко Топольник

1
@MarkoTopolnik Мені б не хотілося жити у світі, де всі правила чи фокус були настільки суворо виконуються, що ніколи не допускати жодних винятків. Не кажучи вже про те, що в ТА існує безліч незмінних винятків.
גלעד ברקן

1
Я теж хотів, але ТАК такий світ в незвично великій мірі. Звичайно, тут є винятки, але вони не вітаються .
Марко Топольник

1
Ще 15 поділилися настроями Олександра. І ви праві, вказуючи, що саме питання є невідповідним для СУ, як коментується під ним.
Марко Тополник

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 цифри, якщо він закінчується праворуч. Тому що у нас було всього 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

Тепер нам потрібно перетворити його на 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

Це привіт світ у зворотному напрямку. Єдине питання - простір . І причину добре пояснює @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 ((char) (((l & 31 | 64)% 95) + 32/1002439 * 1002439));

Щоб зробити це кришками: 3


1
Подумайте про додавання пояснень щодо того, що ви робите і чому.
fedorqui 'ТАК перестаньте шкодити'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.