程式設計需要知道多少數學知識?

唐小娟發表於2014-08-14

      關於程式設計師到底要掌握多少數學知識,不同的人有不同的觀點,下面這篇文章如是說......

 

“How much math do I need to know to program?” Not That Much, Actually.

 

Al Sweigart is a software developer in San Francisco, he has written two books about games with python.

     

       下面是我在reddit的子論壇 r/learnprogramming 看到的幾個帖子:

       · “要成為一個優秀的程式設計師需要學習多少數學?

       · “我應該重新學習數學嗎?

       · “這可能是我提問過的最愚蠢的一個問題。成為一個優秀的程式設計師究竟需要學習多少數學?

      數學和程式設計有一種容易讓人誤解的聯絡,許多人認為在開始學習程式設計之前必須對數學很在行或者數學分數很高。但一個人為了程式設計的話,需要學習多少數學呢?

      實際上不需要很多這篇文章中我會深入探討程式設計中所需要的數學知識。你可能已經都知道了。

 

      對於基本的程式設計,你需要知道下面的:

      ● 加減乘除 — 實際上,電腦會幫你作加減乘除運算。你僅需要知道什麼時候運用它們。

      ● 模運算 — 模運算是用來計算餘數,它的符號通常用%百分號來表示。所以23除以7等於3,餘數是2。23 mod 7 = 2。

      ● 判斷是奇數還是偶數的模運算 — 如果你想知道一個數是奇數還是偶數,用它mod 2來作模運算。如果結果是0,它就是偶數。如果結果是1,就是奇數。23 mod 2等於1,所以23是奇數,24 mod 2等於0,24是偶數。

      ● 對一個數作百分數運算,就是用這個數來乘以一個百分數。譬如你要得到279的54%,就是用0。54*279。這就意味著為什麼1.0等於100%,0.0等於0%。

      ● 知道負數是什麼。負數乘以負數等於正數。負數乘以正數等於負數。就這麼簡單。

      ● 知道笛卡爾座標系統。在程式設計中,(0,0)代表螢幕左上角,Y座標的正軸往下。

      ● 知道勾股定律,因為它是用來計算笛卡爾座標中兩點之間的距離的。勾股定律a^2 + b^2 = c^2。(x1, y1)和(x2, y2)兩點之間的距離等於( (x1 – x2)^2 + (y1 – y2)^2 )。

      ● 知道十進位制、二進位制、十六進位制。十進位制就是我們通常用的十個數:0-9。通常認為這個十進位制系統是人類發明的,因為我們有十個手指。

       電腦採用二進位制資料,只有兩個數字:0和1。這是因為我們用電子元件來構建的電腦,讓電腦只識別兩種狀態更便宜些(一種代表0,另一種代表1)。

       數是一樣的,但是在不同的進位制系統裡的表現形式不同,因為不同進位制包含的數的個數不同。十六進位制比十進位制多六個數字,所以我們用A-F表示超過9的數。能夠表現這些進位制系統的最簡單方法就是用一個計數器(odometer)。下面三種不同的計數器顯示的是同一個數,但在不同的進位制系統中的形式不同:

在新視窗中檢視計數器頁面

      你甚至不需要知道怎麼從一個進位制系統轉換成另一個系統。每種程式語言都有幫你轉換的函式。

      (提示一下,十六進位制的使用是因為一個十六進位制的數可以表示四個二進位制的數。因為十六進位制中的3和二進位制中的0011對應,十六進位制的A和二進位制的1010對應,所以十六進位制中的3A(十進位制的58)可以寫成二進位制的00111010。十六進位制在程式設計中的使用是因為它是對二進位制的簡化。沒人喜歡寫出的數全是0和1。)

      就是這麼多了。除了進位制系統以外,你可以已經知道程式設計所需的數學知識了。雖然普遍認為程式設計需要學習許多數學,但實際上並不需要那麼多。你可能為了編寫一個程式,譬如說地震模擬器,而需要學習數學。其實你更需要學習地震的數學,而不是因為要編寫地震模擬器而學習數學。

 

某些程式設計領域中更為高階的數學

       有一些領域中需要更多的數學知識(但95%的軟體中,你都不需要知道它們。)

      ● 3D遊戲和3D繪圖 — 3D通常需要涉及三角函式和線性代數(用矩陣來解決問題的數學)。當然,有許多3D圖形庫已經實現了這些數學程式設計,你不需要知道這些數學。

      ● 2D物理(譬如憤怒的小鳥)和3D物理(譬如許多流行的3D遊戲) — 為了寫涉及到物理的程式設計,你需要學習一些物理方程和公式(尤其是力學,如彈力,重力,球滾下斜坡等物理。)然而,已經有一些物理引擎和軟體庫幫你實現了,所以你也不需要知道遊戲(如憤怒的小鳥)中的物理公式。

      ● 加密學 — 事實上我指的是RSA。你需要知道質數的有關知識,以及如何求最大公約數(其實是個非常簡單的演算法,還有許多程式語言中都有gcd()函式,幫你求解最大公約數)其他的編碼大部分就是將資料按照某種步驟挪動。舉個例子,下面的flash就是AES“Rijndael”編碼的步驟。所有的步驟包含用一些數減去另一些數,將行向上移,將列數字打亂,再作簡單的加法運算。

       如果你要寫你自己的加密演算法(通常不需要你做,因為已經有許多很好的工具了,並且如果你不是加密學的專家的話,你的程式也許會很容易被破解。)如果你僅僅想加密一些資料的話,已經有許多加密和解密的軟體庫了。

       所以就算是以上的情況,你也不需要真正的知道3D影象,物理或者加密的數學。你只需要學習運用軟體庫就行了。

 

程式設計需要學習什麼?

       你需要學習的是如何建模和設計演算法。這意味著,如何將真實世界的運算或者資料處理抽象出來,寫出程式碼,讓計算機來幫你運算。例如,在遊戲“龍與地下城”(Dungeons and Dragons)中,角色和怪獸都有許多不同的戰鬥統計值:

      ● 血點(Hit points)是一個人死前所能經受的傷害值。越高的血點就意味著可以經受更多的傷害。

      ● 防禦等級(armor class)是對你的武器防禦能力的量度。防禦值越低,武器的防禦能力越高。

      ● THAC0(讀作“thay-co”,“To Hit Armor Class 0”),是對一個人進行有效攻擊的能力的測量。THAC0值越低,攻擊越準。

      ● 武器的攻擊力用類似1d6+2來表示,它表示搖一個六面骰得到的值,然後再加2。2d4就是搖2個4面骰,然後將它們相加。(“龍與地下城”採用的是4,6,8,10,12和20面骰。)

 

       要看攻擊者打防禦者,讓攻擊者搖動一個20面骰。如果這個數字大於或等於攻擊者的THAC0減去防禦者的防禦能力,那麼這個攻擊就成功,防禦者將受到傷害。不然,防禦者就阻擊了這個攻擊,並且不費血。

       我們假設兩個人物,Alice和Bob,他們具有以下值:

       ● Alice: HP 14, AC 5, THAC0 18, DAMAGE 1d6

       ● Bob: HP 12, AC 7, THAC0 16, DAMAGE 2d4

       所以Alice有更多的血點和防禦力(記住,AC越低越好)。但是Bob更可能成功擊中對方(記住,THAC0越低越好),並造成更多的傷害。我們說Bob的攻擊力更強是因為2d4可以造成2-8點傷害,而Alice的1d6只能造成1-6點傷害。(如果你懂統計學,你可以計算出Bob的期望傷害值是5,比Alice的3.5要高。)

       你會打賭Alice或者Bob會贏得比賽對嗎?很難講誰會贏,他們看起來勢均力敵。儘管可能你的統計學學得很好,但做這個計算將會十分頭疼。編寫“龍與地下城”的程式(模擬戰鬥過程),你甚至不需要知道統計學。僅僅需要執行幾百次或者幾千次戰鬥,看看誰贏得更多。

       下面是用Python寫的程式:(下載程式碼

import random, copy
NUM_FIGHTS = 1
VERBOSE = True
# Lower thac0 and lower ac values are better. Higher damage & hp values are better.
aliceTemplate = {'name': 'Alice', 'hp': 14, 'ac': 5, 'thac0': 18, 'dmgnum': 1, 'dmgsize':6, 'dmgmod': 0}
bobTemplate   = {'name': 'Bob',   'hp': 12, 'ac': 7, 'thac0': 16, 'dmgnum': 2, 'dmgsize':4, 'dmgmod': 0}
def display(s):
    if VERBOSE:
        print(s)
def attack(attacker, defender):
    if random.randint(1, 20) >= attacker['thac0'] - defender['ac']:
        damage = 0
        for i in range(attacker['dmgnum']):
            damage += random.randint(1, attacker['dmgsize'])
        damage += attacker['dmgmod']
        display('%s (%s hp) hits %s (%s hp) for %s points of damage. %s is reduced to %s hp.' % (attacker['name'], attacker['hp'], defender['name'], defender['hp'], damage, defender['name'], defender['hp'] - damage))
        defender['hp'] -= damage
    else:
        display('%s misses %s.' % (attacker['name'], defender['name']))
aliceWins = 0
bobWins = 0
for i in range(NUM_FIGHTS):
    display('======================')
    display('Start of combat #%s' % (i+1))
    alice = copy.deepcopy(aliceTemplate)
    bob = copy.deepcopy(bobTemplate)
    while True:
        attack(alice, bob)
        if bob['hp'] <= 0:
            break
        attack(bob, alice)
        if alice['hp'] <= 0:
            break
    if alice['hp'] <= 0:
        display('Alice has died.')
        bobWins += 1
    if bob['hp'] <= 0:
        display('Bob has died.')
        aliceWins += 1
print()
print('Alice won %s (%s%%) fights. Bob won %s (%s%%) fights.' % (aliceWins, round(aliceWins / NUM_FIGHTS * 100, 2), bobWins, round(bobWins / NUM_FIGHTS * 100, 2)))


       當執行這個程式時,你會看到:

======================
Start of combat #1
Alice misses Bob.
Bob (12 hp) hits Alice (14 hp) for 6 points of damage. Alice is reduced to 8 hp.
Alice misses Bob.
Bob misses Alice.
Alice misses Bob.
Bob misses Alice.
Alice misses Bob.
Bob misses Alice.
Alice (8 hp) hits Bob (12 hp) for 5 points of damage. Bob is reduced to 7 hp.
Bob misses Alice.
Alice misses Bob.
Bob misses Alice.
Alice misses Bob.
Bob (7 hp) hits Alice (8 hp) for 2 points of damage. Alice is reduced to 6 hp.
Alice (6 hp) hits Bob (7 hp) for 6 points of damage. Bob is reduced to 1 hp.
Bob misses Alice.
Alice (6 hp) hits Bob (1 hp) for 1 points of damage. Bob is reduced to 0 hp.
Bob has died.
Alice won 1 (100.0%) fights. Bob won 0 (0.0%) fights.

       但是可能Alice正好在某一次戰鬥中很幸運。讓我們關掉輸出再重新執行程式(在螢幕輸出比執行程式更耗時間),當戰鬥次數達到30,000次時(將NUM_FIGHTS改成30000,VERBOSE變數變成False):

Alice 贏得12909 (43.03%)次戰鬥. Bob贏得17091 (56.97%)戰鬥。

       所以我們看到使用上面的數值,Bob稍稍佔先。電腦進行了30,000次戰鬥模擬。如果我們用筆和紙還有骰來進行30000次戰鬥模擬的話,可能需要幾個月來算出結果,而我的筆記本僅用了8秒。

       那麼如果Alice的血點從14增加到20呢。誰會贏呢?

Alice贏得19438 (64.79%)次戰鬥. Bob贏得10562 (35.21%)次戰鬥.

       我們看到給Alice增加6點血點,結果倒過來了,Alice佔先了。那麼如果Alice的血點只是增加到16呢?

Alice贏得15176 (50.59%)次戰鬥啊. Bob贏得14824 (49.41%)次戰鬥.

       所以僅僅增加2個血點,就已經足夠扳回Bob更強攻擊力的勝算。

       來看這個程式,它只是在用加減乘除來計算一個百分比時用到了數學。甚至在更復雜的程式中,需要表示magic spells,治癒部位,多次攻擊,在戰鬥中切換武器等不同效果時,我們也不需要知道更多的數學了。

 

       當然,去學更多的數學吧。可以讓你成為更出色的程式設計師。但是為了學習程式設計需要多少數學?真的非常少。

       更新:我想我應該在基本知識點中增加基本代數,但僅僅需要知道的如 如果X * 3 = 12,知道X等於4。

 

原文:Al Sweigart  編譯:伯樂線上 – 唐小娟 

相關文章