【问题标题】:Solve non-linear system of equations R求解非线性方程组 R
【发布时间】:2017-04-02 19:06:08
【问题描述】:

我尝试使用 R 中的 nleqslv 函数求解一组非线性方程组。不幸的是,我在猜测正确的初始值以使函数成功运行时遇到了麻烦。我有一个值在 0 和 1 之间的向量,称为 c(t)。它们应满足以下等式

c(t)=A*(exp(-mt)+exp(-m(1024-t)))+B^2

使用 t 的三个后续值,我的目标是使用以下代码确定系数 A、B、m

library(nleqslv)

C10 <- c(1.000000e+00,9.754920e-01,9.547681e-01,9.359057e-01,9.182586e-01,9.014674e-01)
system_size <- 1024

for(i in 2:5)
{
  C <- c(C10[i-1],C10[i],C10[i+1],i-2)
  #function
  target <- function(Coeffs){
  y <- numeric(3)
  y[1] <- Coeffs[1]*(exp(-Coeffs[2]*(C[4]-1))+exp(-Coeffs[2]*(system_size-(C[4]-1))))+Coeffs[3]^2-C[1]
  y[2] <- Coeffs[1]*(exp(-Coeffs[2]*(C[4]))+exp(-Coeffs[2]*(system_size-(C[4]))))+Coeffs[3]^2-C[2]
  y[3] <- Coeffs[1]*(exp(-Coeffs[2]*(C[4]+1))+exp(-Coeffs[2]*(system_size-(C[4]+1))))+Coeffs[3]^2-C[3]
  y
 }
 init <- c(0.001,0.01,0)
 sol <- nleqslv(init, target,control=list(btol=.01), method="Broyden")
 }

使用的初始值反映了我在绘制关联值 c(t) 时得到的结果。尽管如此,生成的输出 sol 给出了

chr "Jacobian is ill-conditioned (1/condition=9.0e-18) (see allowSingular option)"

知道出了什么问题以及如何解决这个问题吗?


OP 编辑​​:修改代码以具有最小的工作示例:为 C10 添加了前几个值,调整了循环并为 system_size 添加了值

【问题讨论】:

  • 奇异雅可比行列式表示初始猜测导致解发散。非线性求解器仅与它们开始时的初始猜测一样有效,因此更改您的初始猜测可能会有所帮助。
  • 请提供一个可重现的例子。提供 C10system_size 的值。并仔细检查你的函数方程是否有任何错误。
  • @Bhas 添加了请求的值以获得可重现的示例。
  • @Mislav 虽然最初的猜测没问题,但算法是否也可能失败?

标签: r


【解决方案1】:

随着您为C10 添加一些数据,如果考虑到循环应该考虑length(C10),则该示例运行得非常好。 像这样(有一些变化;为什么见下文):

library(nleqslv)

C10 <- c(1.000000e+00,9.754920e-01,9.547681e-01,9.359057e-01,9.182586e-01,9.014674e-01)
system_size <- 1024

target <- function(Coeffs){
    y <- numeric(3)
    y[1] <- Coeffs[1]*(exp(-Coeffs[2]*(C[4]-1))+exp(-Coeffs[2]*(system_size-(C[4]-1))))+Coeffs[3]^2-C[1]
    y[2] <- Coeffs[1]*(exp(-Coeffs[2]*(C[4]))+exp(-Coeffs[2]*(system_size-(C[4]))))+Coeffs[3]^2-C[2]
    y[3] <- Coeffs[1]*(exp(-Coeffs[2]*(C[4]+1))+exp(-Coeffs[2]*(system_size-(C[4]+1))))+Coeffs[3]^2-C[3]
    y
}

init <- 50*c(0.001,0.01,0)

for(i in 2:min(length(C10)-1,(system_size/2)))
{

    C <- c(C10[i-1],C10[i],C10[i+1],i-2)
  #function
    target <- function(Coeffs){
        y <- numeric(3)
        y[1] <- Coeffs[1]*(exp(-Coeffs[2]*(C[4]-1))+exp(-Coeffs[2]*(system_size-(C[4]-1))))+Coeffs[3]^2-C[1]
        y[2] <- Coeffs[1]*(exp(-Coeffs[2]*(C[4]))+exp(-Coeffs[2]*(system_size-(C[4]))))+Coeffs[3]^2-C[2]
        y[3] <- Coeffs[1]*(exp(-Coeffs[2]*(C[4]+1))+exp(-Coeffs[2]*(system_size-(C[4]+1))))+Coeffs[3]^2-C[3]
        y
    }

    cat("i=",i,"init=",init, "target(init)=",target(init),"\n")
    sol <- nleqslv(init, target,control=list(btol=.01), method="Broyden")
    print(sol)
}

使用您的初始起始值,模型无法解决并给出您提到的错误消息。我通过增加值更改了init 的值。然后找到解决方案直到i=5。具有给定 C10 的较大值将不会运行,因为在循环内 C10[i+1] 被引用(并且它不存在)。

我在调用nleqslv 之前插入了一个cat 语句,在函数调用之后插入了一个print(sol),这样至少可以看到发生了什么以及是否真的找到了解决方案。

您不需要指定method="Broyden",因为它是默认值。

如果发生错误,您应该在for 循环结束出口中测试sol$termcd

像这样的脚本总是在循环内打印东西!

Mislav 是正确的:起始值可能完全错误。

即使起始值没问题,使用的算法也可能会失败。这就是包提供函数testnslvsearchZeros的原因。

我用testnslv 做了一些实验(此处未显示),结论是method="Newton" 是失败的。狗腿式全球战略似乎总是奏效。线搜索策略并不总是有效。

【讨论】:

  • 非常感谢,你是对的,看来我只是错过了这样一个事实,即虽然更改初始值,但在循环后期可能会出现问题,因为在我的旧设置中没有显示循环中的程序我得出了一个错误的结论,即它根本不起作用。不幸的是,对于当前的初始值,我并没有走得太远(它卡在第 10 位)。有什么好的(自动)方法来猜测初始值吗?我只是来到蛮力解决方案,只是简单地用一定范围内的初始值重新初始化算法,直到成功..
  • 我想不出猜测/设置初始值的好方法。如果您可以为初始值提供界限,您可以尝试包中的函数searchZeros。使用随机数生成器(例如runif)为每个参数生成例如 100 个初始值,并且不要忘记在运行生成器之前设置种子(请参阅set.seed)以实现可重复性。
  • 您可以尝试使用迭代i 的解作为下一次迭代i+1 的初始猜测。在sol &lt;- nleqslv....) 之后执行init &lt;- sol$x after 测试是否存在使用sol$termcd 的有效解决方案。
  • 谢谢你,你的想法让我更进一步。可悲的是,在某些时候我仍然得到“Jacobian 条件太差(1/condition=1.0e-12)”-警告。有没有办法“保护”雅可比?此外,我有时会收到“找不到更好的点(算法已停止)”的消息,我有点担心。我是否正确,在平滑变化的参数的假设下不应该有任何问题?
  • 您阅读过nleqslv 手册吗? control 参数中有一个 allowSingular 项。 No better point found ... 记录在手册中。这意味着没有找到可接受的点;综上所述,就是函数判据不低于上一次迭代的判据。如果算法接受 x 值,即使函数值可接受,它也会报告收敛。您将不得不查看生成的函数值来决定您是否可以接受输出。你被警告了!!!
猜你喜欢
  • 1970-01-01
  • 2021-12-13
  • 2019-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多