Bu tuhaflığı keşfettim:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
Çıktı:
hello world
Bu nasıl çalışıyor?
Bu tuhaflığı keşfettim:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
Çıktı:
hello world
Bu nasıl çalışıyor?
Yanıtlar:
Sayı 4946144450195624
64 bite uyar, ikili gösterimi:
10001100100100111110111111110111101100011000010101000
Program, sağdan sola her 5 bitlik grup için bir karakteri çözer
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
d | l | r | o | w | | o | l | l | e | h
5 bit için 2⁵ = 32 karakteri temsil etmek mümkündür. İngilizce alfabe 26 harf içerir, bu harfler dışında 32-26 = 6 semboller için yer bırakır. Bu kodlama şeması ile tüm 26 (bir vaka) ingilizce harf ve 6 sembol (aralarında boşluk olmak) olabilir.
>>= 5
Dongu olarak gruptan gruba atlar, daha sonra 5-bit grubu maskesi ile sayı operatörü kullanılarak izole olur 31₁₀ = 11111₂
cümledel & 31
Şimdi kod 5 bit değerini karşılık gelen 7 bit ascii karakteriyle eşler. Bu zor kısmı, aşağıdaki tabloda küçük harf alfabe harfleri için ikili sunumları kontrol edin:
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
Burada, eşlemek istediğimiz ascii karakterlerinin 7. ve 6. bit seti ( 11xxxxx₂
) ile başladığını görebilirsiniz (yalnızca 6. bit açık olan alan hariç), OR
5 bit kodlamayı96
(96₁₀ = 1100000₂
) ve bu haritalama yapmak için yeterli, ancak bu alan için işe yaramaz (darn space!)
Artık alanı diğer karakterlerle aynı anda işlemek için özel dikkat gösterilmesi gerektiğini biliyoruz. Bunu başarmak için kod çıkarılmış 5 bitlik grupta 7. biti (6. değil) OR 64 64₁₀ = 1000000₂
(l & 31 | 64
) .
Şimdiye kadar 5 bitlik grup şu şekildedir: 10xxxxx₂
(boşluk olurdu 1011111₂ = 95₁₀
). Alanı 0
diğer değerlerden etkilenmeyecek şekilde eşleyebilirsek, 6. biti açabiliriz ve hepsi bu olmalıdır. Parçanın mod 95
oynamaya geldiği yer, boşluk, 1011111₂ = 95₁₀
mod işlemini kullanarak (l & 31 | 64) % 95)
sadece alan geri gider 0
ve bundan sonra kod 32₁₀ = 100000₂
, önceki sonuca ekleyerek ((l & 31 | 64) % 95) + 32)
, 5 bit değerini geçerli bir ascii'ye dönüştürerek 6. biti açar 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
Aşağıdaki kod, küçük harfli bir dize (maks. 12 karakter) verildiğinde ters işlemi yapar, OP koduyla kullanılabilecek 64 bit uzun değerini döndürür:
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);
}
}
Yukarıdaki cevaplara biraz değer katıyoruz. Mükemmel komut dosyasının ardından ara değerler yazdırılır.
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));
}
İşte burada
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
İlginç!
Görünür olan standart ASCII karakterleri 32 ila 127 arasındadır.
Bu yüzden orada 32 ve 95'i (127 - 32) görüyorsunuz.
Aslında, her karakter burada 5 bit ile eşleştirilir, (her karakter için 5 bit kombinasyonun ne olduğunu bulabilirsiniz) ve daha sonra tüm bitler çok sayıda oluşturmak için birleştirilir.
Pozitif uzunlar 63 bit sayıdır, 12 karakterden oluşan şifreli formu tutacak kadar büyüktür. Bu yüzden tutacak kadar büyük Hello word
, ancak daha büyük metinler için daha büyük sayılar, hatta bir BigInteger kullanmalısınız.
Bir uygulamada görünür İngilizce Karakterleri, Farsça Karakterleri ve Sembolleri SMS ile aktarmak istedik. Gördüğünüz gibi var32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127
, 7 bit ile temsil edilebilecek olası değerler .
Her UTF-8 (16 bit) karakteri 7 bite dönüştürdük ve% 56'dan fazla sıkıştırma oranı elde ettik. Böylece, aynı sayıda SMS'de iki kat uzunluktaki metinler gönderebiliriz. (Bir şekilde burada da aynı şey oldu).
| 64
yaptığını açıklamıyor .
Karakterleri 5 bit değerleri olarak kodladınız ve 11 tanesini 64 bit uzunluğunda paketlediniz.
(packedValues >> 5*i) & 31
0-31 aralığında i-th kodlanmış değerdir.
Sabit kısım, dediğiniz gibi, alanı kodlamaktır. Küçük İngilizce harfleri Unicode (ve ascii ve diğer kodlamaların çoğunda) bitişik aralık 97-122'yi işgal eder, ancak boşluk 32'dir.
Bunun üstesinden gelmek için bazı aritmetik kullandınız. ((x+64)%95)+32
neredeyse aynıdır x + 96
(bu durumda OR'nin toplamaya ne kadar bit eşdeğer olduğuna dikkat edin), ancak x = 31 olduğunda, elde ederiz 32
.
Benzer bir nedenden dolayı "merhaba dünya" yazdırır:
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));
ama bundan biraz farklı bir nedenden dolayı:
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));
Olmadan Oracle
Etiket bu soruyu görmek zordu. Aktif ödül beni buraya getirdi. Sorunun başka alakalı teknoloji etiketleri de olmasını isterdim :-(
Çoğunlukla çalışıyorum Oracle database
, bu yüzden Oracle
yorumlamak ve açıklamak için biraz bilgi kullanırdım :-)
Numarayı 4946144450195624
dönüştürelim binary
. Bunun için function
dec2bin denilen küçük bir ondalık sayıyı kullanıyorum .
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>
İkili değeri almak için işlevi kullanalım -
SQL> SELECT dec2bin(4946144450195624) FROM dual;
DEC2BIN(4946144450195624)
--------------------------------------------------------------------------------
10001100100100111110111111110111101100011000010101000
SQL>
Şimdi yakalama 5-bit
dönüşüm. Her grupta 5 basamaklı sağdan sola gruplandırmaya başlayın. Biz: -
100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
Sonunda sağda biten int ile sadece 3 basamak kalacaktı . Çünkü ikili dönüşümde toplam 53 hane vardı.
SQL> SELECT LENGTH(dec2bin(4946144450195624)) FROM dual;
LENGTH(DEC2BIN(4946144450195624))
---------------------------------
53
SQL>
hello world
toplamda 11 karakter (boşluk dahil) olduğundan 2 eklememiz gerekir gruplandırmadan sonra yalnızca 3 bit ile kaldığımız son gruba bit .
Yani, şimdi: -
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
Şimdi, onu 7-bit ascii değerine dönüştürmemiz gerekiyor. Karakterler için kolaydır, sadece 6. ve 7. bitleri ayarlamamız gerekir. 11
Soldaki her 5 bitlik gruba ekleyin .
Bu verir: -
1100100|1101100|1110010|1101111|1110111|1111111|1101111|1101100|1101100|1100101|1101000
İkili değerleri yorumlayalım, kullanacağım 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>
Her bir ikili değere bakalım -
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>
Hangi karakter olduklarına bakalım: -
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>
Peki, çıktıda ne elde ediyoruz?
dlrow ⌂ olleh
Bu tersine merhaba dünya . Tek sorun alan . Ve nedeni cevabında @higuaro tarafından iyi açıklanmış. Cevabında verilen açıklamayı görene kadar, uzay meselesini ilk denemede kendim yorumlayamadım.
Aşağıdaki gibi PHP'ye çevrildiğinde kodu anlamak biraz daha kolay buldum:
<?php
$result=0;
$bignum = 4946144450195624;
for (; $bignum > 0; $bignum >>= 5){
$result = (( $bignum & 31 | 64) % 95) + 32;
echo chr($result);
}
Canlı koda bakın
out.println ((char) (((l & 31 | 64)% 95) + 32/1002439 * 1002439));
Kapaklar yapmak için: 3