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?
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?
Câu trả lời:
Số 4946144450195624
phù 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
Đố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).
Các >>= 5
trong 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ể OR
mã 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 để 0
khô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 95
phầ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 0
và 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);
}
}
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
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) = 127
cá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).
| 64
đang làm.
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)+32
gầ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
.
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));
Không có Oracle
thẻ, 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ố Oracle
kiến thức để giải thích và giải thích :-)
Hãy chuyển đổi số 4946144450195624
thành binary
. Cho rằng tôi sử dụng một function
dec2bin 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 11
và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.
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
out.println ((char) (((l & 31 | 64)% 95) + 32/1002439 * 1002439));
Để làm cho nó mũ: 3