Я обнаружил эту странность:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
Вывод:
hello world
Как это работает?
Я обнаружил эту странность:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
Вывод:
hello world
Как это работает?
Ответы:
Число 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 битов можно представить 2⁵ = 32 символа. Английский алфавит содержит 26 букв, это оставляет место для 32 - 26 = 6 символов, кроме букв. С этой схемой кодификации вы можете иметь все 26 (один случай) английских букв и 6 символов (будучи пробелом среди них).
В >>= 5цикле for происходит переход от группы к группе, затем 5-битная группа изолируется И начинает вычислять число с маской 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-го бита set ( 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 бита, которое можно использовать с кодом 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);
}
}
Добавление некоторой ценности к ответам выше. Следующий скриптовый скрипт печатает промежуточные значения.
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-битная комбинация для каждого символа), а затем все биты объединяются в большое число.
Положительные длинные значения - это 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-сообщений. (Здесь как-то так же и произошло).
| 64делает.
Вы кодировали символы как 5-битные значения и упаковали 11 из них в 64-битную длину.
(packedValues >> 5*i) & 31 это i-е закодированное значение в диапазоне 0-31.
Сложная часть, как вы говорите, это кодирование пространства. Английские буквы в нижнем регистре занимают непрерывный диапазон 97-122 в Unicode (и ascii, и в большинстве других кодировок), но пробел равен 32.
Чтобы преодолеть это, вы использовали некоторую арифметику. ((x+64)%95)+32почти так же, как x + 96(обратите внимание, что в этом случае побитовое ИЛИ эквивалентно сложению), но когда x = 31, мы получаем 32.
Он печатает «привет мир» по той же причине, что и делает:
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, то есть десятичный в двоичный .
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 в своем ответе. Я, честно говоря, не мог интерпретировать проблему космоса сам с первой попытки, пока не увидел объяснение, данное в его ответе.
out.println ((символ) (((l & 31 | 64)% 95) + 32/1002439 * 1002439));
Чтобы сделать это заглавными буквами: 3