在VBA中一次可以实现牛顿的方法吗?

我需要使用牛顿的方法closures。

Function f (x as Double, y as Double) as Double f = x^3-y End Function 

我从单元中得到y的值,然后我想知道f是零的时候。 在上面的玩具例子中,如果单元格包含y=8 ,那么我期望牛顿的方法find接近x=2

我的解决scheme是做一个newton_solve_f函数:

 Function newton_solve_f (y as Double as Double) as Double Dim x as Double x = 0 'initial guess for x 'do Newton's method to find x ... newton_solve_f = x End Function 

所以实际上,我把我的牛顿方法的代码( 从这里取出 ) newton_solve_fnewton_solve_f

问题是我有几个这样的f (一些有两个以上的参数),如果我newton_solve_f为每一个都做一个单独的几乎相同的newton_solve_f ,那将是非常整洁的。

你如何解决这个VBA?

例如在Python中,可以按如下方式解决这个问题:

 def f(y): def g(x): return x^3-y return g def newton_solve(f): #do newton's method on f(x) newton_solve(f(3)) 

这里f(3)是一个函数,一个variables的闭包 。 (维基百科上的封闭示例几乎与此相同。)

PS。 我知道牛顿的方法也需要f的(部分)导数,实际上我正在做一些更像割线方法的东西,但这与我所问的无关

闭包不是VBA的一部分。 但是你可以在方法范围内使用静态variables。 他们不能在方法之外使用。 如果你想要一个variables在外面可见,那么你必须使用全局variables。 最好在一个模块中声明它。

在VB中我们不能定义函数里面的函数。 试图转换您提到的链接中给出的代码。 我希望它可以帮助你。 不熟悉PHP,但你可以看到下面的方法,并作出相应的变化。

 Sub Test() Dim x As Double Dim y As Double Dim z As Double x = Cells(1, 1).Value y = Cells(1, 2).Value z = NewtRap("Fun1", "dFun1", x, y) Cells(1, 3).Value = z End Sub Private Function NewtRap(fname As String, dfname As String, x_guess As Double, y_value As Double) As Double Dim cur_x As Double Dim Maxiter As Double Dim Eps As Double Maxiter = 500 Eps = 0.00001 cur_x = x_guess For i = 1 To Maxiter If (fname = "Fun1") Then fx = Fun1(cur_x) ElseIf (fname = "dFun1") Then fx = dFun1(cur_x) ElseIf (fname = "f") Then fx = f(cur_x, y_value) End If If (dfname = "Fun1") Then fx = Fun1(cur_x) ElseIf (dfname = "dFun1") Then fx = dFun1(cur_x) ElseIf (dfname = "f") Then fx = f(cur_x, y_value) End If If (Abs(dx) < Eps) Then Exit For cur_x = cur_x - (fx / dx) Next i NewtRap = cur_x End Function Function f(x As Double, y As Double) As Double f = x ^ 3 - y End Function Function Fun1(x As Double) As Double Fun1 = x ^ 2 - 7 * x + 10 End Function Function dFun1(x As Double) As Double dFun1 = 2 * x - 7 End Function 

所以,首先要总结一下:你想创build一个函数,它会find(使用牛顿 – 拉夫逊方法)函数的根。 你已经写好了这个函数,但是希望能够帮助你扩展你的代码,这样就可以使用不同数量的函数了。

我想你首先需要考虑一下你想要的input函数。 如果你只处理多项式(如你的例子所示),这应该是相当简单的。

你可以有一般的function:

 Function fnGeneralCase (x, y, z, w, a1, a2, a3, b1, b2, b3, c1, c2, c3 as Double) as Double fnGeneralCase = a1*x^3 + a2*x^2 + a3*x + b1*y^3 + b2*y^2 + b3*y + c1*z^3 + c2*z^2 + c3*z + w End Function Function fnDerivGeneralCase (x, y, z, w, a1, a2, a3, b1, b2, b3, c1, c2, c3 as Double) as Double fnDerivGeneralCase = a1*3*x^2 + a2*2*x + a3 + b1*3*y^2 + b2*2*y + b3 + c1*3*z^2 + c2*2*z + c3 End Function 

只要将input设置为零,当你不需要它们(这将是大多数时间)。

所以对于你的例子来说:

 answer = fnGeneralCase(guess, 0, 0, -8, 1, 0, 0, 0, 0, 0, 0, 0, 0) 

基本上给出:

 function = x^3-8 

如果你想包括多项式,这将变得更复杂,但你仍然可以使用上面的方法…

这似乎是问了2个相关的问题:

  1. 如何在vba中传递一个函数作为参数。
  2. 如何从现有函数中创build一个闭包。

不幸的是,这些都没有得到真正的支持,

  • 对于1,通常可以通过传递string函数名称并使用“Application.Run”来调用该函数。
  • 如果你有很多不同数量的参数的函数,但是对于一定数量的参数,你可以为newton_solve函数增加额外的参数或者使用全局variables。

例如

 Public Function f(x as Double, y as Double) as Double f = x^3-y End Function Function newton_solve_f (function_name as String, y as Double) as Double Dim x as Double x = 0 'initial guess for x 'do Newton's method to find x ... ' invoke function_name x = Application.Run(function_name, x, y) ... newton_solve_f = x End Function 

假设f在一个名为“Module1”的模块中,你可以这样调用它:

 x = newton_solve('Module1.f', 3) 

请注意,您要调用的function必须是公共的。