更多數學趣題:Pascal/楊輝三角

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

Pascal/楊輝三角型如下:

或者是

中國南宋數學家楊輝1261年所著的《詳解九章算法》一書中出現了這個三角。Pascal帕斯卡在1654年發現這一規律(13歲時發現的),所以這個表又叫做Pascal三角。

它的特點是下面一行的數字由上面一行的數字兩兩相加而來(從上圖等腰三角形看得很清楚。)既然知道了這個特點,我們程序思路就有了,通過前一行算後一行,用循環即可以做到。

我們可以先提煉出一個函數,根據上一行得出下一行:

def calculaterow(n,lastrow):

    currentrow=[]

    z=0

    while z < n: #initial current row

        currentrow.append(0)

        z+=1

    currentrow[0]=1  #the first element is always 1

    for i in range(1,n-1): #calculate

        currentrow[i]=lastrow[i-1]+lastrow[i]

    currentrow[n-1]=1  #the last element is always 1

    return currentrow

這個函數的傳入參數有兩個,n和lastrow,n表示第幾行,lastrow是上一行的數組(因爲一行有多個值,所以我們用一個數組記錄一行值)。currentrow是第n行的數組,元素個數等於n,開頭初始化的時候全部給0,一頭一尾賦值爲1。

有了這個函數,我們就可以寫一個程序一行行輸出了:

def drawtriangle(row):

    arr = [1,1] #the first row

    for i in range(3,row+1): 

        s = ""

        arr = calculaterow(i,arr) #calculate the next row

        for element in arr: #convert array to string

            s += str(element)

            s += " "

        print (s)

print("1 ")

print ("1 1 ")

arr = drawtriangle(7)

測試一下,輸出:

1

1 1

1 2 1

1 3 3 1

1 4 6 4 1

1 5 10 10 5 1

1 6 15 20 15 6 1

從程序的角度來講,上面的程序其實編寫得不是很好。它沒有把整個楊輝三角保存記錄下來,而是在循環過程中動態輸出的。處理到下一行,上一行的數據就沒有了。

爲了保存下來,我們可以這麼修改程序:

def calrows(n):

    rows=[[1],[1,1]]

    for i in range(2,n):

        row=[1]

        for j in range(1,i):

            row.append(rows[i-1][j-1]+rows[i-1][j])

        row.append(1)

        rows.append(row)



    return rows

這個程序我們用到了二維數組,rows=[[1],[1,1]]語句時二維數組的初始化,放了兩個元素(行),每一個元素都是一個數組。第一個數組只有一個元素,第二個數組又兩個元素。按照楊輝三角的定義,我們循環生成了三角形的所有元素。

另外我們對這個輸出結果不是很滿意,輸出的是一個直角三角形,看起來不那麼直觀,等腰三角形會比較好。

我們看一下如何變成等腰?其實即使給每一行前面加空格就可以了,所以我們得有一個算法直到要空出多少格來。最簡單的想法是空出n-i格來,如上面的三角,共有七行,第一行就前面補6格空格,第二行5個,程序容易改,只要print的地方改成print (" "*(row-i)+s)就可以了,我們看看效果:

 

比起這個直角三角形,上面的圖形顯得對稱多了,不過你也肯定看出來了,好像沒有那麼等腰,有一點歪了。原因在於生成的數字不只一位數,會有兩位數,三位數,很快就會有更多位數。如果每一行固定的錯一格,肯定就會歪到一邊去。

所以我們得有個辦法計算出一行的所有數字的長度。最重要的,要計算出最後一行的數字長度。事實上我們在上面已經算出最後一行的數組來了,我們寫一個函數,根據數組裏的數字算出最後一行佔的字符數。

def arraylength(ar):

    maxs = ""

    for e in ar:

        maxs += str(e)

        maxs += " "

    return  len(maxs)-1

想法很簡單,就是拿數組的元素拼串,中間隔一個空格。

接下來我們就可以看某一行前面需要加多少空格了。簡單算一下就知道,那最後的那行的長度減去本行的長度,除以2,就是補的空格數。如最後一行有50個字符長度,本行是20個字符,要想這兩行看起來是中對齊的,那麼本行前面要補(50-20)/2=15個空格。

我們也做這麼一個函數:

def scenter(strin,maxlen):

    strn = len(strin)

    spaces = int((maxlen-strn)/2)

    return " "*spaces  + strin

有了上面的函數,我們實現更等腰的三角形就比較容易了:

lastarr = getlastrow(integertest)

maxleng = arraylength(lastarr)

print(scenter("1 ",(maxleng)))

arr = drawtriangle(integertest)

先拿到最後一行,然後再重新輸出三角形。

 

這下子看起來舒服多了。

Pascal三角還有一個很好玩的性質,我們看一下直角三角形能看出來:

 

竟然是斐波那契數列!意不意外?驚不驚喜?

Pascal三角並不僅僅是一個好玩的數學遊戲,它是二項式係數在三角形中的一種幾何排列,也就是二項式定理。(a+b)n的展開式中的各項係數依次對應楊輝三角的第(n+1)行中的每一項。第4行的四個數恰好依次對應兩數和的立方的展開式的每一項的係數,即

又因爲三角中第n行的m個數可表示爲 C(n-1,m-1),即爲從n-1個不同元素中取m-1個元素的組合數。因此可得出二項式定理的公式爲:

 

楊輝三角把二項式係數圖形化,這是中國古代數學的傑出研究成果之一。說起來,中國古代的數學成就總體上還是領先於世界的,但是中國思想的通病是以實用爲主,不尋求純粹知識真理的探索,數學上也是主要考慮計算,不求原理,稱爲“寓理於算”,所以漸漸就沒有後勁了,數學成績一直停留在初等數學的範疇。這是非常可惜和要反思的。

也因爲二項式定理與Pascal三角的這種緊密關係,人們就喜歡來回出題,通過一個解另一個。如問給定一個多項式(by+ax)k,求出多項式展開後xn *ym 項的係數。這個題目可以用二項式定理來解,也可以用Pascal三角來解。用二項式定理,公式表示爲:

所以xn *ym項的係數就是C(k,n)*anbk-n。而求組合數,只要用階乘除法即可。

有的時候,會轉一個彎,還有這麼問的,求對於所有的0 <= i <= n,0 <= j <= min(i,m),有多少對 (i,j)滿足C(i,j)是k的倍數。因爲我們知道了Pascal三角中第n行的m個數可表示爲 C(n-1,m-1),所以這個問題轉換成在Pascal三角數中找K的倍數的數的個數。

對剛纔我們計算保存Pascal三角二維數組的程序稍微修改一下,在計算除數字的時候判斷下是否能被K整除,就可以算出這個題目:

def calrows(n,k):

    count = 0

    rows=[[1],[1,1]]

    for i in range(2,n):

        row=[1]

        for j in range(1,i):

            row.append(rows[i-1][j-1]+rows[i-1][j])

            if (rows[i-1][j-1]+rows[i-1][j])%k==0:

                count += 1

        row.append(1)

        rows.append(row)



    print(count)

    return rows

這個是2016年的中國青少年編程競賽題目。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章