web-dev-qa-db-ja.com

Matlabのニュートンラプソン法

私はmatlabを初めて使用し、近似x = aを開始してニュートンラプソン法をn回反復する関数を作成する必要があります。この開始近似は相互作用としてカウントされず、別の要件はforループが必要であることです。投稿された他の同様の質問を見てきましたが、私の場合はwhileループを使用したくありません。

これは私の入力が想定されているものです:

mynewton(f,a,n) which takes three inputs: 
f: A function handle for a function of x.
a: A real number.
n: A positive integer.

そして、これがこれまでの私のコードです。

function r=mynewton(f,a,n)
syms x;
z=f(x);
y=a;
for i=1:n    
    y(i+1)=y(i)-(z(i)/diff(z(i)));
end
r=y
end

関数を呼び出そうとすると、次のようなエラーが発生します。

Error in MuPAD command: DOUBLE cannot convert the input expression into a double    array.
If the input expression contains a symbolic variable, use the VPA function instead.
Error in mynewton (line 6)
y(i+1)=y(i)-(z(i)/diff(z(i)));

質問はこのVPA関数をどのように使用しますか?私のコードの残りの部分もおそらく100%正しいわけではありませんが、vpaの問題に対処したり、コードの他の部分を修正したりするためのヘルプをいただければ幸いです。

ありがとう!

4
user3908437

ニュートンラプソン法では正しくないことが2つありますが、確かに修正可能です。これを修正した後は、あなたが話しているVPAエラーは必要ありません。


エラー#1-反復の更新

最初のものは反復そのものです。ニュートンラプソン法の定義を思い出してください。

何とかhttp://web.mit.edu/10.001/Web/Course_Notes/NLAE/equation6.gif

次の反復では、前の反復の値を使用します。 loop counterを使用し、これをf(x)に代入していますが、これは正しくありません。 前の反復の値である必要があります。

エラー#2-記号値と数値の混合

関数のコーディング方法を見ると、関数をシンボリックに定義していますが、関数にnumeric値を代入しようとしています。残念ながら、これはMATLABでは機能しません。実際にの値を代入する場合は、 subs を使用する必要があります。これにより、実際の値がxの関数として、または関数が使用している独立変数に置き換えられます。これを行っても、値はsym型のままです。これを数値で使用できるようにするには、これをdoubleとしてキャストする必要があります。


また、効率を上げるために、yを配列にする必要はありません。これを、反復ごとに更新される単一の値にするだけです。以上のことをすべて踏まえると、コードは次のように更新されます。念のために言っておきますが、私はループの前に関数の導関数を取り、必要な計算量を減らしました。また、ニュートン-ラフソン反復の分子と分母の項を分割して、物事を明確にし、これをsubsにとってより口に合うようにしました。難しい話は抜きにして:

_function r = mynewton(f,a,n)
syms x;
z = f(x);
diffZ = diff(z); %// Edit - Include derivative
y = a; %// Initial root

for idx = 1 : n    
    numZ = subs(z,x,y); %// Numerator - Substitute f(x) for f(y)
    denZ = subs(diffZ,x,y); %// Denominator - Substitute for f'(x) for f'(y)
    y = y - double(numZ)/double(denZ); %// Update - Cast to double to get the numerical value
end
r = y; %// Send to output
end
_

ループ内でiidxに置き換えたことに注意してください。理由は、iまたはjをループインデックスとして使用することは実際には推奨されていないためです。これらの文字は、複素数を表すために予約されているためです。 Shai によるこの投稿を見ると、これらの変数をループインデックスとして使用する方が実際には遅いことがわかります。 iとjをMatlabの変数として使用

いずれにせよ、これをテストするために、関数がy = sin(x)であり、最初のルートが_x0 = 2_であり、5回の反復があると仮定します。

_f = @(x) sin(x);
r = mynewton(f, 2, 5)

r =

3.1416
_

sin(x)の切片はpiの整数倍にあるため、これはsin(x)の知識と一致します。 _x0 = 2_はpiの近くにあるため、これは期待どおりに機能します。


あなたのための少しのボーナス

元のコードでは、各反復でルートの値がyに格納されていました。本当にやりたい場合は、次のようにコードを変更する必要があります。物事をより効率的にするために、yを事前に割り当てたことを覚えておいてください。

_function r = mynewton(f,a,n)
syms x;
z = f(x);
diffZ = diff(z);
y = zeros(1,n+1); %// Pre-allocate output array 
y(1) = a; %// First entry is the initial root

for idx = 1 : n    
    numZ = subs(z,x,y(idx)); %// Remember to use PREVIOUS guess for next guess
    denZ = subs(diffZ,x,y(idx));
    y(idx+1) = y(idx) - double(numZ)/double(denZ); %// Place next guess in right spot  
end
r = y; %// Send to output
end
_

上記とまったく同じパラメータを使用してこのコードを実行すると、次のようになります。

_f = @(x) sin(x);
r = mynewton(f, 2, 5)

r =

    2.0000    4.1850    2.4679    3.2662    3.1409    3.1416
_

rの各値は、その特定の反復でのルートの推測を示します。配列の最初の要素は(もちろん)最初の推測です。次の値は、ニュートンラプソンルートの各反復での推測です。配列の最後の要素は最後の反復であり、これはpiにほぼ等しいことに注意してください。

9
rayryeng

evalを使用する必要があります

したがって、z(x)=f(x)、次にeval(z(1))を使用して、x = 1で関数を評価します。

完全なコード:

function r=mynewton(fun,a,n)
% e.g. for mynewton(@sin, 1, 2)
syms x;
z(x)=fun(x);  % sin(x)
y=a;
for i=1:n    
    y(i+1)=y(i)-(eval(z(i))/eval(diff(z(i))));
end
r=y
end

質問のVPA部分についてはよくわかりませんが、通常はMUPADエラーを無視します:P

0
legas