Find threshold value where f(x)=0

核能气质少年 提交于 2019-12-14 02:17:45

问题


I have a function f(x), that is positive and decreasing for x<c, and is zero for all x>=c. How can I find c, the threshold where the function hits zero (to within a tolerance)?

Here's an example:

zer = function(x){
    ifelse(x>5, rep(0,length(x)), 5 - x)
}

> x=-5:15
> plot(x,zer(x))

You can use uniroot to find where a function crosses zero, but that relies on the function being negative and positive on either side of the crossing, so I can't use that here. In the above case it evaluates zer at the upper interval (15), finds a zero, and returns that.

I wrote a bisection algorithm that starts with the interval and moves left if f(midpoint) == 0 or right if f(midpoint) > 0. This works, but I'm wondering if I've missed an implementation in a package or elsewhere that does this better, or if I've missed "one simple trick that lets you use uniroot to solve this".

The best I can find in the docs is the Numerical Task View's cryptic "There are implementations of the bisection algorithm in several contributed packages."

Note I don't have the gradient of f(x) so can't use Newton's method or anything that needs gradient evaluations at x values.


回答1:


One possibility - instead of returning 0 for f(x)==0, return a small constant negative number:

zer2 = function(x){
    y = zer(x)
    y[y==0]=-1e-6
    y
}

this gives a solution that can be found with uniroot:

> uniroot(zer2, c(-5,15))
$root
[1] 5.000043

The size of the small negative number might be important though.

Also, I'm not sure how well-behaved uniroot is if half the function is a constant value of -1 - it seems to cope in this case and its probably robust enough.




回答2:


This problem seems to be well suited to a bisection method. We can do it something like this for example:

findroot = function(f, x, tol = 1e-9) {
  if (f(x[2]) > 0) stop("No root in range")
  if (f(x[1]) <= tol) return (x[1])
  else {
    xmid = mean(x)
    if(f(xmid) == 0) x = c(x[1], xmid) else x = c(xmid, x[2])
    return (findroot(f, x, tol))
  }
}

findroot(zer, range(x))
# [1] 5



回答3:


I'm not sure this answers the question, but I've tried to follow your problem description to the letter:

  1. f(x) is positive and decreasing for x < c, and is zero for all x >= c;
  2. find its zero to within a tolerance.

So I start with a little helper function, a variant of a function I generally use in order to avoid FAQ 7.31. Then it's a simple matter of keeping the first TRUE it returns.

is.zero <- function(x, tol = .Machine$double.eps^0.5) abs(x) < tol

i <- which(is.zero(zer(x)))[1]
x[i]
#[1] 5

Off-topic.
The helper function mentioned is

is.equal <- function(x, y, tol = .Machine$double.eps^0.5) abs(x - y) < tol

This function is not equivalent to all.equal since it is vectorized and the values of the shorter argument x or y recycle.



来源:https://stackoverflow.com/questions/47484556/find-threshold-value-where-fx-0

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!