二進(jìn)制
按位計(jì)算都是在二進(jìn)制上進(jìn)行的。
十進(jìn)制的位是個(gè)十栅表、百笋鄙、千、萬怪瓶、十萬萧落、百萬...987這個(gè)數(shù)字9在百位,8在十位洗贰,7在個(gè)位找岖,其實(shí)就是987=9x100+8x80+7x1。所以位就是數(shù)字的順序位置敛滋。
二進(jìn)制的位是1许布,2,4绎晃,8蜜唾,16,32...比如1011就是1x8+0x4+1x2+1x1=11箕昭。
Python里面用bin(n)
可以把整數(shù)n變?yōu)?b開頭的二進(jìn)制字符串灵妨,比如
>>> bin(11)
'0b1011'
>>> bin(9)
'0b1001'
異或運(yùn)算
Python中異或運(yùn)算的符號(hào)是6數(shù)字上面的^。
對(duì)于兩個(gè)二進(jìn)制落竹,比如1011和1001泌霍,把它們異或之后得到的是0010,因?yàn)閮蓚€(gè)二進(jìn)制同位上相同述召,那么結(jié)果二進(jìn)制這個(gè)位上就是0朱转,不同就是1,參考下面的代碼:
>>> 11^15
4
>>> print(bin(11));print(bin(15));print('0b0100')
0b1011
0b1111
0b0100
bin(4)其實(shí)是0b100积暖,等同于0b0100藤为。因?yàn)槲覀兌贾朗M(jìn)制0030就是30,前面的0只是占位置的夺刑。
注意1011^1111=0100這個(gè)事實(shí)缅疟,只看前兩位,簡(jiǎn)化一下的例子有10^11=01遍愿,只看后兩位11^11=00存淫。
更簡(jiǎn)單的例子是1^1=0,1^0=1沼填,0^1=1桅咆,0^0=0,其實(shí)就是每一位上相同得0坞笙,相異得1岩饼。
再看代碼中最后三行的數(shù)字荚虚,你會(huì)發(fā)現(xiàn)任意兩個(gè)異或的結(jié)果都是第三個(gè),11^15=4籍茧,11^4=15版述,15^4=11。兩個(gè)數(shù)字a硕糊,b異或后得到第三個(gè)數(shù)字c院水,那么這三個(gè)數(shù)字互為其余兩個(gè)數(shù)字的異或結(jié)果。
另外简十,任何數(shù)字和自己異或結(jié)果都是0檬某,因?yàn)槊恳晃欢枷嗤恳晃唤Y(jié)果就都是0螟蝙。
數(shù)據(jù)校驗(yàn)
假如你有一段文字恢恼,發(fā)送給我,我收到后如何確保中間傳輸?shù)臅r(shí)候沒有出錯(cuò)或者丟失或者被其他人中間做了修改胰默?
如果讓你再重新發(fā)送一遍场斑,我把兩遍收到的對(duì)比,可不可以牵署?不行漏隐!首先同樣的錯(cuò)誤很可能會(huì)再次發(fā)生,兩遍都錯(cuò)的可能性并不信浮青责;其次即使發(fā)現(xiàn)兩次收到的不同,但哪一次才是正確的呢取具?
換個(gè)思路脖隶,我們事先都約定好一個(gè)數(shù)字C比如100,你把要發(fā)送的數(shù)據(jù)A先和C做異或得到B暇检,把A和B都發(fā)給我产阱,我收到之后,再把收到的數(shù)據(jù)A0和B0做異或块仆,得到結(jié)果C0构蹬,C0再和C做異或,最終應(yīng)該是0悔据,這樣就可以確定數(shù)據(jù)是正確的了庄敛,如果不是0就表示數(shù)據(jù)出了問題。
為什么蜜暑?因?yàn)锽=A^C铐姚,我用B0^A0=C0策肝,再用C0^C=0自身異或?yàn)?完成驗(yàn)證肛捍。
當(dāng)然如果數(shù)據(jù)A很多隐绵,那么你可以用C和A中的每個(gè)數(shù)字做異或,得到最后一個(gè)簡(jiǎn)單的數(shù)字B拙毫,把這個(gè)數(shù)字B放在A的最前面和A一起打包成D發(fā)過來依许,我只要把D的第一個(gè)數(shù)字當(dāng)做B,其余當(dāng)做A就可以了缀蹄。
參照下面的代碼:
data = '你好峭跳,Python!' #要發(fā)送的數(shù)據(jù)
A = [ord(t) for t in data] #數(shù)據(jù)變成待傳送的列表[20320, 22909, 65292,...]
C = 100 #用C初始缺前,下面循環(huán)和A每一項(xiàng)做異或
for t in A:
C = C ^ t
B = C #B是C和A每一項(xiàng)異或的結(jié)果蛀醉,這時(shí)C已經(jīng)變成B,不再是100
D = [B] + A #拼合傳送數(shù)據(jù)
A0 = D[1:] #A0是D減去第一項(xiàng)剩余的其余部分
B0 = D[0] #用B0是D的第一項(xiàng)衅码,下面循環(huán)和A0每一項(xiàng)做異或
for t in A0:
B0 = B0 ^ t
C0 = B0 #C0是B0和A0每一項(xiàng)異或的結(jié)果
C0 ^ 100 #因?yàn)镃已經(jīng)不再是100拯刁,所以這里不能使用C
ord('a')
獲得字母a對(duì)應(yīng)的計(jì)算機(jī)ascii編碼數(shù)字,所有的字符包括中文日文逝段,每一個(gè)都對(duì)應(yīng)一個(gè)ascii編碼數(shù)字垛玻。[int(t) for t in '123']
語(yǔ)法得到的是[1,2,3]
如果你修改D=[B]+A+[33,1223,55]
那么最后結(jié)果就不再是0,表示數(shù)據(jù)傳輸時(shí)候出了錯(cuò)誤奶躯。
我們都知道電腦會(huì)不斷地發(fā)送信息給遠(yuǎn)程服務(wù)器帚桩,遠(yuǎn)程服務(wù)器也不斷的發(fā)送信息給電腦,自動(dòng)取款機(jī)也是一臺(tái)電腦嘹黔,如果你把它的網(wǎng)線拔下來插在你的筆記本上账嚎,然后編寫一個(gè)程序來假冒取款機(jī)發(fā)送信息給銀行遠(yuǎn)程服務(wù)器,那么你就可以直接修改你的存款余額参淹。
而有了校驗(yàn)機(jī)制醉锄,你編寫的程序因?yàn)椴恢廊】顧C(jī)使用的秘鑰(就是上面說的C=100),那么你發(fā)送的數(shù)據(jù)就會(huì)被銀行服務(wù)器認(rèn)為是錯(cuò)誤信息浙值。
實(shí)際情況要比這復(fù)雜很多恳不。
信息加密
仍然利用異或算法,我們可以把數(shù)據(jù)A和秘鑰C異或生成X开呐,A^C=X烟勋,如果你不知道C,即使截獲到X也沒有辦法讀取出A來筐付,所以異或本身就是一種加密方法卵惦。
如果你獲得了X,也知道C瓦戚,那么當(dāng)然可以用C^X=A得到A沮尿。這就是解密。
和上面的B不同,B只是一個(gè)數(shù)字畜疾,而X則是對(duì)A的每一個(gè)數(shù)字加密后得到的新數(shù)字列表赴邻。
可以從下面的代碼中理解:
def enctrypt(text, key):
text_asc_arr = [ord(t) for t in text] #將’你好 Python!‘轉(zhuǎn)為ascii碼列表[20320, 22909, 32,...]
key_asc_arr = [ord(t) for t in key] #將key轉(zhuǎn)為ascii碼列表[24,32,1543]
key_len = len(key_asc_arr)#秘鑰單詞長(zhǎng)度
for n in range(len(text_asc_arr)):
k = n % key_len #秘鑰單詞循環(huán)往復(fù)使用
text_asc_arr[n] = text_asc_arr[n] ^ key_asc_arr[k] #異或操作成為[451,223,116,...]
return ' '.join([bin(t)[2:] for t in text_asc_arr]) #返回字符串拼接格式‘1010101 101011 1100 01...'
def decrypt(text, key):
asc_str_arr = text.split(' ') #將字符串拆成列表‘1010101 1010...'-->['101010','1010',...]
text_asc_arr = [int(s, 2) for s in asc_str_arr] #轉(zhuǎn)成ascii列表[451,223,116,...]
key_asc_arr = [ord(t) for t in key]#轉(zhuǎn)為ascii碼列表[24,32,1543]
key_len = len(key_asc_arr)#秘鑰單詞長(zhǎng)度
for n in range(len(text_asc_arr)):
k = n % key_len#秘鑰單詞循環(huán)往復(fù)使用
text_asc_arr[n] = text_asc_arr[n] ^ key_asc_arr[k]#異或操作成為[20320, 22909, 32,...]
return ''.join([chr(int(t)) for t in text_asc_arr])#返回字符串拼接格式‘你好 Python啡捶!'
en = enctrypt('你好 Python姥敛!', '我很好')
de = decrypt(e, 'wobuhao')
de2 = decrypt(e, '我很好')
print(en)
print(de)
print(de2)
輸出的結(jié)果是
10110101110001 11011110101 101100101011101 110001001000001 101111111110001 101100100001001 110001001111001 101111111100111 101100100010011 1001110100010000
??夿戴徙奨或徐奼鵲
你好 Python!
其中de = decrypt(e, 'wobuhao')
使用了錯(cuò)誤的秘鑰'wobuhao'導(dǎo)致了解密出來都是亂碼瞎暑。
異或加密只是眾多加密方式中的一種彤敛。之所以常用異或進(jìn)行校驗(yàn)或簡(jiǎn)單加密,是因?yàn)楫惢蚴腔诙M(jìn)制計(jì)算的了赌,速度非常的快墨榄。