ฉันค้นพบสิ่งแปลกประหลาดนี้:
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
ในสำหรับวงกระโดดจากกลุ่มไปยังกลุ่มแล้วกลุ่ม 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);
}
}
การเพิ่มคุณค่าให้กับคำตอบข้างต้น สคริปต์ 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 บิตสำหรับอักขระแต่ละตัวคืออะไร) จากนั้นบิตทั้งหมดจะถูกต่อกันเพื่อสร้างจำนวนมาก
ความยาวเชิงบวกคือตัวเลข 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
กำลังทำอยู่จริงๆ
คุณได้รับผลลัพธ์ที่เกิดขึ้นซึ่ง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
คือค่าที่เข้ารหัส i-th ด้วยช่วง 0-31
ส่วนที่ยากอย่างที่คุณพูดคือการเข้ารหัสพื้นที่ ตัวอักษรภาษาอังกฤษตัวพิมพ์เล็กใช้ช่วงที่ต่อเนื่องกัน 97-122 ใน Unicode (และ ascii และการเข้ารหัสอื่น ๆ ส่วนใหญ่) แต่พื้นที่คือ 32
เพื่อเอาชนะสิ่งนี้คุณต้องใช้เลขคณิต ((x+64)%95)+32
เกือบจะเป็นเช่นเดียวกับx + 96
(หมายเหตุวิธีบิตหรือเทียบเท่ากับนอกจากนี้ในกรณีนี้) แต่เมื่อ 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
สำหรับการที่ผมใช้เล็ก ๆ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 ในคำตอบของเขา ฉันไม่สามารถตีความประเด็นอวกาศได้ด้วยตัวเองตั้งแต่แรกพยายามจนกระทั่งฉันเห็นคำอธิบายที่ให้ไว้ในคำตอบของเขา
ฉันพบว่าโค้ดเข้าใจง่ายขึ้นเล็กน้อยเมื่อแปลเป็น PHP ดังนี้:
<?php
$result=0;
$bignum = 4946144450195624;
for (; $bignum > 0; $bignum >>= 5){
$result = (( $bignum & 31 | 64) % 95) + 32;
echo chr($result);
}
ดูรหัสสด
out.println ((ตัวอักษร) (((& & 31 | 64)% 95) + 32/1002439 * 1002439));
วิธีทำให้เป็นตัวพิมพ์ใหญ่: 3