2025年2月15日 星期六

砥礪前行

砥礪前行


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
16 February 2025


任何設計好的系統都必須經過長期考驗,執行考驗測試出結果卻是一種磨練,這種工作做起來並不比設計系統來得輕鬆。磨練系統需要範例,範例貴在具有創意,創意還得要有意義,範例才值得採用,有意義、有創意的範例就值得珍惜。自己能設想出來的範例是有限的,從網上尋求一些資源則是必要的,我在網上搜尋範例資源時,有過這些體會。

大家最熟悉的資源莫過於 Rosetta code 網頁: https://rosettacode.org/wiki/Category:Programming_Tasks
這個網頁的好處就在其資料公開的透明度是百分之百,提出來的範例題目毫無隱瞞,附貼出各種程式語言的程式解答也毫無隱瞞。不限制瀏覽、不限制下載、不限制貢獻、不限制引用,全球使用者均能受益,網頁受人尊重,貢獻者也多。

網上還有許多可用資源,這裡介紹另一知名之 Project Euler 網頁:https://projecteuler.net/archives
經過許多年的發展,這個網頁目前(20250216)的範例題目已經累積到將近有 1000 個了。網頁則要求使用者必須登記後才能參與活動,也才能看到題目的程式解答,使用起來有點不便。我大致瀏覽過其範例題目的內容,顯示出不少問題均涉及必須使用具有可程式化大數計算能力的系統才利於獲得答案。因此,能貢獻解答而貼出的範例程式就比較有限了,一般傳統的程式語言不具有大數計算的功能,還要能程式化,那就更不容易見到了。所以,參與活動者的貢獻也就因此而不如 Rosetta code 網頁來得多。當然,相較之下顯示出這個網頁內的範例資源題目比較艱深。

設計 ABC Forth 系統曾經留下的記錄,可以追溯到 1980 年代,剛開始時,主要目標放在為 Forth 系統設計出好用的中算符程式寫法。因為我個人的研究工作,常需要進行可程式化數學計算程式的設計,中算符的程式寫法省時好用,於是愛用 Forth 就愛屋及鳥的研發出 Forth 自我形成的中算符系統,中算符功能是設計出整個系統之構想的起源。

後來的發展則偏重於程式結構語法,BASIC 程式語言表達數學式子的方式方便好用,有利於數學計算方面之程式設計,於是 ABC Forth 的雛型應運而生,有了系統性的結構。由於它是架設於一般 Forth 系統上延伸出性能而成,它的特色就是 BASIC 式的程式可以跟傳統 Forth 程式混用,共享資源後執行出共榮結果。

可程式化的大數計算能力,是後來的發展,因為需要而加裝。加裝的過程中順便發現了設計出可程式化的複數計算能力、或比較罕用的可程式化常規分數計算能力、甚至於其他數系的可程式化計算能力都是類似的設計。原本的大數計算只考慮處理大整數問題,延伸性的研究,則設計出了能處理不限位數大浮點數問題的系統。

這個網頁過去公佈過的實際程式,都附有貼出時間。曾有好幾次在我發展出特殊性能而網貼成果時,網上都還見不到能執行出同樣性能的程式語言系統,網文貼出後,大家互相勉勵、互相砥礪,後來我就看到了別種程式語言性能上的進步。

互相觀摩別種程式語言設計出來的程式,能幫助自己設計 Forth 程式。公開 Forth 程式,當然也能幫助別人使用各自喜歡之程式語言設計程式。這裡刻意挑選了一個 Project Euler 網頁提出的範例『題目 8 』進行一次小小的貢獻。

為了砥礪前行,繼續發展 Forth ,這次,不用 BASIC 式的語法設計程式,純用 Forth 。

題目雖只採用一千位數來考驗程式設計,它卻是個形同『海底撈針』式的問題,舉例不用太誇張而使用海量位數,使用一千位數點到為止就夠了,用來研究問題才能恰到好處。

題目問你一千位數中連乘 4 位數者,那一個位置的數字最大。然後,再問你連乘 13 位數時又是那一個位置的數字最大?

題目從網頁擷圖展示如下,隨後附貼純用 Forth 寫成的程式及其執行後所得的答案。

所採用的系統為 ciForth 5.3.0 ,作業系統為 Ubuntu 20.04 。

鍾情於 Forth 的愛用者可以直接載入程式執行出結果。別種程式語言的愛用者,請勿嫌這個程式的冗長與複雜,它有助於了解求解問題的程式規劃步驟,指令名稱就是說明。

附記 : 由於三月份遠行返台一個月,貼文暫停兩次。四月份再見。

\ euler08.f

: cinumber ( addr -- n )
  0 0  rot dup 1+ c@ [char] . = >r
  $@ r@ if 1 /string then
  >number nip 0 = 
  if d>s r> if negate then 
  else r> drop 2drop 
  then ;

create chr 128 cells allot
create val 128 cells allot
create tmp 2 cells allot

s" 73167176531330624919225119674426574742355349194934" chr $!
s" 96983520312774506326239578318016984801869478851843" chr $+!
s" 85861560789112949495459501737958331952853208805511" chr $+!
s" 12540698747158523863050715693290963295227443043557" chr $+!
s" 66896648950445244523161731856403098711121722383113" chr $+!
s" 62229893423380308135336276614282806444486645238749" chr $+!
s" 30358907296290491560440772390713810515859307960866" chr $+!
s" 70172427121883998797908792274921901699720888093776" chr $+!
s" 65727333001053367881220235421809751254540594752243" chr $+!
s" 52584907711670556013604839586446706324415722155397" chr $+! 
s" 53697817977846174064955149290862569321978468622482" chr $+!
s" 83972241375657056057490261407972968652414535100474" chr $+!
s" 82166370484403199890008895243450658541227588666881" chr $+!
s" 16427171479924442928230863465674813919123162824586" chr $+!
s" 17866458359124566529476545682848912883142607690042" chr $+!
s" 24219022671055626321111109370544217506941658960408" chr $+!
s" 07198403850962455444362981230987879927244284909188" chr $+!
s" 84580156166097919133875499200524063689912560717606" chr $+!
s" 05886116467109405077541002256983155200055935729725" chr $+!
s" 71636269561882670428252483600823257530420752963450" chr $+!

: chr>val ( -- )
  val 128 cells erase
  chr $@ over + swap 
  do 
     tmp 2 cells erase 
     I c@ tmp $c+
     tmp cinumber val $c+
  loop ;

: mult13 ( add -- n )
  dup c@ swap 1+ dup 12 + swap
  do 
     I c@ * 
  loop ;

variable maxaddr
variable maxval

: 1000calculations ( -- )
  0 maxval !
  val $@ over dup maxaddr ! + swap
  do 
    I mult13 dup maxval @ >
    if   I maxaddr ! maxval !
    else drop
    then
  loop 
;

: check ( -- )
  maxaddr @  val $@ drop - 
  chr $@ drop + 13 type
; 

: main
  chr>val
  1000calculations
  cr ." greatest product 0f 13 adjacent digits are  " check 
  cr ." product value =  " maxval @ . ;

\s

Largest product in a series
Problem 8 
The four adjacent digits in the 1000-digit number 
that have the greatest product are 9 × 9 × 8 × 9 = 5832.

73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450

Find the thirteen adjacent digits in the 1000-digit number 
that have the greatest product. What is the value of this product?

Ans : 23514624000

greatest product 0f 13 adjacent digits are  5576689664895 <-- from 197 digits
product value =  23514624000  OK


2025年2月1日 星期六

專家系統

專家系統


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
2 February 2025


53年前(1972)出現過一個曾被公認是很成功的人工智慧軟體程式專家系統 MYCIN ,它被用來根據血液數據提供出診斷與治療建議。那時的電腦性能沒有那麼好,但可以容得下500條規則進行推論得出結果。這個應用典例,說明了專家系統由推理引擎及數據資料庫所形成,它是一種由專家整理出來的確定性人工智慧,由電腦執行的過程中,沒有模糊的(fuzzy)決策理念摻雜其間。它跟現今的熱門話題:運用類神經網路發展出來的人工智慧(AI)系統不同,差別就在設計系統時只依據專家總結出來的定論規劃程式。

運轉原子爐時,為了安全,必須講求運轉人員能迅速的確定爐心燃料棒的狀況,判斷狀況的依據則僅能根據各種儀錶的讀數。能運轉原子爐的工程師都是專家,都必須被訓練成能根據儀錶讀數解讀出燃料棒發生了什麼事情?而且判斷要快,才能立刻採取正確的處理措施,確保原子爐一定安全。

我曾運轉過一座 40MW(t) 的重水式原子爐許多年,爐心有大約 200 根以天然鈾製成的金屬燃料棒,由於它是專門供作研究用的巨型核輻射裝置,內部全面採用必須絕對安全的設計。例如:每單根燃料棒冷卻水的出口管路,都必須經過好幾套儀錶的監測,運轉時隨時提供重要數據,關鍵性的參數不正常時,還可能立即引發原子爐『急停』(trip),自動強制原子爐處於絕對安全的停爐狀態。

所用的監測儀錶有氣體分裂產物監測器(GFP)、冷卻水流量計(FT)、冷卻水溫度計(TE)、冷卻水壓力計(PIS).....等。由於壓力參數最敏感、響應最快速,一裝就三套,兩套接通到流量測量元件(FE)的上游,一套接通到下游,兩邊的壓差有 2 psi 的程度。冷卻水壓力出現問題時,壓力計的信號會直接驅動壓力控制器(PC),引發原子爐自動急停。

研究用原子爐的燃料棒比較容易出現異常狀況,因為爐心的運轉條件必須經常配合研究需要而調變。甚至於在全功率運轉狀況下,經常有添加的研究設施在爐心範圍內作動,也有只能短瞬間被照射的物質必須快速進出於爐心領域,而原子爐的動態性能研究本就是這種原子爐建造起來的目的。種種的研究需求都會令原子爐運轉功率變化,同時就令燃料棒的狀態跟著變化。

燃料棒出現問題時,出口冷卻水的監測參數會立刻顯現出來,根據參數判斷出問題是一門專家才能搞得很清楚的技術。判斷必須講求迅速,狀況又有許多種,把專家確定的知識設計成以電腦輔助運轉的專家系統就有價值。專家系統能快速的提供建議,告訴專家那些可能,例如:燃料棒內套管破了、外套管破了、管頂或管底的閥門漏水了、管內防止燃料棒斷掉的先斷式張力棒(tension member)斷掉了、管內冷卻水道變形或有雜物堵塞妨礙冷卻了.....等等現象,都需要能被快速的判斷出來,以便採取措施。這種專家技術討論起來有點複雜,我只把相關圖示貼在這裡供大家認識。請問:光憑上述資訊,能自己把儀錶參數與燃料棒狀況對應得上嗎?答案是不能,只能由學通過整個系統的專家來回答問題,這就是建立專家系統的好處。此外,我因較為資深,碰過例外情況,還能在例外狀況發生而專家系統上沒有列入程序的狀況下解釋問題,我做過這種貢獻,是比專家更專家的事情。
設計電腦的專家系統有許多例子可舉,我們可以舉一個很簡單之數學方面的專家程式為例,越簡單越好,主要目的只在說明專家系統的道理:

二元二次方程式的類型判別式,早就已經被整理得很完善了。從專門的數學手冊中,都可以找到這種規則分類表,使用者只須取出二元二次方程式的每一項係數,自行代入指定的幾個判別式,計算出結果,然後對照至規則分類表中的幾個條件,就能判別出方程式是屬於那一種幾何圖形?所根據的方法,在數學上稱為不變式論(Invarians)。

這樣的小型數學專家系統,於電腦的使用沒現在那麼方便前,至少還是要靠一點人工的計算,與人工設計的邏輯判斷來得到結果。但是,如果不能方便的設計程式,要解決這樣的問題,仍是不值得耗時、耗力去完成。 ABC Forth 系統改善了傳統 Forth 的這種情況,這一篇文章也係為了解決此類問題而寫。

在三位作者A. D. Aleksandrov(Editor ), A. N. Kolmogorov (Editor ), M. A. Lavrentiev(Editor ),所著之 Mathematics: Its Content, Methods, and Meaning (3 Volume Set).The MIT Press 於 March 15th 1969 出版的書中,Chapter 3 第十二節就刊載了整理得非常完善的規則分類表,我們就以這份表格來設計程式,設計出來的程式幾乎不用解說,任何人都可以很容易的明白與使用,程式中就得用到性能優越、用法平易近人的邏輯運算,才能表現得非常清楚。

由於我在設計 ciForth 的浮點運算系統時,把 『 E 或 e 』當作一個指令來處理出 10 的幾次方之表達方式,程式中不宜再用 E 或 e 來作為變數名稱。因此,這個範例程式中改採全用雙字元來宣告出變數,想用 E 或 e 時,改用 EE ,特此提示。


\ 二元二次方程式判別程式
\ 根據不變式論(Invarians)設計。
\ 作者:曾慶潭2011/4/16保有一切版權,知會作者,附加本宣告則允許引用。
\ Copyright 2011/4/16 Ching-Tang Tseng
\ Permission is granted by the author to use this software
\ for any application provided this copyright notice is preserved.
\ 2025/2/2重整內容並在Lina64之ABC Forth系統上執行,測試無誤。

10 Reals I1 I2 I3 K1 AA BB CC DD EE FF

: INPUT-COEFFICIENTS {{ AA = 9.0 E 0 }} {{ BB = 0.0 E 0 }} {{ CC = 1.6 E 1 }} {{ DD = 0.0 E 0 }} {{ EE = 0.0 E 0 }} {{ FF = 1.0 E 0 }} ; : DISCRIMINANTS BASIC 10 LET { BB = BB / 2.0 E 0 } 20 LET { DD = DD / 2.0 E 0 } 30 LET { EE = EE / 2.0 E 0 } 40 LET { I1 = AA + CC } 50 LET { I2 = AA * CC - BB * BB } 60 LET { I3 = AA * CC * FF + 2. E 0 * BB * DD * EE - AA * EE * EE - CC * DD * DD - FF * BB * BB } 70 LET { K1 = AA * FF - DD * DD + CC * FF - EE * EE } 80 END ; : DESCRIPTION BASIC 10 PRINT " 二元二次方程式:AX^2+BXY+CY^2+DX+EY+F=0 " 20 PRINT " 係數為: " 30 PRINT { " A = " ; AA ; " B = " ; BB ; " C = " ; CC } 40 PRINT { " D = " ; DD ; " E = " ; EE ; " F = " ; FF } 50 PRINT " 方程式為: " 60 END ; : MAIN BASIC 10 RUN INPUT-COEFFICIENTS 20 RUN DESCRIPTION 30 RUN DISCRIMINANTS 40 IF { ( I2 > f0.0E0 ) AND ( ( I1 * I3 ) < f0.0E0 ) } THEN 150 50 IF { ( I2 > f0.0E0 ) AND ( ( I1 * I3 ) > f0.0E0 ) } THEN 170 60 IF { ( I2 > f0.0E0 ) AND ( I3 = f0.0E0 ) } THEN 190 70 IF { ( I2 < f0.0E0 ) AND ( I3 <> f0.0E0 ) } THEN 210 80 IF { ( I2 < f0.0E0 ) AND ( I3 = f0.0E0 ) } THEN 230 90 IF { ( I2 = f0.0E0 ) AND ( I3 <> f0.0E0 ) } THEN 250 100 IF { ( I2 = f0.0E0 ) AND ( I3 = f0.0E0 ) AND ( K1 < f0.0E0 ) } THEN 270 110 IF { ( I2 = f0.0E0 ) AND ( I3 = f0.0E0 ) AND ( K1 > f0.0E0 ) } THEN 290 120 IF { ( I2 = f0.0E0 ) AND ( I3 = f0.0E0 ) AND ( K1 = f0.0E0 ) } THEN 310 130 PRINT " 已知條件不符合程式執行要求,未判別出結果。 " 140 GOTO 999 150 PRINT " 橢圓,典型方程式為:(X^2/a^2)+(X^2/b^2)=1 " 160 GOTO 999 170 PRINT " 虛橢圓,典型方程式為:(X^2/a^2)+(X^2/b^2)=-1 " 180 GOTO 999 190 PRINT " 點,典型方程式為:(X^2/a^2)+(X^2/b^2)=1 " 200 GOTO 999 210 PRINT " 雙曲線,典型方程式為:(X^2/a^2)-(X^2/b^2)=1 " 220 GOTO 999 230 PRINT " 一對相交的直線,典型方程式為:(X^2/a^2)-(Y^2/b^2)=0 " 240 GOTO 999 250 PRINT " 拋物線,典型方程式為:X^2=2pY,其中p=SQRT(-(I3/I1^3)) " 260 GOTO 999 270 PRINT " 一對平行線,典型方程式為:X^2=a^2 " 280 GOTO 999 290 PRINT " 一對虛平行線,典型方程式為:X^2=-a^2 " 300 GOTO 999 310 PRINT " 一對重合的直線,典型方程式為:X^2=0 " 999 END ; 載入程式後,執行MAIN指令,得到下列結果: MAIN 二元二次方程式:AX^2+BXY+CY^2+DX+EY+F=0 係數為: A = 9.000000000 B = 0.000000000E0 C = 16.00000000 D = 0.000000000E0 E = 0.000000000E0 F = 1.000000000 方程式為: 虛橢圓,典型方程式為:(X^2/a^2)+(X^2/b^2)=-1 ok


這樣的程式顯示出幾項特點:

(1) 並不是只有從頭到底都必須一直計算的程式,才叫數學計算程式。這個程式根本只進行很簡單的數學計算,最後能得到正確答案,主要還是依靠邏輯判斷的功能。

(2) 非結構化的程式性能,並不是不好的性能。這個程式簡直就是一個澈底非結構化的典型程式,但是!我在設計它時,就因為系統具有能寫非結構化程式的功能,如此設計出來的程式,簡直就是照規則表直接翻譯,一次完成。

大家現在就回頭讀一讀看 MAIN 指令的內容,您不可能會看不懂程式在做甚麼?嚴格的專家經驗,就需要這樣的程式功能來表達,可貴的經驗才不會被扭曲。
反之,您若使用現行任何一種號稱嚴格結構化的程式語言,設計這種程式,邏輯判斷三次,就足以將程式設計者搞混了,那怕您將程式依序一層一層的縮格,規劃得再漂亮,寫完程式後五分鐘,就無法正確的維護了。那麼,要結構化程式語言何用?不如就像 ABC Forth 系統一樣,能夠非結構化。

作者曾經是核子反應器廠(俗稱原子爐)的運轉專家,深知當年想以程式實現專家經驗時的關鍵問題所在,如果那時我就設計了具有邏輯功能的 ABC Forth 系統,想具體實現作者的專家經驗,並實際應用設計出來的程式,作為核子反應器廠運轉時的強力輔助工具,實現計劃就不會那麼困難了。

(3) 還有,最重要的一點,就是,如果系統不提供純粹中文式的邏輯運算功能,我就不覺得設計上述的程式,對中國人而言,會有任何意義了。因為邏輯條件講求嚴謹,輸出結果講求絕對正確,程式的日後維護講求易看、易懂、易修改,沒有中文功能,就不必白費力氣去設計日後難用、難維護的程式。

(4) 近代應用於解決數學問題的商售大型套裝軟體,由於電腦易於安裝廉價大量的記憶體,執行速度也不再是應用時主要的考量問題。系統的功能也開始提供非純粹計算式的解題功能。例如:執行求解數學式子的微積分問題時,根本不執行數值分析程式,改成設計對輸入數學式的文字或符號式剖析,再經歸納出來的數學分析規則,只進行簡單的判別式計算,就能輸出正確解答。這樣的程式功能,就得依靠本章中的『條件整合』式邏輯運算功能來處理問題,上述的程式提供了一點解決此類問題的思考依據。

(5) 電腦輸出決策絕對重要,如果最聰明的人類,能夠製作出最關鍵性的決策表格,截至目前為止,甚麼程式語言?才能提供人類最清楚的表達程式,令其與表格同步。甚麼樣的程式?放幾十年後,還能夠照樣清楚的反應出專家技術。這些問題,表示為電腦設計出簡單明確的邏輯運算功能非常重要。現行 ABC Forth 數學計算系統,具備這樣的特質。