良葛格 - 從40個判斷式到1個算式
2015-03-14
核心論點:優秀的程式設計師解題時,不會只往程式邏輯裡鑽——他懂音樂理論、數學規律、物理模型。本文作者用一個樂器小程式說明:40 個 if 判斷式,因為理解「十二平均律」而變成 1 個算式。程式能力是工具,跨領域知識才是讓工具發揮威力的關鍵。
本文轉載自 IThome,作者林信良。
在程式開發領域,手中如果能有多種工具,如此面對不同問題,才能使用最適合工具加以解決。
若要算出 1 加到 100 總和,該怎麼做呢?學過程式的人,八成會寫個 for 迴圈跑 1 到 100,重複進行加總來解決問題;現在函數式比較有人討論了,那來個遞迴好了,將目前數字與後續數字總和做相加,看來似乎比較高級;至於經常以數學思維來處理問題的人,則會告訴你 1 加 100 後乘以 100 再除以 2。
還有其他的思考方式嗎?
從 40 個判斷式開始:蠻力解法的陷阱
不久前在玩機器人積木,設計了個類似吉他的東西,打算用左手三個觸碰感應器區分八個狀態,在每個狀態下,右手可按下主機上五個按鈕各發出一個音階,也就是說,想設計出一個可以發出 40 個音階的樂器,其實可以再多一個觸碰感應器,以發出更多音階,不過,我先停下來思考一個問題,按照這樣設計,也表示觸碰感應器加上主機按鈕,總共會有 40 個可能的組合方式,該如何判斷狀態?
最基本的方式,就是建立 40 個 if 分支判斷,這方法行不通,光是建立 40 個分支這個過程就夠嚇人了,更何況,如果打算再多個觸碰感應器,就得再多出 40 個分支,才能讓樂器變成可以發出 80 個音階,稍微了解程式設計的開發者,都知道這種寫法完全就是個反模式,一定還有其他方式!
建立一個 40 個元素的陣列如何?陣列值為 01、02、03、04、05,表示未按任何觸控感應器下,各按下主機其中之一按鈕的情況,如果按下一個觸控感應器,就用 11、12、13、14、15 來代表主機各按鈕被按下,這麼一來,三個觸碰感應器可當成 2 進位數字,並轉換出 0 到 7 的 10 進位值,主機按鈕算出 1 到 5 的值,如此可跑迴圈,對應到陣列中某個元素值,如果每個元素值之後接上頻率,例如 01261626,去掉前兩位再除以 1000,那就是 261.626,也就是 Do 音階頻率。
這表示,我可以將 40 個判斷式,改用 40 個元素的陣列取代,如果要再多個觸碰感應器,那就是在陣列中追加 40 個元素,每個元素值之後同樣附上各音階頻率,追加陣列元素值是比追加 if 分支判斷來得簡單一些,只是得查出各音階頻率,仔細地附加到每個元素值之後,這滿麻煩的。
不過,我還是打開了瀏覽器,鍵入了「音階頻率」開始搜尋,在維基百科「音高」條目上找出「音高頻率表」,過程中,卻順手點進去看了一下「十二平均律」條目,不久,40 個元素的陣列就被拋棄了,只留下一個算式。
十二平均律的威力:一個音樂理論,解決一個程式問題
會被十二平均律條目吸引,在於最近接觸樂器,印象中看過幾次這個名稱。簡單來說,就是將一個八度(像是 Do、Re、Mi 到下個高音 Do)平均分成十二等份,每等分為一個半音,至於十二等分的劃分方式不是等差級數,而是等比級數,在十二平均律中,每個音頻率為前一個音的 2 的 12 次方根,約為 1.059463。例如,如果 Do 音為 261.626,那麼下個半音就是 261.626 乘上 1.059463,也就是 277.183(升 Do),再下個半音就是 261.626 × 1.059463 × 1.059463 = 293.665,也就是 Re。
對這個樂器小程式來說,單只是知道這樣就足夠了,之前設計的陣列元素 01、02、03、04、05、11、12……到 71、72、73、74、75,各可以對應到一個頻率,實際上,陣列元素換算過來 10 進位就是 1 到 40,一個觸碰感應器輸出值是 0 與 1,主機按鈕輸出值是 1 到 5,如果三個觸碰感應器輸出是 a、b、c,主機按鈕輸出是 d,換算公式就是 (a+b×2+c×4)×5+d,若結果為 n,且以頻率 261.626 開始,之後各個音的頻率就是 1.059463 的 n 次方,再乘上 261.626。
這麼一來,如果要再增加一個觸碰感應器,也只要調整為 (a+b×2+c×4+d×8)×5+e,不用使用龐大 if 分支判斷,也不用辛苦地建立陣列將狀態對照至頻率。只因為了解了十二平均律,真正使得設計方向得以朝簡化、有彈性的方向改變的,並不是多高深的程式設計觀念,單單只是音樂領域中的基礎知識。
不是所有東西都是釘子:程式思維的盲點與跨領域知識的價值
有句西方諺語說:「當你手上只有錘子時,所有東西都是釘子。」這句話若拿來套用在程式開發領域,通常是指手中不要只有一種工具,像是語言、程式庫或框架等,如此面對不同問題,才能使用最適合工具加以解決。
實際上,這句話可以擴大來看,擅長某領域知識的人,面對什麼事情,都會用他擅長的邏輯來處理事情,這本是好事,然而有時也會形成一種盲點,就像擅長程式設計思維的人,解決問題有時也很容易就往程式邏輯中鑽,就像一開始談到的 1 加到 100 問題。
1 加 100 後乘以 100 再除以 2 的解法,其實就是等差級數公式,首項與末項的和乘以項數除以 2,據說數學家高斯在小時候就發現這個公式,他的老師讓學生們做從 1 加到 100 的習題,大多數小孩應該都是從頭開始加,而高斯觀察到 1+100=101、2+99=101……,因而能很快說出 5050 的正確答案。
就觀察力這點,對數學家與程式人都是相當重要的能力,遇到問題時換個方向觀察規律,往往是從問題中理出頭緒的重要開始。壓縮、模式辨識等重大演算法,往往就是如此產生。
如果處理特定領域問題,更重要的,往往不是只用程式邏輯蠻幹,而是跳出既有程式領域,使用問題領域中的思維來尋求解答,這就是許多程式人所謂領域知識的重要性。1 加到 100 的總和,以迴圈來解複雜度會是 O(n),以等差級數公式來解決則會是 O(1);使用狀態的思維會是 40 個分支判斷,從十二平均律來解,卻只要一個算式!
一定還有其他方式!:用蒙地卡羅法求圓周率的啟示
從數學領域往其他領域看,也不乏這類例子,像是求圓周率並不是只能使用圓周長與半徑,也可透過亂數取樣模擬來解決,看看產生的數各有多少在圓內及圓外,因此導出圓周率公式,更早前還有 1777 年法國數學家 Buffon 的投針試驗(Buffon's needle),他在白紙上畫了等距平行線,並對白紙投擲長度為平行線間距一半、重量相等的針,在 2212 次投擲中,針與平行線相交 704 次,2212/704 約為 3.142,也就是圓周率近似值。
亂數取樣模擬求圓周率這類方式,稱為蒙地卡羅法(Monte Carlo method),Monte Carlo 為摩洛哥首都,也就是知名賭城,由此聯想此方式帶有賭博意味,現今則可使用電腦來產生亂數,將蒙地卡羅法應用在數值方法之中,像是計算不規則圖形的面積等。
已經有太多人呼籲過領域知識的重要性,只是每個人時間畢竟有限,不可能各個領域都能有所涉獵,更何況每個領域的專精,沒有個 5 年、10 年,也不足以成為專家。
就我這次的問題來說,也只是剛好問題夠簡單,能夠使用十二平均律,簡單地解決罷了。成為領域專家是個很迷人的目標,不過這之前,重點在於面對問題時,是否總能抱持著「一定還有其他方式」的想法——像是除了 1 加到 100 這類的問題之外,你知道費式數列也可以用數學公式來解嗎?
常見問題 FAQ
文章說「跨領域知識很重要」——孩子學程式,需要同時學音樂和數學嗎?
不需要刻意為了學程式而去學音樂或數學,但這篇文章說明的是:一個人本身擁有的各種知識和興趣,在解決程式問題時會自然發揮作用。橘子蘋果的課程設計中有一個核心理念——讓孩子做出「有意義的作品」,而不只是完成技術練習。做一個音樂遊戲、做一個數學視覺化工具,孩子在這個過程中自然會開始連接不同領域的知識。這種「跨領域的聯結力」才是真正的 21 世紀核心素養。
「40 個 if 判斷式 vs. 1 個算式」——這個概念對小孩有意義嗎?
這是程式設計裡最重要的思維之一:能不能找到更聰明的解法,而不是用蠻力硬解?這個思維對各年齡層都有意義。橘子蘋果的 Scratch 課讓孩子學「迴圈」,就是在教他們:「不用寫 100 行重複的程式,一個迴圈就夠了。」從 Scratch 到演算法課,這條路徑的核心就是讓孩子越來越懂得「觀察規律、化繁為簡」——這和高斯發現 1+100 的規律,本質上是同一件事。
複雜度 O(n) 和 O(1) 是什麼意思?小孩需要懂嗎?
O(n) 和 O(1) 是「演算法複雜度」的概念,O(n) 表示問題越大、花的時間越多(例如從 1 加到 100 要算 100 步);O(1) 表示不管問題多大,都只要一個步驟(例如用公式直接算出答案)。這是橘子蘋果演算法研究與應用班的核心內容,適合有程式基礎的國高中生(菁英課程第 8 階段)。年紀小的孩子不需要記住這個名詞,但理解「有些解法就是比其他解法快很多」,從 Scratch 階段就可以開始培養。
孩子學程式,什麼時候開始接觸「演算法思維」比較適合?
演算法思維的種子,可以從 Scratch 就開始種——讓孩子思考「有沒有更少步驟的方法?」但系統性地學習演算法(排序、搜尋、動態規劃、圖論等),適合有 Python 或 JavaScript 基礎的國高中生。橘子蘋果菁英課程的一條龍路徑中,第 8 階段「演算法研究與應用班」正是這個學習點;第 9 階段「AI 人工智慧班」更將演算法知識應用到機器學習場景中,讓孩子理解 AI 的底層邏輯。
「觀察規律、化繁為簡」的能力可以被刻意訓練嗎?
可以,而且這正是好的程式教育和一般補習班式教學的最大區別。一般教學讓孩子「背誦解法」;好的程式教育讓孩子「在遇到問題時,主動尋找規律」。橘子蘋果的雙師模式中,主講老師負責引導概念,助教老師個別幫孩子卡關——這不只是教語法,更是在培養孩子的「問題解決直覺」,也就是文章中所說的「面對問題時,是否總能抱持著一定還有其他方式的想法」。
這篇文章說「電腦科學的領域不僅僅是寫程式而已」,那孩子學程式的核心目標是什麼?
John MacCormick 在《改變世界的九大演算法》中說:「所有偉大的觀念,不需要會寫程式等電腦科學的知識就能解釋。」這句話點出了程式教育最深層的目標——不是訓練打字員,而是培養「計算思維」:把複雜問題拆解成步驟、尋找規律、用最簡單的方式解決問題。這種思維框架,在任何職業和生活情境中都有用。橘子蘋果每一堂課的設計,都以這個目標為核心,讓孩子「從做中學會思考」。
讓孩子學會找「那個更聰明的算式」——而不只是把 40 個 if 都寫完
- 橘子蘋果雙師模式,引導孩子發現規律、化繁為簡
- 從 Scratch 到演算法,系統培養計算思維
- 免費試聽一堂,讓孩子親身體驗「解題的快感」
