這是最近做的乙個python的小專案,內容是實現求導的符號運算,雖然我最終的實現化簡能力能力比較弱,但是還是乙個很有意思的專案。
語法樹是我這個專案裡面所有表示式的存在形式。
樹的乙個結點要麼是字串(常數或者變數),要麼是內部結點,內部結點代表一棵子樹,對應儲存的結構體有兩個字段:根儲存的運算和子結點的列表,列表元素個數是操作符的元數,列表元素是子結點。
例如字首表示式(+ (* x 2) 1)表示2x+1,那麼對應的樹根結點是』+』,對應的子結點列表是[tree(2x), 1],其中tree(2x)又是一棵子語法樹,根為』*』, 對應子結點的列表是[2, x]。即語法樹是遞迴定義的,最終葉子結點是常量或者變數。
我的專案中約定輸入字首表示式,即類似lisp的表示式,所以轉化非常方便。
對於字首表示式(op arg0, arg1, …, argn),先讀入op為根結點操作符,然後讀入引數列表即可建立語法樹,注意讀入引數列表過程中可能讀到表示式,需要遞迴建樹。
利用字首表示式的演算法遞迴求值即可。遇到變數,代入具體數值即可。
設calculate(tree,x)計算tree在變數取值x時候的值。
因為要求乙個函式而不是具體數值,所以需要返回乙個函式或者lambda表示式,可以實現func(tree) 為lambda x: calculate(tree, x)。
這裡是演算法的核心。
對於二元運算,演算法為遞迴求導,規則如下:
1.葉子結點:x導數為1,常數導數為0。
2.非葉子結點,左右子樹為l,r。那麼結點的運算來遞迴求導: (l
+r)′
=l′+
r′ (
l−r)
′=l′
−r′
(l∗r
)′=l
∗r′+
l′∗r
(l/r)′=
(l′∗
r−l∗
r′)/
(r2)
(lr)′=(
rlr−
1)∗l
′+ln
(l)l
r∗r′
手動實現這五條規則即可。
對於一元函式,有統一的計算方法,即鏈式法則: h(
g(x)
)′=h
′(g(
x))∗
g′(x
) 所以需要儲存乙個函式表,儲存下每乙個函式的導函式,利用鏈式法則遞迴計算即可。注意我們這兒並不儲存導函式的函式形式,而是儲存如何建立導函式語法樹的lambda表示式。、
例如,ln(x)的導函式就是lambda x: exptree(『\』, [1, x])。
因為上述的求導規則經常會產生0或者1項,例如: (2
∗x)′
=(2)
′∗x+
2∗(x
)′=0
∗x+2
∗1所以需要化簡,規則為: 1.0
+m=m
+0=m
2.m−
0=m
3.1∗m
=m∗1
=m4.0∗
m=m∗
0=0
5.m/1
=m6.0/
m=0
7.m1=
m 8.0
m=0
手動實現規則即可。
要注意的是,需要先化簡子樹來遞迴化簡。另外,如果左右子樹都是常數,可以直接計算。
介面需要使用者輸入字首表示式和運算指令,輸出對應的函式或者函式值,另外輸出對應函式的時候,為了直觀,除了函式的中綴表示式,我還輸出了函式影象。
互動上我沒有花和大力氣,所以格式相對比較死板,不過提供了足夠的幫助資訊,應該用起來很方便。
但是,這畢竟只是乙個耗時一天不到的小專案,要改進的地方還有很多:
1.這個專案不能運用運算律和函式性質化簡,所以以下這些化簡,暫時無法完成:
x-x=0,2x+3x=5x,sinx * sinx + cosx * cosx=1,ln(exp(x))=x。
2.這個專案暫時不支援自己指定中間函式,這個涉及到詞法語法分析等等,沒有實現。
3.對於重複表示式沒有共享而是一一計算,沒有優化。
但是畢竟這是乙個輕量級專案,所以不準備加入這些特性了。
MATLAB 求導 符號計算
求導 diff 函式 求的一階導數 diff 函式,n 求的n階導數 n是具體整數 diff 函式,變數名 求對的偏導數 diff 函式,變數名,n 求對的n階偏導數 符號計算函式 r subs s,new 利用new的值代替符號表示式s中的預設符號 r subs s 用由呼叫函式或matlab工作...
SICP學習筆記 2 3 2 例項 符號求導
練習2.56 define deriv exp var cond number?exp 0 variable?exp if same variable?exp var 1 0 sum?exp make sum deriv addend exp var deriv augend exp var pro...
Matlab符號運算
一 宣告 宣告單個符號變數 sym a 宣告多個符號變數 syms a b c 二 符號表示式 提取分子分母 n,d numdem a 自變數為 v的符號函式的反函式 finverse f,v 求和 symsum s,v,a,b 三 符號表示式化簡 以直觀漂亮的形式顯示 pretty f 合併同類項...