Prolog - 简化导数
Prolog - simplify derivative
所以我这学期才开始使用 Prolog,并完成了实现一个非常基本的 d(function, variable, derivative)
的作业,我是这样做的:
d(X,X,1) :- !.
d(C,X,0) :- atomic(C). %, (C \= X).
d(X**E,X,E*X**(E-1)).
d(U+V,X,A+B) :- d(U,X,A), d(V,X,B).
d(U-V,X,A-B) :- d(U,X,A), d(V,X,B).
d(U*V,X,DU*V+U*DV) :- d(U,X,DU), d(V,X,DV).
d(U/V,X,(DU*V-U*DV)/(V*V)) :- d(U,X,DU), d(V,X,DV).
我知道这并不完整,但它涵盖了练习中所需的所有任务。
然而,
?- d((x*x+2*x+3)/(3*x),x,R).
导致
R = ((1*x+x*1+ (0*x+2*1)+0)* (3*x)- (x*x+2*x+3)* (0*x+3*1))/ (3*x* (3*x)).
这看起来一点也不漂亮。 is/2 不幸的是不喜欢我的 x 因为它不是数字...
有没有简单的解决方案来获得更干净的结果?
获得数字的一种可能性是用一个值替换变量 x
的每个实例,访问派生树。你应该写一个子句来匹配每个二元运算符,或者使用通用访问,比如
set_vars(E, Vs, Ev) :-
E =.. [F,L,R],
set_vars(L, Vs, Lv),
set_vars(R, Vs, Rv),
Ev =.. [F,Lv,Rv].
set_vars(V, Vs, N) :- memberchk(V=N, Vs).
set_vars(V, _, V).
产量
?- d((x*x+2*x+3)/(3*x),x,R), set_vars(R,[x=5],E), T is E.
R = ((1*x+x*1+ (0*x+2*1)+0)* (3*x)- (x*x+2*x+3)* (0*x+3*1))/ (3*x* (3*x)),
E = ((1*5+5*1+ (0*5+2*1)+0)* (3*5)- (5*5+2*5+3)* (0*5+3*1))/ (3*5* (3*5)),
T = 0.29333333333333333
但是,您的第一个子句中有一个错误,一旦更正,将允许直接计算派生表达式:
d(X,V,1) :- X == V, !.
...
现在,我们可以扔掉实用程序 set_vars/3,所以
?- d((T*T+2*T+3)/(3*T),T,R), T=8, V is R.
T = 8,
R = ((1*8+8*1+ (0*8+2*1)+0)* (3*8)- (8*8+2*8+3)* (0*8+3*1))/ (3*8* (3*8)),
V = 0.3177083333333333.
我宁愿将其视为两个独立的问题:
首先,正确推导(根据您的具体要求,您可能已经接近了)。
然后,在代数层面上简化表达式。利用代数恒等式,看看在某些子表达式上应用交换律/结合律/分配律是否可以将它们重写为等价的东西(但更简单/更紧凑)。
作为起点,您可能想看看有点相关的问题“Replacing parts of expression in prolog”。
这是一个简单的草图,您可以如何进行简化——使用 来防止实例化不充分:
expr_simplified(A, B) :-
iwhen(ground(A), xpr_simplr(A,B)).
xpr_simplr(A, B) :-
( atomic(A)
-> A = B
; ( A = X+0 ; A = 0+X ; A = 1*X ; A = X*1 )
-> xpr_simplr(X, B)
; ( A = 0*_ ; A = _*0 )
-> B = 0
; A = X+X
-> B = X*2
; A = X*X
-> B = X**2
; A = X**1
-> B = X
; A =.. [F|Xs0], % defaulty catch-all
maplist(xpr_simplr, Xs0, Xs),
B =.. [F|Xs]
).
让我们看看它如何处理您给出的表达式。我们应用 expr_simplified/2
直到我们到达一个固定点:
?- A = ((1*x+x*1+(0*x+2*1)+0)*(3*x)-(x*x+2*x+3)*(0*x+3*1))/(3*x*(3*x)),
expr_simplified(A,B),
expr_simplified(B,C),
expr_simplified(C,D).
A = ((1*x+x*1+(0*x+2*1)+0)*(3*x)-(x*x+2*x+3)*(0*x+3*1))/(3*x*(3*x)),
B = ((x+x+(0+2))*(3*x)-(x**2+2*x+3)*(0+3))/(3*x)**2,
C = ((x*2+2)*(3*x)-(x**2+2*x+3)*3)/(3*x)**2,
D = C. % fixed point reached
虽然简化器并不完美,但表达式的可读性提高了很多。
所以我这学期才开始使用 Prolog,并完成了实现一个非常基本的 d(function, variable, derivative)
的作业,我是这样做的:
d(X,X,1) :- !.
d(C,X,0) :- atomic(C). %, (C \= X).
d(X**E,X,E*X**(E-1)).
d(U+V,X,A+B) :- d(U,X,A), d(V,X,B).
d(U-V,X,A-B) :- d(U,X,A), d(V,X,B).
d(U*V,X,DU*V+U*DV) :- d(U,X,DU), d(V,X,DV).
d(U/V,X,(DU*V-U*DV)/(V*V)) :- d(U,X,DU), d(V,X,DV).
我知道这并不完整,但它涵盖了练习中所需的所有任务。
然而,
?- d((x*x+2*x+3)/(3*x),x,R).
导致
R = ((1*x+x*1+ (0*x+2*1)+0)* (3*x)- (x*x+2*x+3)* (0*x+3*1))/ (3*x* (3*x)).
这看起来一点也不漂亮。 is/2 不幸的是不喜欢我的 x 因为它不是数字...
有没有简单的解决方案来获得更干净的结果?
获得数字的一种可能性是用一个值替换变量 x
的每个实例,访问派生树。你应该写一个子句来匹配每个二元运算符,或者使用通用访问,比如
set_vars(E, Vs, Ev) :-
E =.. [F,L,R],
set_vars(L, Vs, Lv),
set_vars(R, Vs, Rv),
Ev =.. [F,Lv,Rv].
set_vars(V, Vs, N) :- memberchk(V=N, Vs).
set_vars(V, _, V).
产量
?- d((x*x+2*x+3)/(3*x),x,R), set_vars(R,[x=5],E), T is E.
R = ((1*x+x*1+ (0*x+2*1)+0)* (3*x)- (x*x+2*x+3)* (0*x+3*1))/ (3*x* (3*x)),
E = ((1*5+5*1+ (0*5+2*1)+0)* (3*5)- (5*5+2*5+3)* (0*5+3*1))/ (3*5* (3*5)),
T = 0.29333333333333333
但是,您的第一个子句中有一个错误,一旦更正,将允许直接计算派生表达式:
d(X,V,1) :- X == V, !.
...
现在,我们可以扔掉实用程序 set_vars/3,所以
?- d((T*T+2*T+3)/(3*T),T,R), T=8, V is R.
T = 8,
R = ((1*8+8*1+ (0*8+2*1)+0)* (3*8)- (8*8+2*8+3)* (0*8+3*1))/ (3*8* (3*8)),
V = 0.3177083333333333.
我宁愿将其视为两个独立的问题:
首先,正确推导(根据您的具体要求,您可能已经接近了)。
然后,在代数层面上简化表达式。利用代数恒等式,看看在某些子表达式上应用交换律/结合律/分配律是否可以将它们重写为等价的东西(但更简单/更紧凑)。
作为起点,您可能想看看有点相关的问题“Replacing parts of expression in prolog”。
这是一个简单的草图,您可以如何进行简化——使用
expr_simplified(A, B) :- iwhen(ground(A), xpr_simplr(A,B)). xpr_simplr(A, B) :- ( atomic(A) -> A = B ; ( A = X+0 ; A = 0+X ; A = 1*X ; A = X*1 ) -> xpr_simplr(X, B) ; ( A = 0*_ ; A = _*0 ) -> B = 0 ; A = X+X -> B = X*2 ; A = X*X -> B = X**2 ; A = X**1 -> B = X ; A =.. [F|Xs0], % defaulty catch-all maplist(xpr_simplr, Xs0, Xs), B =.. [F|Xs] ).
让我们看看它如何处理您给出的表达式。我们应用 expr_simplified/2
直到我们到达一个固定点:
?- A = ((1*x+x*1+(0*x+2*1)+0)*(3*x)-(x*x+2*x+3)*(0*x+3*1))/(3*x*(3*x)), expr_simplified(A,B), expr_simplified(B,C), expr_simplified(C,D). A = ((1*x+x*1+(0*x+2*1)+0)*(3*x)-(x*x+2*x+3)*(0*x+3*1))/(3*x*(3*x)), B = ((x+x+(0+2))*(3*x)-(x**2+2*x+3)*(0+3))/(3*x)**2, C = ((x*2+2)*(3*x)-(x**2+2*x+3)*3)/(3*x)**2, D = C. % fixed point reached
虽然简化器并不完美,但表达式的可读性提高了很多。