1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

なめらかなステップ関数

Last updated at Posted at 2023-03-25

これは何?

を読んで、多項式でやるとどうなるかを試したくなったので書いた。

なにかというと、値の異なる定数関数を滑らかにつなぎたいという話。

元の記事はシグモイド曲線を加工していたけど、多項式も悪くないよと思って。

設定

話を簡単にするため、



F(x)=\left\{
\begin{array}{ll}
-1 & if \: x≦-1 \\
f(x) & if\:-1<x<1 \\
1 & if \: 1≦x \\
\end{array}
\right.

という設定にする。
小文字の方の $ f(x) $ を多項式にする。

f(x) が満たすべき性質

満たすべき性質として必須なのはまず連続性。

  • $f(-1)=-1$
  • $f(1)=1$

それと

  • $f(-x)=-f(x)$

だと使いやすそうなのでこの性質も入れておく。

あとは適当に $n$ を定めて $0<i≦n$ のとき、$x=±1$ における $n$ 階微分がゼロになればいい。
そうすれば $F(x)$ は $n$ 階微分まで連続になる。

そういう関数は無限にあるが、そういう性質を満たす多項式で最低次数のものとなると一意に定まる。

1階微分まで 〜 6階微分まで のグラフ

対称性から偶数次の項がなくなる。
あとはふつうに方程式

\begin{align}

f(1) &= 1 \\
\frac{d^i}{dx^i}f(1) &= 0 \: \:  \: \: if \:  \:  \:  \: i ∈ \{1, 2, ..., n \}

\end{align}

を解けばよい。
解いた結果が以下。

1階微分まで連続

image.png

$f(x)$ は 3次関数になる。
$F(x)$ は 2階微分が不連続になる。

2階微分まで連続

image.png

$f(x)$ は 5次関数になる。
$F(x)$ は 3階微分が不連続になる。

3階微分まで連続

image.png

$f(x)$ は 7次関数になる。
$F(x)$ は 3階微分に微分不可能な点がある。

4階微分まで連続

image.png

$f(x)$ は 9次関数になる。
$F(x)$ は 3階微分もなめらか。

5階微分まで連続

image.png

$f(x)$ は 11次関数になる。

6階微分まで連続

image.png

$f(x)$ は 13次関数になる。

元の記事の計算

3階微分すると

image.png

こんな恐ろしいことになってしまうので、WolframAlpha 様に全部お願いした。

プロットして初めてわかったけど、2階微分の変曲点が多項式版より多い。

1階微分

image.png

2階微分

image.png

3階微分

image.png

まとめと感想

私から見える用途だと、3階微分まで連続、までは使いみちがありそう。

計算すると 10階でも20階でもいけるけど、そうなってくると工夫して計算しないと誤差が溜まって大変なことになるので要注意。

あと。

最初は python + matplotlib で書こうかと思ってたけど、 ruby + gnuplot で書いた。
ruby 好きなのでこっちが楽。

ソースコードは以下。100行足らず。

ruby
require "matrix"

def exptext(ks,c)
  d = ks.inject(1){ |acc,e| acc.lcm(e.denominator) }
  m = ks.zip(c).reverse.map{ |k,e|
    ex = e==1 ? "" : "^{#{e}}"
    ks = k*d==1 ? "" : (k*d==-1 ? "-" : "%+d" % (k*d).to_i )
    "#{ks}x#{ex}"
  }.join
  m.gsub!( /^\+/, "" )
  d==1 ? m : "(#{m})/#{d}"
end

def con(n)
  c=(0...n).map{ |e| e*2+1 }
  m=Matrix[*c.map.with_index(0){ |e,ix|
      if ix==0
        [1]*c.size
      else
        c.map{ |e| ((e-ix+1)..e).inject(&:*) }
      end
  }]
  v = Matrix[ [1]+[0]*(c.size-1) ].transpose
  ks=(m.inv*v).to_a.flatten
  [
    exptext(ks,c),
    ks.zip(c).map{ |k,e| "#{k.to_f}*x**#{e}" }.join("+"),
    ks.zip(c).map{ |k,e| "#{(k*e).to_f}*x**#{e-1}" }.join("+"),
    ks.zip(c).map{ |k,e| "#{(k*e*(e-1)).to_f}*x**#{[0,e-2].max}" }.join("+"),
    ks.zip(c).map{ |k,e| "#{(k*e*(e-1)*(e-2)).to_f}*x**#{[0,e-3].max}" }.join("+"),
  ]
end

def init_plot(n)
  [
    "set term pngcairo size 1280, 960 font ',24'",
    "set sample 10000",
    "set output 'graph#{n}.png'",
    "set key left top",
    "set format y '%5.1f'",
  ]
end

def g(n)
  exp, y, dy, ddy, dddy = con(n)
  xrange = "[-1.25:1.25]"
  with="with lines lw 2 lc black"
  s = [
    *init_plot(n),
    "set label 1 center at screen  0.5,0.95 '#{exp}'",
    "set xrange #{xrange}",

    "set multiplot layout 2,2 margins 0.1,0.9,0.1,0.9 spacing char 6,3",

    "set yrange [-1.1:1.1]",
    "plot x<-1 ? -1 : (1<x ? 1 : #{y}) #{with} title 'F(x)'",

    "set yrange [-0.1:4]",
    "plot x<-1 ? 0 : (1<x ? 0 : #{dy}) #{with} title '1階微分'",
  ]
  if 1<n
    s+=[
      "set yrange [-8:8]",
      "plot x<-1 ? 0 : (1<x ? 0 : #{ddy}) #{with} title '2階微分'",
      ]
  end
  if 2<n 
    s+=[
      "set yrange [-40:25]",
      "plot x<-1 ? 0 : (1<x ? 0 : #{dddy}) #{with} title '3階微分'",
    ]
  end
  puts %x!echo "#{s.join("\n")}" | gnuplot -!
end

[1,2,3,4,5,6,7].each do |x|
  g(x)
end
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?