咱們知道四則混合運算加減乘除是算數的基礎,這裏面乘和除又能夠經過加減來實現,因此加減是更爲基本的運算。這些咱們小學就知道了,人類是這麼走過來的,計算機也是這麼走過來的。python
咱們先來實現兩個數的乘法,a*b,按照定義就是把a自加b次,如3*4,就是把3本身加本身總共加四次。git
程序以下:算法
def multiply(a,b): if a==0 or b==0: return 0 elif a==1: return b elif b==1: return a else: r = 0 while b>=1: r = r + a b = b - 1 return r print(multiply(123,12))
程序的核心就是那個while循環,將a自加b次。api
你確定看出來了,上面的程序只能計算正整數,負數算不對。因此咱們再考慮一下正負數。有四種狀況:+ +,+ -,- +,- -。對這四種狀況,咱們得出它的結果的符號,而後化爲正數進行計算。程序以下:ide
def multiply(a,b): sign = 1 #default to positive if a>0 and b>0: sign = 1 elif a>0 and b<0: sign = -1 b = 0 - b # set b to positive elif a<0 and b>0: sign = -1 a = 0 - a # set a to positive elif a<0 and b<0: sign = 1 a = 0 - a # set a to positive b = 0 - b # set b to positive if a==0 or b==0: return 0 elif a==1: return b elif b==1: return a else: r = 0 while b>=1: r = r + a b = b - 1 if sign == 1: # positive return r else: #negative return 0 - r
好。咱們經過加法實現了乘法,循環這麼屢次,很笨很笨,可是確實是能幹活兒。一樣咱們也能夠用減法實現除法。函數
咱們再琢磨一下,若是b是實數就算不對了,由於有小數部分,而上面的循環只對整數有效。實際上實數是由兩個整數中間加點組成的,因此仍是能夠處理。有點複雜,暫不介紹。學習
好奇的你又會開始問了,計算機內部真的是這麼幹的嗎?答案是部分是,實際上,計算機內部是經過加法和移位操做聯合起來實現乘法的,爲了處理負數和實數,還須要用到補碼和規範化存儲規範。後面咱們會講到。測試
接下來看一下移位操做怎麼極大地加快了計算。小學學習的就是這個過程。code
舉個例子125*103,咱們上面的程序須要循環103次,看咱們怎麼移位加速:視頻
按照十進制的定義,103 = 1*102+0*101+3*100。這樣只要把125移位幾回再相加便可。分紅三部分:
1*102 將125移位2次,再乘1,獲得12500
0*101 0值,不計算
3*100 將125移位0次,再乘3,獲得375
而後把三部分相加12500+0+375=12875。
一樣獲得結果,只用了三次移位外加三次循環。
其實,Python裏面是有移位操做的<<。可是操做的結果有點跟想的不同,你能夠試一下45<<,結果會返回90,而不是450,你會有一點吃驚。緣由是由於計算機內部用二進制表示,左移一次等效於乘2。咱們能夠本身模擬寫一個shift函數:
def lshift(a,n): r = a while n>=1 : r = r * 10 n -= 1 return r
這個函數將一個數乘10,100,1000,至關於移位n次。本程序實現用到了乘法,這個只是示意,實際計算機裏面移位是一個獨立的基本運算。
這裏咱們還用到了一個新的表達 n -= 1,這是一個縮寫,做用等同於 n = n-1,可是執行效率可能會更高(依賴於具體的實現),起碼看起來更加專業。
再用這個lshift()改寫乘法程序:
def multiply(a,b): sign = 1 if a>0 and b>0: sign = 1 elif a>0 and b<0: sign = -1 b = 0 - b elif a<0 and b>0: sign = -1 a = 0 - a elif a<0 and b<0: sign = 1 a = 0 - a b = 0 - b if a==0 or b==0: return 0 elif a==1: return b elif b==1: return a else: r = 0 #result s = str(b) #convert to string for i in range(0,len(s)): # for each digit in the integer string n = int(s[len(s)-i-1]) # get the digit from right to left c=lshift(a,i) #shift while n>=1: r = r + c n = n - 1 if sign == 1: return r else: return 0 - r
程序的關鍵部分是那個嵌套循環:
for i in range(0,len(s)): # for each digit in the integer string n = int(s[len(s)-i-1]) # get the digit from right to left c=lshift(a,i) #shift while n>=1: r = r + c n = n - 1
這裏咱們看到了一個for語句,這是一個固定次數的循環,次數就是數字串的位數,如對103,循環次數就是3。i的取值依次爲0,1,2。以後,根據位置從右往左拿到該位置上的數字,即程序語句:n = int(s[len(s)-i-1]),如對103,第一次i爲0,那麼就是拿的3-0-1=2這個位置的數字,這個位置是103的最右邊一位(記得編號是從0開始的,因此最後一位也就是第三位的位置編號爲2)。拿到該數字後,先把被乘數移位,移i次,好比對103中的3,就移位0次,至關於不動,對1就要移位兩次。而後拿着這個移位後的數乘以n(經過加循環n次實現),即對103中的3,咱們把125循環三次相加獲得375,對於103中的0,咱們不計算,對於103中的1,咱們把12500循環1次相加獲得12500,因此最後的結果爲12875。拿出紙和筆,本身手工跟蹤一遍。
順便提一下,while循環裏的r=r+c, n=n-1寫成r += c, n -= 1更加顯得像是高手。
測試一下print(multiply(125,103))。
再解釋一下二進制下的乘法實現,爲之後講計算機內部實現作點準備。
咱們已經知道,計算機內部用二進制表示數,101010...,其數值等於k*2n+K*2n-1+...+k*20。
因此a*b就至關於a*k*2n+a*K*2n-1+...+a*k*20,即把a移位後累加。二進制的好處是它只有0和1兩個數字,因此不用考慮別的,直接移位或者不移位就能夠了(十進制裏面還要考慮個位數的乘法)。
試一下6*5,6的二進制表示爲110,5的二進制表示爲101,咱們計算110*101的值:
除法相似的,是經過減法和移位操做實現的,咱們看一個例子。
咱們看815/23,手工按照這幾步從高位到低位作:
計算完畢,獲得商爲035,即35,餘數爲10。
程序以下:
def lshift(a,n): r = a while n>=1 : r = r * 10 n -= 1 return r def divide(a,b): sign = 1 if a>0 and b>0: sign = 1 elif a>0 and b<0: sign = -1 b = 0 - b elif a<0 and b>0: sign = -1 a = 0 - a elif a<0 and b<0: sign = 1 a = 0 - a b = 0 - b if a==0: return 0 elif b==1: return a else: result = 0 remaining = 0 s = str(a) for i in range(0,len(s)): # for each digit in a n = int(s[i]) # get the digit from left to right divident = lshift(remaining,1)+n if divident>=b: #divide count=0 while divident>=b: count = count + 1 divident = divident - b result = lshift(result,1) + count remaining = divident else: result = lshift(result,1) + 0 remaining = divident if sign == 1: return result else: return 0 - result
測試print(divide(815,23))。大家仿照乘法的過程本身把除法跟蹤一遍。
二進制除法也是一樣的原理,也由於只有0,1兩個數,因此比十進制簡單,不用除個位數,只須要比大小,大就是1,小就是0。
咱們試一下85/6,用二進制表示爲1010101/110,按照以下步驟操做:
計算完畢,最後的商爲0001110(即14),餘數爲1。
如今咱們就知道把四則運算化成了加減運算,之後咱們還會進一步將減法化爲加法。這樣從原理上,只須要有加法操做就能夠了。萬法歸一。
擴展一下,若是你在中學學得多一點,有可能學到了複數,咱們的程序又不能支持了。這裏咱們須要一個新的表達,用一個數是不行,複數包含實部和虛部,因此Python用12 + 3j表示。注意這裏用的是j,不是數學中的i,這麼作是爲了聽從電子工程界的慣例。
有了這個知識,咱們也是能夠寫出程序來實現複數域的運算的。我不演示程序了,大家本身作。
計算機好神奇啊,你確定會這麼想。它經過簡單數字(0/1)和基本運算疊加累計,最後構成了複雜的運算。可是這麼想並不全對,更準確的說法是計算機很笨而人很聰明,計算機只能作最簡單的操做,而人經過算法一步步讓計算機完成了複雜運算。一直到如今發展出Internet,智能手機,發射火箭,穿越星際空間,傳播視頻,遠程醫療,破譯DNA,發展無人駕駛汽車,可是咱們須知循着路徑回溯,一層層破解技術,最後的內核就是一量個簡單極了的操做孤單地擺在咱們眼前。正如大千世界,分析到了最後,就只有幾個基本粒子漂浮在虛無縹緲的時空。古來聖賢皆寂寞。
地球上現存的人類只有一種,統一叫作智人(Homo Sapiens)。