我发现了这个奇怪之处:</ p>
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
输出:
hello world
这是如何运作的?
答案 0 :(得分:254)
数字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个符号(其中包含空格)。
for循环中的>>= 5
从一个组跳到另一个组,然后5位组被隔离并且在句子31₁₀ = 11111₂
现在代码将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位),你可以{{1} 5位
使用OR
(96
)进行编码,这应该足以进行映射,但这对空间无效(空间!)
现在我们知道必须特别注意与其他角色同时处理空间。为实现此目的,代码将打开第7位(但不是第6位)
提取的5位组使用OR 64 96₁₀ = 1100000₂
(64₁₀ = 1000000₂
)。
到目前为止,5位组的格式为:l & 31 | 64
(空格为10xxxxx₂
)。
如果我们可以将空间映射到1011111₂ = 95₁₀
而不影响其他值,那么我们可以打开第6位,这应该是全部。
以下是使用mod的0
部分,空间为mod 95
操作1011111₂ = 95₁₀
只有空格返回(l & 31 | 64) % 95)
,此后,代码通过添加0
将第6位打开
到前一个结果,32₁₀ = 100000₂
将5位值转换为有效的ascii字符
((l & 31 | 64) % 95) + 32)
以下代码执行逆过程,给定一个小写字符串(最多12个字符),返回可以与OP代码一起使用的64位长值:
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
答案 1 :(得分:39)
为上述答案添加一些价值。 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
答案 2 :(得分:26)
有趣!
可见的标准ASCII字符在32到127之间。
这就是你在那里看到32和95(127 - 32)的原因。
实际上每个字符在这里映射到5位,(你可以找到每个字符的5位组合),然后连接所有位以形成一个大数字。
正数为63位数,足以容纳12个字符的加密形式。所以它足以容纳Hello word
,但对于较大的文本,你应该使用更大的数字,甚至是BigInteger。
在一个应用程序中,我们希望通过短信传输可见的英文字符,波斯字符和符号。如您所见,有32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127
个可能的值,可以用7位表示。
我们将每个UTF-8(16位)字符转换为7位,并获得超过56%的压缩率。因此,我们可以在相同数量的SMS中发送长度为两倍的文本。 (这在某种程度上发生了同样的事情)。
答案 3 :(得分:17)
您收到的结果恰好是char
以下值的表示
104 -> h
101 -> e
108 -> l
108 -> l
111 -> o
32 -> (space)
119 -> w
111 -> o
114 -> r
108 -> l
100 -> d
答案 4 :(得分:16)
您已将字符编码为5位值,并将其中的11个打包为64位长。
(packedValues >> 5*i) & 31
是第i个编码值,范围为0-31。
正如你所说,困难的部分是编码空间。小写英文字母在Unicode(以及ascii和大多数其他编码)中占据连续范围97-122,但空格为32。
为了克服这个问题,你使用了一些算法。 ((x+64)%95)+32
与x + 96
几乎相同(请注意,在这种情况下,按位OR等同于加法),但当x = 31时,我们得到32
。
答案 5 :(得分:6)
由于类似的原因打印“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));
答案 6 :(得分:2)
如果没有Oracle
标记,很难看到这个问题。活跃的赏金把我带到了这里。我希望这个问题还有其他相关的技术标签: - (
我主要使用Oracle database
,因此我会使用一些Oracle
知识来解释和解释: - )
我们将数字4946144450195624
转换为binary
。为此,我使用一个名为dec2bin的小function
,即十进制到二进制。
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>
那么,我们在输出中得到了什么?
明白
反向是hello⌂world。唯一的问题是空间。 @higuaro在他的回答中解释了原因。老实说,我在第一次尝试时无法解释空间问题,直到我看到他的答案中给出的解释。
答案 7 :(得分:1)
我发现在翻译成PHP时,代码稍微容易理解,如下所示:
<?php
$result=0;
$bignum = 4946144450195624;
for (; $bignum > 0; $bignum >>= 5){
$result = (( $bignum & 31 | 64) % 95) + 32;
echo chr($result);
}
请参阅live code
答案 8 :(得分:0)
out.println((char)(((l&amp; 31 | 64)%95)+ 32/1002439 * 1002439));
使它成为上限:3