【JavaScript】微分係数の近似値を求めて接線を描いてみる

第3回と第4回ではグラフの描き方にチャンレジしてみました。第4回では関数 f(x) を定義し,それをグラフとして描くことができるようになったので,あとは関数の定義の部分を書き換えれば学校で習ったさまざまな関数を実際にグラフとして確かめることができるようになります。

以前書いたコードの改良

<!DOCTYPE html><html><body><script>

//関数f(x)を定義する
let f = e => {
    const y = e*e;
    return y;
};

const range = 4; // x軸両端の幅
let x = -range/2; //始点のx座標

//軸線を描く
document.write('<svg width=400 height=400><line x1=0 y1=200 x2=400 y2=200 stroke="black"/><line x1=200 y1=0 x2=200 y2=400 stroke="black"/>');

//関数のグラフを描く
for(let i=0; i < 400; i++) { //処理を400回繰り返し

    //始点と終点の座標をもとに直線を引く

    document.write('<g transform="translate(200,200)scale('+(400/range)+', '+(-400/range)+')"><line x1='+x+' y1='+f(x)+' x2='+(x+range/400)+' y2='+f(x+range/400)+' stroke="blue" stroke-width="'+range/400*2+'" /></g>');

    x += range/400; // xの値を次の点の座標にする

};

//最後にタグを閉じる
document.write('</svg>');

</script></body></html>

前に作った二次関数のグラフを描くコードをさらに改良しました。関数の定義をはじめに持ってきたことと縮尺の調整ができるようになりました。定数 range は $x$ 軸両端の幅を表し,range = 4 は左端の $x$ 座標が $-2$,右端Sが $+2$ ということです。描画作業は $x$ 座標 $-2$ から $2$ までを 400 分割して直線をつなげていきます。線の太さも縮尺によって変わるので range/400*2 として調整しています。これでグラフの大きさを変えるのが楽になりました。

微分係数の近似値を求める

さて,ここから式を微分して接線の傾きを求め,直線を引いてみましょう。

微分といえば,普通なら $y=x^2$ は $y’=2x$ となり,公式ですぐに傾きが分かります。もちろんこれを使って直線を引いてもいいのですが,もし関数がその他のさまざまな形をとるとしたら,微分係数をどう求めるかが問題になってきます。

そこで,微分係数を近似値で求めることでグラフとして描けるようにしよう,というのが今回チャレンジする内容です。

もし,$y=f(x)$ という関数があるとしたら微分係数は

$\displaystyle f'(x)=\lim_{h\rightarrow0}\cfrac{f(x+h)-f(x)}{h}$

でした。数IIの微分の初めに習った定義です。また,これをもとに $x=t$ 接線の方程式を求めると

$y=f'(t)(x-t)+f(t)$

となります。

これで数式の材料はそろいました。

<!DOCTYPE html><html><body><script>

//関数f(x)を定義する
let f = e => {
    const y = e*e;
    return y;
};

//接線を関数fd(x)として定義する
let fd = e => {
    const h = 0.001;
    const yd = (f(t+h)-f(t))/h; // 接線の傾きを求める
    const y = yd*(e-t)+f(t);
    return y;
};

const t = 0.6; // 微分係数を求める位置
const range = 4; // x軸両端の幅
let x = -range/2; //始点のx座標

//軸線を描く
document.write('<svg width=400 height=400><line x1=0 y1=200 x2=400 y2=200 stroke="black"/><line x1=200 y1=0 x2=200 y2=400 stroke="black"/>');

//関数のグラフを描く
for(let i=0; i < 400; i++) { //処理を400回繰り返し

    //始点と終点の座標をもとに直線を引く

    document.write('<g transform="translate(200,200)scale('+(400/range)+', '+(-400/range)+')"><line x1='+x+' y1='+f(x)+' x2='+(x+range/400)+' y2='+f(x+range/400)+' stroke="blue" stroke-width="'+range/400*2+'" /></g>');

    //接線を引く
    document.write('<g transform="translate(200,200)scale('+(400/range)+', '+(-400/range)+')"><line x1='+x+' y1='+fd(x)+' x2='+(x+range/400)+' y2='+fd(x+range/400)+' stroke="green" stroke-width="'+range/400*2+'" /></g>');

    x += range/400; // xの値を次の点の座標にする

};

//最後にタグを閉じる
document.write('</svg>');

</script></body></html>

コードの追加部分を説明します。

//接線を関数fd(x)として定義する
let fd = e => {
    const h = 0.001;
    const yd = (f(t+h)-f(t))/h; // 接線の傾きを求める
    const y = yd*(e-t)+f(t);
    return y;
};

接線の方程式を関数 y=fd(x) として定義しています。関数の内部では x は e という変数で扱っています。$\displaystyle f'(x)=\lim_{h\rightarrow0}\cfrac{f(x+h)-f(x)}{h}$ をコードで表すと const yd = (f(t+h)-f(t))/h; となります。$f'(x)$ を変数 yd として $x=t$ の位置における接線の傾きを求めています。本来はこの h を限りなく 0 に近づけることで接線の傾きを求めるのですが,これを h = 0.001 という 0 に近い小数を用いることで近似しています。近似値をもっと本来の値に近づけたいならこの小数をもっと小さくすれば良いのですが,これ以上小さくしても画面上での違いは分からないでしょう。

const t = 0.6; // 微分係数を求める位置

ここで,微分係数を求める位置 $x=t$ の値を代入しています。ここでは $x=0.6$ における接線の傾きを求めるということです。

    //接線を引く
    document.write('<g transform="translate(200,200)scale('+(400/range)+', '+(-400/range)+')"><line x1='+x+' y1='+fd(x)+' x2='+(x+range/400)+' y2='+fd(x+range/400)+' stroke="green" stroke-width="'+range/400*2+'" /></g>');

関数のグラフを描画する部分の下に,接線を描画する部分を書いています。f(x) が二次関数の式の $y$ 座標であるのに対し,fd(x) は接線の式の $y$ 座標を表しています。画面上では 1 本の直線に見えますが,実際には短い直線をつなぎ合わせて 1 本の直線にしています。

あとは,関数 f(x) の定義の const y = e*e; の部分が二次関数 $y=e^2$ を表しているので,この関数の形を変えれば他のさまざまな関数の接線を引くことができます。いろいろ試してみましょう。