我正在编写一些代码作为非线性回归工具的一部分,我试图找出一种方法,以一种可读和快速的良好 balancer 的方式返回给定函数的第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)来构造一个jacobian . 在程序的生命周期中,此函数被调用的次数非常多 .
我得到了相当可接受的性能,但是,我怀疑它可以改进 . 分析表明,第二个函数计算(长一个)的评估是一个cpu排放 - 这可以优化吗?我不确定匿名函数是否会产生性能问题,因为它是可读的......
我是F#编程的新手,所以如果您发现任何风格/形式或性能方面的任何问题,请告诉我!
谢谢
更新:在实现 JohnPalmer 建议的更改并重构之后,不是返回一个接受x值作为参数的匿名函数,而是在原地进行整个计算,我看到大约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
1 回答
导致性能问题的匿名函数最可能的原因是每次调用getPartials函数时都会创建一个新的堆对象 . 如果您只有少量不同的
paramVect
,则可以通过缓存匿名函数获得一些性能优势 .至于第二个表达式的评估,你可以尝试这个(采用John Palmer的建议来消除常见的子表达式):