Saya menemukan keanehan ini:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
Keluaran:
hello world
Bagaimana cara kerjanya?
Saya menemukan keanehan ini:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
Keluaran:
hello world
Bagaimana cara kerjanya?
Jawaban:
Jumlahnya 4946144450195624
pas 64 bit, representasi binernya adalah:
10001100100100111110111111110111101100011000010101000
Program mendekode karakter untuk setiap grup 5-bit, dari kanan ke kiri
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
d | l | r | o | w | | o | l | l | e | h
Untuk 5 bit, dimungkinkan mewakili 2⁵ = 32 karakter. Alfabet bahasa Inggris berisi 26 huruf, ini menyisakan ruang untuk 32 - 26 = 6 simbol terpisah dari huruf. Dengan skema kodifikasi ini Anda dapat memiliki semua 26 (satu huruf) huruf bahasa Inggris dan 6 simbol (menjadi spasi di antara mereka).
The >>= 5
dalam for-loop melompat dari grup ke grup, maka grup 5-bit terisolasi DAN nomor dengan topeng 31₁₀ = 11111₂
dalam kalimatl & 31
Sekarang kode memetakan nilai 5-bit ke karakter ascii 7-bit yang sesuai. Ini adalah bagian yang sulit, periksa representasi biner untuk huruf alfabet huruf kecil dalam tabel berikut:
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
Di sini Anda dapat melihat bahwa karakter ascii yang ingin kami petakan dimulai dengan set bit ke-7 dan ke-6 ( 11xxxxx₂
) (kecuali untuk ruang, yang hanya memiliki bit ke-6), Anda dapat OR
menggunakan kodifikasi 5-bit dengan 96
( 96₁₀ = 1100000₂
) dan itu seharusnya cukup untuk melakukan pemetaan, tetapi itu tidak akan berhasil untuk ruang (ruang sialan!)
Sekarang kita tahu bahwa perhatian khusus harus diambil untuk memproses ruang pada saat yang sama dengan karakter lain. Untuk mencapai ini, kode mengaktifkan bit ke-7 (tetapi bukan ke-6) pada grup 5-bit yang diekstraksi dengan OR 64 64₁₀ = 1000000₂
( l & 31 | 64
).
Sejauh ini kelompok 5-bit berbentuk: 10xxxxx₂
(ruang akan 1011111₂ = 95₁₀
). Jika kita dapat memetakan ruang untuk 0
menghapus nilai-nilai lain, maka kita dapat mengaktifkan bit ke-6 dan itu saja. Inilah mod 95
bagian yang harus dimainkan, space 1011111₂ = 95₁₀
, menggunakan operasi mod, (l & 31 | 64) % 95)
hanya ruang yang kembali 0
, dan setelah ini, kode menghidupkan bit ke-6 dengan menambahkan 32₁₀ = 100000₂
ke hasil sebelumnya, ((l & 31 | 64) % 95) + 32)
mengubah nilai 5-bit menjadi ascii yang valid karakter
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
Kode berikut melakukan proses invers, diberi string huruf kecil (maks 12 karakter), mengembalikan nilai panjang 64 bit yang dapat digunakan dengan kode 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);
}
}
Menambahkan beberapa nilai ke jawaban di atas. Script groovy berikut mencetak nilai-nilai perantara.
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));
}
Ini dia
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
Menarik!
Karakter ASCII standar yang terlihat berada dalam kisaran 32 hingga 127.
Itu sebabnya Anda melihat 32, dan 95 (127 - 32) di sana.
Sebenarnya setiap karakter dipetakan ke 5 bit di sini, (Anda dapat menemukan apa kombinasi 5 bit untuk setiap karakter), dan kemudian semua bit digabungkan untuk membentuk angka yang besar.
Panjang positif adalah angka 63 bit, cukup besar untuk menampung bentuk terenkripsi 12 karakter. Jadi cukup besar untuk menampung Hello word
, tetapi untuk teks yang lebih besar Anda harus menggunakan angka yang lebih besar, atau bahkan BigInteger.
Dalam suatu aplikasi kami ingin mentransfer Karakter Bahasa Inggris, Karakter Persia dan Simbol yang terlihat melalui SMS. Seperti yang Anda lihat ada 32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127
nilai yang mungkin, yang dapat direpresentasikan dengan 7 bit.
Kami mengkonversi setiap karakter UTF-8 (16 bit) menjadi 7 bit, dan mendapatkan lebih dari rasio kompresi 56%. Jadi kami dapat mengirim teks dengan panjang dua kali lipat dalam jumlah SMS yang sama. (Entah bagaimana hal yang sama terjadi di sini).
| 64
dilakukan.
Anda telah menyandikan karakter sebagai nilai 5-bit dan mengemas 11 di antaranya menjadi panjang 64 bit.
(packedValues >> 5*i) & 31
adalah nilai enkode ke-i dengan rentang 0-31.
Bagian yang sulit, seperti yang Anda katakan, adalah pengkodean ruang. Huruf bahasa Inggris kecil menempati kisaran yang berdekatan 97-122 di Unicode (dan ascii, dan sebagian besar penyandian lainnya), tetapi ruangnya 32.
Untuk mengatasinya, Anda menggunakan aritmatika. ((x+64)%95)+32
hampir sama dengan x + 96
(perhatikan bagaimana bitwise OR setara dengan penambahan, dalam kasus ini), tetapi ketika x = 31, kita dapatkan 32
.
Mencetak "hello world" untuk alasan yang sama seperti ini:
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));
tetapi untuk alasan yang agak berbeda dari ini:
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));
Tanpa Oracle
tag, sulit untuk melihat pertanyaan ini. Karunia aktif membawaku ke sini. Saya berharap pertanyaannya memiliki tag teknologi lain yang relevan juga :-(
Saya kebanyakan bekerja dengan Oracle database
, jadi saya akan menggunakan beberapa Oracle
pengetahuan untuk menafsirkan dan menjelaskan :-)
Mari kita konversi nomornya 4946144450195624
menjadi binary
. Untuk itu saya menggunakan function
dec2bin kecil bernama 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>
Mari kita gunakan fungsi untuk mendapatkan nilai biner -
SQL> SELECT dec2bin(4946144450195624) FROM dual;
DEC2BIN(4946144450195624)
--------------------------------------------------------------------------------
10001100100100111110111111110111101100011000010101000
SQL>
Sekarang tangkapannya adalah 5-bit
konversi. Mulai pengelompokan dari kanan ke kiri dengan 5 digit di setiap grup. Kita mendapatkan :-
100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
Kami akhirnya akan dibiarkan dengan hanya 3 digit di ujungnya di kanan. Karena, kami memiliki total 53 digit dalam konversi biner.
SQL> SELECT LENGTH(dec2bin(4946144450195624)) FROM dual;
LENGTH(DEC2BIN(4946144450195624))
---------------------------------
53
SQL>
hello world
total memiliki 11 karakter (termasuk spasi), jadi kita perlu menambahkan 2 bit ke grup terakhir yang tersisa hanya 3 bit setelah pengelompokan.
Jadi, sekarang kita memiliki: -
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
Sekarang, kita perlu mengubahnya menjadi nilai ascii 7-bit. Untuk karakter itu mudah, kita hanya perlu mengatur bit 6 dan 7. Tambahkan 11
ke setiap grup 5-bit di atas ke kiri.
Itu memberi: -
1100100|1101100|1110010|1101111|1110111|1111111|1101111|1101100|1101100|1100101|1101000
Mari kita menafsirkan nilai-nilai biner, saya akan gunakan 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>
Mari kita lihat setiap nilai biner -
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>
Mari kita lihat karakter apa mereka: -
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>
Jadi, apa yang kita dapatkan di output?
dlrow ⌂ olleh
Itu hello⌂world secara terbalik. Satu-satunya masalah adalah ruang . Dan alasannya dijelaskan dengan baik oleh @ higuaro dalam jawabannya. Jujur saya tidak bisa menafsirkan masalah ruang angkasa pada upaya pertama, sampai saya melihat penjelasan yang diberikan dalam jawabannya.
Saya menemukan kode sedikit lebih mudah dimengerti ketika diterjemahkan ke dalam PHP, sebagai berikut:
<?php
$result=0;
$bignum = 4946144450195624;
for (; $bignum > 0; $bignum >>= 5){
$result = (( $bignum & 31 | 64) % 95) + 32;
echo chr($result);
}
Lihat kode langsung
out.println ((char) (((l & 31 | 64)% 95) + 32/1002439 * 1002439));
Untuk membuatnya tutup: 3