简体   繁体   中英

C# Unity3D Calculus

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

familiarize yourself with all the existing libraries, which approach the challenge you are interested in.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM