Open sjkaliski opened 11 years ago
Look at simpsonRecursive() in /numeric/calculus.js. It takes an epsilon argument, which is part of ensuring the result is in a certain error bound.
I think we're going to need to use error bounds, because all the calc functions with limits are going to be a little off. To reliably convert them to the right answer seems impossible without vastly complicating things.
@ethanresnick did you see the implementation I started putting in place? It's actually really only relevant for testing...but it also could just be used globally for any functions requiring an error bound.
I like it. Going to make a few changes w/ explanations in the commit logs; let me know what you think. We can always revert them.
Don't think this is really closed.
Epsilon gets around the JS rounding errors, but it doesn't get around the other kind of error (i.e. that we're approximating "infinitely close" in derivatives and limits with fixed decimal distances).
For example, if I do pointDiff(function(x) { return 999999999*Math.pow(x,2); }, 10)
, the answer is .6 off, which is greater than the default EPSILON
, and that's just due to the fact that the function's not being evaluated close enough to 10 for the derivative to stay super accurate.
To fix this kind of issue, we could just cheat and make the decimal used in pointDiff
et al even smaller, and frankly, that might be fine. But to do it "right" I think we need some sort of recursion with conversion tracking, i.e. keep running pointDiff and evaluating the function closer to 10 each time until the change in the answer between iterations falls below EPSILON
. (Though, with this use, maybe epsilon wouldn't be the best name for that threshold...not sure.) That's what I thought you were talking about with the do...while
s.
Yeah you're right, I just got overly excited.
I am concerned that if you went with a recursive method, you might exceed max stack size.
Maybe in addition to epsilon, you provide a max recursion count that limits how far that goes. Because if you wanted to get close enough, you might notice performance problems. But if you're fine with some level of error in hopes of gaining efficiency, then you can reduce it.
The do...while
was partially a joke because I think it's an insane construct, but it actually would be super useful in this case.
Thoughts?
Hmm...thinking about this some more, maybe just replacing the decimal used in pointDiff
with Number.MIN_VALUE
is the right option. After all, we're saying that pointDiff
's decimal is not small enough and proposing to making it iteratively smaller, so what if we just used the smallest possible value right off the bat? Carrying around all those decimal places might be too memory intensive, but it would definitely be simpler.
If we do decide to do something iterative/recursive, then I think (though correct me if I'm wrong) that using do...while
gets around the max stack size worry because it doesn't introduce another function call/new vars.
Re having a max recursion count exposed in the API: I have to think about it some more; could definitely be useful, but it seems like it might be overkill too.
So I started writing tests for calculus.
Here's an example:
Now, the point derivative of 2x + 2 at any value is 2. The method returns the following: 1.9999999999242843
This is "basically" 2, but it won't pass tests. So I've considered two options:
I like two more, although it certainly is going to be a hassle to set up. But this way we can have an "acceptable" error bound, which is sustainable and acceptable for the browser, and has minimal effect in performance or large scale operations.