以APL計算黃金比例

APL 語言之中,有一個運算子"power"可以幫助計算數學中的黃金比例。

APL 表達式幾乎與數學定義一樣漂亮、簡潔:

沒錯,只有7個 unicode 字元的表達式,便計算出黃金比例。對數論稍有認識的同仁,也許知道黃金比例的連續分數是:

(圖片連結自維基百科)

其定義不太明顯地使用了遞迴,以一般編程語言表現的話大概離不開:

    def phi(N):
        return 1 + (1 % phi(N))
    end

根據數論,無理數只能以無限連續分數表達,即上面圖示中的 "..." 應該無窮盡地延伸下去,才為之正確的黃金比例。而上述函數定義亦導致無限遞迴,定義上雖然沒有錯,但運作上會導致 stack overflow,很難說是正確的程式。

那麼,APL 是如何僅憑十個字元計算出黃金比例呢?或者首先應該問:APL 返回的值正確嗎?以下係Wolfram所給的黃金比例的小數約數:

1.618033988749894848204586834365638117720309179805762862135...

而 APL 給出的值(使用NARS2000實作):

1.618033988749894

可見小數點後16個位被截斷 (truncate) 了,而小數點後1至15位都脗合。先撇除電腦表達、顯示浮點值的複雜問題,1.618033988749894應該是可接受的約數。

下一個問題:APL 表達式是如何達致遞迴?秘訣在於 稱呼作"power"的運算子。

表達式 {1+1÷⍵}⍣≡1 中, 的作用是

  1. 呼叫 {1+1÷⍵} 1
  2. 結果為整數2,與原先的參數(整數1)以函數 比較
  3. 2≡1 返回整數0亦即布林值false
  4. 如果上一步得出結果為布林值true,則以整數2為答案,否則繼續下一步
  5. 返回第一步,以整數2作為輸入

也許將 函數替換成先把左、右參數列印的匿名函數會比較容易理解:

第一行列印的 ⍺: 2 ⍵: 1就係匿名函數 {⎕←'⍺:',⍺,' ⍵:',⍵⋄⍺≡⍵} 的效果,而下一次呼叫之中,原來的左引數⍺: 2就成為了右引數⍵: 2

直至最後一次呼叫,的差異(即⍺-⍵的值)等於(或少於)NARS2000的小數誤差容許度 (comparison tolerance) 3E¯15⍺≡⍵ 返回布林值 true,遞迴終結並以最後一次呼叫 {1+1÷⍵} 的結果1.618033988749894作為整個表達式的返回值。

解釋到這裏,讀者可能會覺得迷失。事實上我已經思考了良久,都未能得出更好的解釋方法。可以見到APL是一種極度高階及抽象的語言。

最後我再嘗試以一句句子總結 運算子的功能:

FG分別為函數,在表達式 F⍣G N 中,(F N) 的值遞迴地輸入至函數 F,直至 (F N)及 N 以函數 G 比較後得出布林值 true 方告完結。

好像還是有點太抽象。

留言

這個網誌中的熱門文章

APL Code Golfing

Project Euler第八題

Dyalog APL比賽-2019年-Phase 1-第2題