私はこの奇妙なことを発見しました:
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(すべて1ケース)の英字と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番目のビットセット(11xxxxx₂
)で始まることがわかります(6ビット目のみがオンになっているスペースを除く)OR
。5ビットのコード化は96
(96₁₀ = 1100000₂
)、それはする必要がありますマッピングを行うのに十分ですが、それはスペースでは機能しません(スペースをくすぐります!)
これで、他のキャラクターと同時にスペースを処理するために特別な注意を払う必要があることがわかりました。これを達成するために、コードがOR 64で抽出した5ビットのグループに(6日ではなく)に7ビットをオン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)
有効なASCIIに5ビットの値を変換しますキャラクター
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文字)を指定すると、逆のプロセスを実行し、OPのコードで使用できる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);
}
}
上記の回答にいくつかの値を追加します。次の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ビットの組み合わせを見つけることができます)、すべてのビットが連結されて大きな数を形成します。
正のlongは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で2倍の長さのテキストを送信できます。(それはどういうわけかここで同じことが起こりました)。
| 64
ているかを実際に説明するものではありません。
文字を5ビット値としてエンコードし、そのうちの11文字を64ビット長にパックしました。
(packedValues >> 5*i) & 31
範囲0〜31のi番目のエンコードされた値です。
あなたが言うように、難しい部分はスペースをエンコードすることです。小文字の英字は、Unicode(およびascii、およびその他のほとんどのエンコーディング)で97〜122の連続した範囲を占めますが、スペースは32です。
これを克服するために、いくつかの算術を使用しました。((x+64)%95)+32
とほとんど同じですx + 96
(この場合、ビット単位のORが加算に相当することに注意してください)。ただし、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と呼ばれるつまりdecimal-to-binaryを使用します。
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
それは逆にhello⌂worldです。唯一の問題はスペースです。そしてその理由は@higuaroの回答でよく説明されています。正直に言って、彼の答えにある説明を見るまで、私は最初の試みで自分自身で宇宙問題を解釈することができませんでした。
out.println((char)(((l&31 | 64)%95)+ 32/1002439 * 1002439));
それをキャップにする:3