2016-12-03 3 views
-2

以下は、与えられた関数fの導関数を計算するためのPythonコードです。2つのバージョンの微分計算の精度の違いは何ですか?

  1. バージョン1(ソリューション)

    x[ix] += h # increment by h 
    fxh = f(x) # evalute f(x + h) 
    x[ix] -= 2 * h 
    fxnh = f(x) 
    x[ix] += h 
    numgrad = (fxh - fxnh)/2/h 
    
  2. バージョン2(私のバージョン)

    fx = f(x) # evalute f(x) 
    x[ix] += h 
    fxh = f(x) # evalute f(x+h) 
    x[ix] -= h 
    numgrad = (fxh - fx)/h 
    

それはバージョン1がより良い精度を与える示しており、誰もが理由を説明することができ2つの計算の違いは何ですか?

更新 最初は数学的な問題であることはわかりませんでしたが、浮動精度の影響に関する問題だと思いました。 MSeifertで提案されているように、私はフロートポイントのノイズが重要であることに同意しますが、ノイズに曝されるとノイズの影響がより小さくなります。

+0

はCS.SEへようこそ!一般的に言えば、コードはここでは話題にはならず、Python固有のものはここでは話題にはなりません。スタックオーバーフローについては、コーディングに関する質問がしばしば尋ねられます。そこに質問を移動したい場合は、「フラグ」をクリックして司会者の注意を喚起し、改造者に移動を依頼してください。あるいは、これがPython固有でない場合は、Pythonを知らない人でも理解できる数学または擬似コードでコードを置き換えてください。 (例えば:私はいくつかのPythonを知っていますが、この文脈で 'x [ix]'が何を意味するのか分かりません) –

+0

最初のバージョンがより良い精度を与えるという文は一般的に真実ではありません。片面近似が*数学的に有利な(つまり、コンピュータの浮動小数点演算とは独立している)状況があります。たとえば、[upwind schemes](https://en.wikipedia.org/wiki/Upwind_scheme)を参照してください。なぜ最初のバージョンがより正確であるのか*ほとんどの時間*、[有限差分](* https://en.wikipedia.org/wiki/Finite_difference)の* order *に精通してください。 – Phillip

答えて

3

これはPythonの問題ではなく、純粋なalgorythmic問題です。

((f(x+h) - f(x))/h) - f'(x) = h/2 f"(x) + o(h) 

大きさ順のエラーです:

f(x+h) = f(x) + h f'(x) + h*h/2 f"(x) + h*h*h/6 f'''(x) + o(h3) 

それはあなたの最初のフォームがエラーを与えることを来る:関数fは素敵な性質を持っていると仮定すると、あなたはそのTaylor series発展を見ることができますあなたは二番目の形式を使用している場合は時間の

、あなたが得る:

((f(x+h) - f(x-h))/2*h) - f'(x) = h*h/3 f'''(x) + o(h2) 

時間内の用語が落ちてきたし、必要な誘導体が存在する場合は、エラーがもちろんH

の大きさの順にそれだけで理にかなっている...

0

解決策は「片面」です。f(x+h) - f(x)を比較し、一般的な解決策は「両面」f(x+h) - f(x-h)です。

It has shown version one gives a better accuracy

手段を知って良いでしょう。それはあまりにも一般的なので。両面結果が期待値に近いことを

def double_sided_derivative(f, x, h): 
    x_l, x_h = x - h, x + h 
    return (f(x_h) - f(x_l))/2/h 

def one_sided_derivative(f, x, h): 
    x_h = x + h 
    return (f(x_h) - f(x))/h 

h = 1e-8 

def f(x): 
    return 1e-6 * x 

# difference to real derivate: 
double_sided_derivative(f, 10, h) - 1e-6, one_sided_derivative(f, 10, h) - 1e-6 
# (6.715496481486314e-14, 1.5185825954029317e-13) 

注:

は、しかし、私は、私がここに適切かもしれない例があると思います。これにより、catastrophic cancellationにつながることさえあります。その後、浮動小数点ノイズによって主に制御される結果に終わることがあります。値が実際に小さい数で除算されるため、この効果はさらに強化されます。

両面を使用すると、差異が大きくなり、キャンセルが発生する可能性が高くなります(機能によって異なります)。しかし、私の意見では、最大の利点は、あなたが両方の面で斜面を考慮に入れることです(それを幾分平均して)。たとえば、次のようになります。

h = 1e-6 

def f(x): 
    return 4 + x + 5 * x**2 

def fdx(x): 
    return 1 + 10 * x 

x = 10 

double_sided_derivative(f, x, h) - fdx(x), one_sided_derivative(f, x, h) - fdx(x) 
# (-2.7626811061054468e-08, 4.974594048690051e-06) 

これは、片側近似よりも真の値(2桁の大きさ)に非常に近い値です。

+0

私は最大の利点は、最初のバージョンのエラーは 'O(h²)'にあり、もう一方は 'O(h)'にあるということです。 – Phillip

+0

@Phillip私はそれが機能に依存していると思います。私は主に浮動小数点アスペクトに焦点を当てていました(浮動小数点精度タグ付き)。または、浮動小数点演算の結果として 'O(h ** 2)'が返されますか? – MSeifert

+0

これは、第1導関数と第2導関数が存在するすべての関数を保持する数学的結果であり、コンピュータ内での表現ではなく、数学的オブジェクト上のものです。 – Phillip

関連する問題