私はこの奇妙さを発見しました:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
出力:
hello world
これはどのように作動しますか?
上記の回答に価値を追加します。 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で2倍の長さのテキストを送信できます。 (どういうわけかここで同じことが起こりました)。
以下の値の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
は、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
に変換しましょう。そのために、dec2binと呼ばれる小さなfunction
を使用します。つまり、---(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
最終的には、右端で桁だけで終了します。なぜなら、バイナリ変換では合計53桁だったからです。
SQL> SELECT LENGTH(dec2bin(4946144450195624)) FROM dual;
LENGTH(DEC2BIN(4946144450195624))
---------------------------------
53
SQL>
hello world
totalには11文字(スペースを含む)があるため、グループ化後に3ビットだけ残った最後のグループに2ビットを追加する必要があります。
だから、今私たちは:-
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
次に、それを7ビットASCII値に変換する必要があります。簡単なキャラクターの場合は、6番目と7番目のビットを設定するだけです。左側の上の各5ビットグループに11
を追加します。
それは与える:-
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>
それで、出力には何が得られますか?
d l r o w⌂o l l e h
つまり、逆にhello⌂worldです。唯一の問題はスペースです。そしてその理由は、@ higuaroの回答でよく説明されています。正直に言って、最初の試みで自分のスペースの問題を自分で解釈することはできませんでした。
次のように、コードをPHPに翻訳すると理解しやすくなりました。
<?php
$result=0;
$bignum = 4946144450195624;
for (; $bignum > 0; $bignum >>= 5){
$result = (( $bignum & 31 | 64) % 95) + 32;
echo chr($result);
}
live code を参照してください
out.println((char)(((l&31 | 64)%95)+ 32/1002439 * 1002439));
キャップにするには:3