初等數學題解:化乘除爲加減

===》點我返回目錄《===

咱們知道四則混合運算加減乘除是算數的基礎,這裏面乘和除又能夠經過加減來實現,因此加減是更爲基本的運算。這些咱們小學就知道了,人類是這麼走過來的,計算機也是這麼走過來的。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的值:

  1. 101的右邊第一位爲1,將110左移0位獲得110
  2. 101的右邊第二位爲0,所以不計算(結果當成0)
  3. 101的右邊第三位爲1,將110左移2位獲得11000
  4. 把1,2,3三步的結果相加:11000+0+110=11110,該結果就是30。

除法相似的,是經過減法和移位操做實現的,咱們看一個例子。

咱們看815/23,手工按照這幾步從高位到低位作:

  1. 先用8除23,商爲0,餘數爲8;
  2. 餘數8左移,獲得80,再加上第二位1,獲得81,除23,商爲3,餘數爲12;
  3. 餘數12左移,獲得120,再加上第三位5,獲得125,除23,商爲5,餘數爲10。

計算完畢,獲得商爲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,按照以下步驟操做:

  1. 先拿最高位,1,跟110比大小,小,商爲0,餘數爲1;
  2. 餘數左移,爲10,再加上第二位數字0,得數仍是10,跟110比大小,小,商爲0,餘數爲10;
  3. 餘數左移,爲100,再加上第三位數字1,得數是101,跟110比大小,小,商爲0,餘數爲101;
  4. 餘數左移,爲1010,再加上第四位數字0,得數是1010,跟110比大小,大,商爲1,餘數爲100;
  5. 餘數左移,爲1000,再加上第五位數字1,得數是1001,跟110比大小,大,商爲1,餘數爲11;
  6. 餘數左移,爲110,再加上第六位數字0,得數是110,跟110比大小,大,商爲1,餘數爲0;
  7. 餘數左移,爲00,再加上第七位數字1,得數是1,跟110比大小,小,商爲0,餘數爲1;

計算完畢,最後的商爲0001110(即14),餘數爲1。

如今咱們就知道把四則運算化成了加減運算,之後咱們還會進一步將減法化爲加法。這樣從原理上,只須要有加法操做就能夠了。萬法歸一。

擴展一下,若是你在中學學得多一點,有可能學到了複數,咱們的程序又不能支持了。這裏咱們須要一個新的表達,用一個數是不行,複數包含實部和虛部,因此Python用12 + 3j表示。注意這裏用的是j,不是數學中的i,這麼作是爲了聽從電子工程界的慣例。

有了這個知識,咱們也是能夠寫出程序來實現複數域的運算的。我不演示程序了,大家本身作。

計算機好神奇啊,你確定會這麼想。它經過簡單數字(0/1)和基本運算疊加累計,最後構成了複雜的運算。可是這麼想並不全對,更準確的說法是計算機很笨而人很聰明,計算機只能作最簡單的操做,而人經過算法一步步讓計算機完成了複雜運算。一直到如今發展出Internet,智能手機,發射火箭,穿越星際空間,傳播視頻,遠程醫療,破譯DNA,發展無人駕駛汽車,可是咱們須知循着路徑回溯,一層層破解技術,最後的內核就是一量個簡單極了的操做孤單地擺在咱們眼前。正如大千世界,分析到了最後,就只有幾個基本粒子漂浮在虛無縹緲的時空。古來聖賢皆寂寞。

地球上現存的人類只有一種,統一叫作智人(Homo Sapiens)。

相關文章
相關標籤/搜索