简体   繁体   English

C# Unity3D 微积分

[英]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.我想用 Unity 做类似于 MATLAB 的数学运算,也想用化学、物理和工程学做科学类的事情。

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.我仍在学习微分方程,但想将其与 C# 中的数值计算联系起来进行计算。

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).这是 Unity 开发的基本管道(实际上是我们这个时代的所有软件)。

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!您可以从这个 QA 开始,其中提到了许多软件包!

https://stackoverflow.com/a/34208687/294884 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!通过完全熟悉现有的包,您将首先学习如何打包这样的东西、API 等,这是您制作自己的包时必须熟悉的第一件事!

Your implementation is a good approximation (since derivative is a limit and h is a finite value).您的实现是一个很好的近似值(因为导数是一个极限,而h是一个有限值)。 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 :如果您经常使用Derivative ,您可以尝试将其声明为扩展方法

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演示:让我们找出当x[0 .. 2 * PI)范围内的Sin函数时的最大误差

// 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 .但是我们可以问: h如何趋向于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);对于复数,我们有很多方法(例如, h可以螺旋下降到0或沿着一条直线); in case of real numbers h can be either positive ( right semi-derivative) or negative ( left semi-derivative).实数的情况下, h可以是正数右半导数)或负数左半导数)。 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))/dxx = 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 .Abs(x)的情况下,你会弄错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).如果不是在微积分中而是在工程中(想象一辆确实有速度的移动汽车), 0在上下文中是一个更好的答案。 Another issue is that when computing derivative at x we don't need f exist at x .另一个问题是,当计算x处的导数时,我们不需要f存在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).请注意, x = 0处的导数df/dx存在(它是正无穷大)。 That's why when computing derivative we should avoid computing f(x) .这就是为什么在计算导数时我们应该避免计算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); double.NaN - 导数不存在(这是错误的); "central" derivative will return “中心”衍生品将回归

 (h/h - -h/h) / (2 * h) == 1 / h == some huge number (approximation of +Inf)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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