公司被人買下, 換了老闆. 他做過一個固定利率(FRM)的 Prepayment 模型, 程序是用C++寫的. 他叫我用他的框架做個可調利率(ARM)的模型, 即兩者必須用同一程序, 同時還要寫成C++程序. 工作的第一步是讀懂他的模型, 這步不難. 因為模型測試必須在 Unix 上用 C++ 做, 所以在開始建立模型之前還必須讀懂他的程序. 讀着讀着就問題來了, 模型中的對數和指數函數全部換成了部分分式, 即兩個多項式之商. 這想來是用什麼恆等式推出來的. 我想憑我的功力, 應該沒問題. 結果根本不可能, 我最後猜測, 這是某種近似公式. 但準確公式在那兒, 為什麼要近似, 我實在想不出, 我只好去問老闆怎麼回事. 答案出乎意料的簡單, 節約 CPU. 他一語道破天機後, 其餘的道理就很容易想明白了. 所謂”準確”公式, 實際上只是方便的公式而已. 這種涉及到人類思維的模型, 根本不可能真正準確. 這些任何語言中都有的現成函數, 儘管直接使用很方便且精煉, 但極其耗費時間. 算這些東西計算機也沒有好辦法, 只能用級數展開. 它與加減乘相比, 時間可多化幾十甚至100 倍. 換成部分分式後, 儘管看起來麻煩多了, 但十幾個加減乘除, 花的時間比那函數還是要少的多. 我老闆把這些部分分式的係數通過最小二乘法來確定, 得到精度非常高. X 的低次方, 比如 4, 如寫成X**4, 看上去很漂亮, 但耗時極多. 自從知道了”地雷”的秘密, 我就把它寫成X*X*X*X, 甚至把以前寫的程序都一一改過來, 否則簡直有一種罪惡感. 但對於多項式, 還有更進一步的優化方法, 在大部分關於數值計算的書中都能找到. 假定是個4次多項式, 如寫成 a + b X + C XX + d XXX + e XXXX, 儘管比指數形式要快得多, 但還能繼續優化. 我做過檢驗, 計算機做加減乘的時間是一樣的, 所以這相當於14次加法. 但如寫成 a + X (b + X ( c + X (d + e X))), 只做 8 次加法, 改進幅度遠大於許多人的想象. 一般來說, 對一個 N 次多項式, 前者要做 N(N+3)/2 次加法, 後者只要做 2N 次加法, 即使 N 只是 4 這種不算大的數字, 差別已經十分巨大. 計算機和人一樣, 做除法特別慢. 根據《十萬個為什麼》, 幾十年前的計算機,除法和加法的差別是 1:5. 對於現代化計算機, 這比例大約是 1: 2.3, 所以減少除法運算是節約 CPU 的一個重要方面. 我的程序中除以 2 從來是寫成乘以 0.5, 除以3就要看我心情了. 數值計算中常用的 Simpson 積分, 前面有個除以三, 我向來是不做的, 要等到積分收斂後再除, 只除一次. 如果迭代一共進行了20次, 這就節約了44次加減乘. 我還從一位計算機專家那兒學到這麼一招. 在 C 和 C++ 中, 假如有一項 C/3, 計算機就會任勞任怨地每次都給你做一遍除法. 但是如寫成 C * (1/3), 這除法就會在編譯(Compile)時完成, 在實際運算時只要做一次乘法就可以了. 有一次, 老闆讓我看他寫的一段程序, 其中有 exp(X), X 大於零. 他將大於一的部分用現成的函數算, 把計算精度較高且使用頻率也較高的小於一的部分用上下各四次的部分分式來近似: (1 + X (b + X (c + X (d +e X)))) / (1 - X (b + X (c - X (d +e X)))). 共 16 次加法和一次除法. 我將它改成 Y = X * X U = 1 + Y (c + e Y) V = X (b + d Y). 整個表達式成為 (U + V) / (U – V), 一共 10 次加法,一次除法. 除去無法可想的除法, 其餘部分減少了 37.5%. 在這方面頗為厲害的老闆對這一改進還是刮目相看的. 這位老闆是研究場論的理論物理博士, 理論功底相當堅實, 他曾證明過Ho-Lee 模型與路徑無關的充分條件. 用最小二乘法確定部分分式的係數, 從沒在文獻中見過, 很可能是他的原創. 儘管它只用到了高中代數這一碗水, 但要想到這一點, 沒有一桶水是難以想象的. 要想出這兒提到的各種優化, 對計算機的計算原理必須相當熟悉, 但要理解他們, 需要的只是高中甚至初中代數的知識. |