I want to use Unity to do math kind of like MATLAB, but also science type things in chemistry, physics, and engineering.
Just wanted to ask if these functions sound right for computing derivatives and partial derivatives numerically, and how I might go about doing 2nd partial derivatives and Laplace operator like in formulas like the Schrodinger's Equation, the Heat Equation, and so on?
I'm still learning Differential Equations, but wanted to relate it to numerical computation in C# for calculation.
public double Derivative(Func<double, double> function, double x, double h)
{
return (function(x + h) - function(x)) / h;
}
public double SecondDerivative(Func<double, double> function, double x, double h)
{
return (function(x + h) - 2 * function(x) + function(x - h)) / (h * h);
}
public double ThirdDerivative(Func<double, double> function, double x, double h)
{
return (function(x + 3 * h) - 3 * function(x + 2 * h) + 3 * function(x + h) - function(x)) / (h * h * h);
}
public double PartialDerivativeX(Func<double, double, double> function, double x, double y, double h)
{
return (function(x + h, y) - function(x, y)) / h;
}
public double PartialDerivativeY(Func<double, double, double> function, double x, double y, double h)
{
return (function(x, y + h) - function(x, y)) / h;
}
You probably have a little way to go.
As a first step, what you should do / must do is
This is the basic pipeline of Unity development (and really all software in our era).
If you search the web, you will find the libraries which handle issues such as derivatives, transcendental functions, etc etc
you could start with this QA, a number of packages are mentioned!
https://stackoverflow.com/a/34208687/294884
By totally familiarizing yourself with existing packages, you will to begin with learn how to package such a thing, API, etc, which is the first thing you have to get comfortable with when you make your own!
Your implementation is a good approximation (since derivative is a limit and h
is a finite value). However, I suggest some different code:
public static class MyMath {
// static: we don't want "this"
// Func<double, double> return value: derivative is a function, not a value.
// If we want a point - double - let's name the method as DerivativeAt
// No h - we can't provide correct h for all possible x
public static Func<double, double> Derivative(Func<double, double> function) {
//DONE: Validate public methods arguments
if (null == function)
throw new ArgumentNullException("function");
return new Func<double, double>((x) => {
// Let's compute h for given x
// Easiest, but not the best
double h = Math.Abs(x) < 1e-10 ? 1e-16 : x / 1.0e6;
// "Central" derivative is often a better choice then right one ((f(x + h) - f(x))/h)
return (function(x + h) - function(x - h)) / (2.0 * h);
});
}
// h = 0.0: be nice and let user has no idea what step is reasonable
public static double DerivativeAt(Func<double, double> function,
double x,
double h = 0.0) {
//DONE: Validate public methods arguments
if (null == function)
throw new ArgumentNullException("function");
// If user don't want to provide h, let's compute it
if (0 == h)
h = Math.Abs(x) < 1e-10 ? 1e-16 : x / 1.0e6; // Easiest, but not the best
// "Central" derivative is often a better choice then right one ((f(x + h) - f(x))/h)
return (function(x + h) - function(x - h)) / (2.0 * h);
}
}
If you frequently use Derivative
you can try declaring it as an extension method :
public static Func<double, double> Derivative(this Func<double, double> function) {...}
public static double DerivativeAt(this Func<double, double> function,
double x,
double h = 0.0) { ... }
Demo: let's find out maximum error when x
within [0 .. 2 * PI)
range for Sin
function
// We don't want to repeat pesky "MyMath" in "MyMath.Derivative"
using static MyNamespace.MyMath;
...
// Derivative of Sin (expected to be Cos)
var d_sin = Derivative(x => Math.Sin(x));
double maxError = Enumerable
.Range(0, 1000)
.Select(i => 2.0 * Math.PI * i / 1000.0)
.Select(x => Math.Abs(d_sin(x) - Math.Cos(x))) // d(sin(x)) / dx == cos(x)
.Max();
Console.WriteLine(maxError);
Outcome:
1.64271596325705E-10
Edit: "Central" derivative.
As we know, derivative is a limit
df/dx == lim (f(x + h) - f(x)) / h
h -> 0
however we can ask: how h
tend to 0
. We have a lot of ways in case of complex numbers ( h
can, say, spiral down to 0
or goes along a strait line); in case of real numbers h
can be either positive ( right semi-derivative) or negative ( left semi-derivative). Usually (standard definition) we require left semi-deivative be equal to right one in order to have derivative:
d+f(x) == d-f(x) == df/dx
However, sometime we use lenient definition ("central" derivative):
df/dx == (d+f(x) + d-f(x)) / 2
For instance, d(abs(x))/dx
at x = 0
d-abs(x) = -1
d+abs(x) = 1
d abs(x) / dx doesn't exist (standard definition)
d abs(x) / dx = 0 "central", lenient definition.
Please, note that you current code computes in fact right semi-derivative ; in case of Abs(x)
you'll get wrong 1
. 0
is a better answer in the context if not in calculus but in, say, engineering (imagine a moving car which does have a velocity). Another issue is that when computing derivative at x
we don't need f
exist at x
. For instance
f(x) = x / abs(x) which can be put as
-1 when x < 0
f(x) = doesn't exist when x = 0
+1 when x > 0
Please, note that derivative df/dx
at x = 0
exists (it's positive infinity). That's why when computing derivative we should avoid computing f(x)
. You current code will return
(h / h + 0 / 0) / h == (1 + NaN) / h == NaN
double.NaN
- derivative doesn't exists (and that is wrong); "central" derivative will return
(h/h - -h/h) / (2 * h) == 1 / h == some huge number (approximation of +Inf)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.