2019年5月13日月曜日

Piecewise関数

スリンキーの自由落下の問題で,独立変数の条件によって異る関数形を当てはめたい場面があった。具体的には,自然長より長い場合と短い場合でバネ定数が異る非対称バネのモデルを使いたかった。

このような場合,MathematicaではPiecewise関数を使うことができる。
Piecewise[{{式1,条件1}, {式2,条件2}, …}]
制約条件(条件i)によって定義された区域に値(式i)を持つ区分関数を表す。
[例1]Plot[Piecewise[{{x^2,x<0},{x,x>0}}],{x,-2,2}]
[例2]D[Piecewise[{{x^2,x<0},{x,x>0}}],x]

Juliaを調べてみるとPiecewiseに相当するものはない。組み込み関数のsign(x) を用いて,変数xの正負の符号をとりだし,これからHeaviside関数をつくり,さらにInterval関数を作るという手順になる。あとは適当な関数にInterval関数を掛ければよいだけだ。
function heaviside(t)
   0.5 * (sign(t) + 1)
end
function interval(t, a, b)
   heaviside(t-a) - heaviside(t-b)
end

ところで,StackOverflowのDefine Piecewise Functions in Julia には上記の方法に加えて,より一般的なPiecewise関数をユーザ定義した例が示されている。確かめてみると,若干の修正が必要で,Vector{Function}(n)に undef を加えればよいことがわかった。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function piecewise(x::Symbol,c::Expr,f::Expr)
  n=length(f.args)
  @assert n==length(c.args)
  @assert c.head==:vect
  @assert f.head==:vect
  vf=Vector{Function}(undef,n)
  for i in 1:n
    vf[i]=@eval $x->$(f.args[i])
  end
  return @eval ($x)->($(vf)[findfirst($c)])($x) 
end
pf=piecewise(:x,:([x>0, x==0, x<0]),:([2*x,-1,-x]))
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pf(1) # => 2
pf(-2) # => 2
pf(0) # => -1


0 件のコメント: