我正在编写一些代码作为非线性回归工具的一部分,我试图找出一种方法,以一种可读和快速的良好平衡的方式返回给定函数的第n个偏导数。函数(以及部分的分析表示)在运行时是已知的,这些可以是硬编码的。这是我到目前为止所做的事情(有效):
let getPartials (paramVect: array<float>) idx =
let a = paramVect.[0]
let b = paramVect.[1]
let c = paramVect.[2]
match idx with
| 1 -> (fun x -> (1.0+b+c*x)**(-1.0/b)) // df(x)/da
| 2 -> (fun x -> ((a*(1.0+c*b*x)**(-(b+1.0)/b))*((b*c*x+1.0)*Math.Log(b*c*x+1.0)-b*c*x))/(b*b)) // df(x)/db
| 3 -> (fun x -> -a*x*(b*c*x+1.0)**(-(b+1.0)/b)) // df/dc
| _ -> (fun x -> 0.0) //everything else is zero
我使用它的方法是首先使用参数向量构造一个部分函数,以便最小化需要传入的次数。然后我反复调用(getPartials(i)x_val)来构造一个雅各比人。在程序的生命周期中,此函数被调用的次数非常多。
我对此表现得相当可接受,但我怀疑它可以改进。分析表明,第二个函数计算(长一个)的评估是一个cpu排放 - 这可以优化吗?我不确定匿名函数是否会产生性能问题,因为它是可读的......
我是全新的到F#编程,所以如果您发现任何风格/形式或性能方面的任何问题,请告诉我!
谢谢
更新:在实现 JohnPalmer 建议的更改并重构之后,不是返回一个接受x值作为参数的匿名函数,而是执行整个计算,I我看到大约300%的速度增加。能够返回部分功能更方便,但不值得花费。
let getPartials (paramVect: array<float>) idx x =
let a = paramVect.[0]
let b = paramVect.[1]
let cbx = paramVect.[1] * paramVect.[2] * x
match idx with
| 1 -> (1.0+cbx)**(-1.0/b) // df(x)/da
| 2 -> ((a*(1.0+cbx)**(-(b+1.0)/b))*((cbx+1.0)*Math.Log(cbx+1.0)-cbx))/(b**2.0) // df(x)/db
| 3 -> -a*x*(cbx+1.0)**(-(b+1.0)/b) // df/dc
| _ -> 0.0 //everything else is zero
答案 0 :(得分:1)
匿名函数导致性能问题的最可能原因是每次调用getPartials函数时都会创建一个新的堆对象。如果您只有少量不同的paramVect
,则可以通过缓存匿名函数获得一些性能优势。
关于第二个表达式的评估,你可以尝试这个(采用John Palmer的建议来消除常见的子表达式):
fun x -> let bcx = b * c * x
let bcx1 = bcx + 1.0
a * bcx1 ** (-(b+1.0)/b) * (bcx1 * Math.Log bcx1 - bcx)/(b*b)