「数学」カテゴリーアーカイブ

Wolfram alphaの便利な使い方

オンラインで使える数学用の検索エンジン
Wolfram alpha
を使うといろいろな事が出来ます。それらを紹介します。

積分


integral from 0 to infty sin(x)/x

基本中の基本、積分です。複雑な積分でも定積分でも不定積分でも無限大でも受け付けてくれます。
ただし、計算してくれるかは分かりません。
例えば\(\rm{sinc}\)関数\(\left(\rm{sinc}(x)=\frac{sin(x)}{x}\right)\)のゼロから無限大までの積分は、
20160202-222431_c
のように出力してくれます。\(\pi\)で出力されるのは凄いです。

\(\rm{sinc}\)関数のように有名な関数であれば、

integral sin(x)/x

としたほうがたくさんの情報が得られます。

微分方程式の解


y''(x)+y(x) = sin(x)

と入力すると微分方程式を解いてくれます。また、

y''(x)+y(x) = sin(x), y(0)=1, y'(0)=0

な風にすると、初期条件を与えたうえで解いてくれます。
20160202-221952_c
自分の与えた数式通りに認識しているか、確かめましょう。

行列の対角化


Eigenvectors[{-2,2,4},{-2,4,2},{-2,1,4}]

行列の対角化を行ってくれます。上を入力すると行列
\(
\begin{eqnarray}
\left(
\begin{array}{ccc}
-2 & 2 & 4 \\
-2 & 4 & 2 \\
-2 & 1 & 4
\end{array}
\right)
\end{eqnarray}
\)を対角化し、その固有値と固有ベクトルを出力してくれます。

級数展開


関数の級数展開を出力してくれます。
例えば、

Series[BesselJ(1,x),{x,0,5}]

と入力するとベッセル関数\(J_1(x)\)を\(x\)について、\(x=0\)回りで、\(x^5\)まで展開して出力してくれます。
20160202-223943_c

また、

Series[cos(x),{x,1,10}]

とすると、\(\cos(x)\)を\(x\)について、\(x=1\)回りで、\(x^{10}\)まで展開して出力してくれます。

また、

Series[exp(ix)/x,{x,0,4}]

としても展開してくれます。この場合はローラン(Laurent)展開であり、特異点周りの展開となっています。
上の場合、出力として、
20160202-221030_c
という結果が得られます。
もう一つ、ガンマ関数\(\Gamma(x)\)の\(x=0\)まわりでのローラン展開です。

Series[gamma(x),{x,0,3}]

と入力するとガンマ関数\(\Gamma(x)\)を\(x\)について、\(x=0\)回りで、\(x^3\)まで展開して出力してくれます。
20160202-223636_c
すごい・・・

キャラクターの曲線


様々なキャラクターの曲線のグラフを出力してくれます。
その曲線はWolfram alpha named parametric curvesにまとめられていましたので紹介します。
もしくは、↓をクリックすることで展開されます。

上の他には、wolframで

fictional character curves

animal curves

person curves

と入力して,moreを押していってください。

例を挙げます。

tachikoma like curve

“tachikoma”は、攻殻機動隊に出てくる戦車です。こんな感じです。
tachikoma_wolfram_c

——————–

charmander like curve

“charmander”は、ヒトカゲの事のようです。
charmander_wolfram_c
———————
もしも、上のグラフの数式をコピペしたい場合は、各グラフの下に
plaintext_wolfram
という欄の、”plain text”というところをクリックすれば数式を取得できます。

三角波、のこぎり波、矩形波、その他の数式

正弦波ではない周期関数、三角波、矩形波等々の数式による表現です。
if文は使っていません。
床関数\(floor(x)\)を主に用いています。

これらが唯一の作り方ではなく、最善の作り方でもありません。

非連続になる関数の場合の特定な関数の場合、中間の値を取るようにしています(フーリエ級数の収束より)。


階段関数

——-
f1
z_f1_x_c
——-
f2
z_f2_x_c
——-
fl
z_fl_x_c


のこぎり波

s1
z_s1_x_c
s2
z_s2_x_c
saw
z_saw_x_c


矩形波

k1,k2,k,ka,kb,kk
z_k1_abx_c

z_k2_abx_c

z_k_abx_c

z_ka_abx_c

z_kb_abx_c

z_kk_abx_c


その他

sl,s,spk
tr,se,de,mo
well1,well2

sl
z_sl_abx_c

s
z_s_ax_c
三角波は、\({\rm acos}(\cos(x))\)とも書けます。

spk
z_spk_abx_c

tr
z_tr_abx_c

se
z_se_abx_c

de
z_de_abx_c

mo
z_mo_abx_c

well1
zwell1_abx_c

well2
z_well2_abx_c

well

追記
\(\displaystyle
f(a,b,x) = sgn(abs(2*(x-b)/a)-1e0)
\)
でokです。aは井戸の幅、bは井戸の中心を示します。


まとめ
periodic_functions_c

全てをまとめたgnuplotのコードは以下のものです。

set size ratio -1
set yr[-2:2]
set xr[-5:5]
set samples 101
set grid
set xtics 1
set ytics 1
set mxtics 2
set mytics 2

f1(x)=floor(x)
f2(x)=-floor(-x)-1e0
fl(x)=0.5e0*(floor(x)-floor(-x)-1e0)

s1(x)=x-floor(x)
s2(x)=x+floor(-x)+1e0
saw(x)=x-0.5e0*(floor(x)-floor(-x)-1e0)

k1(a,b,x)=f1((x+a)/(a+b))-f1(x/(a+b))
k2(a,b,x)=f2((x+a)/(a+b))-f2(x/(a+b))
k(a,b,x)=fl((x+a)/(a+b))-fl(x/(a+b))
kk(a,b,x)=2e0*(k(a,b,x)-0.5e0)

ka(a,b,x)=0.5e0*(sgn(k(a,b,x)-0.25e0))+0.5e0
kb(a,b,x)=0.5e0*(sgn(k(a,b,x)-0.75e0))+0.5e0

sl(a,b,x)=kk(a+b,a+b,x+0.5e0*b+a)*k(a,b,x)

s(a,x)=2e0*abs(s2(x/(a*2))-0.5e0)*kk(a*2e0,a*2e0,x+a)
tr(a,b,x)=k(a,b,x)*s(0.5e0*(a+b),x-0.5e0*b)*(1e0+b/a)+sl(b,a,x-b)

spk(a,b,x)=-abs(kk(a,b,x))+1e0

se(a,b,x)=(x-b-(a+b)*fl(x/(a+b)))/a*k(a,b,x)+0.5e0*(k(a,b,x)*(1e0-b/a)+1e0)*spk(a+b,a+b,x)

de(a,b,x)=-se(a,b,x)*2e0*(k2(a+b,a+b,x-0.5e0*b)-0.5e0)
mo(a,b,x)=de(a*0.5e0,b+a*0.5e0,x+a*0.5e0)+de(a*0.5e0,b+a*0.5e0,-(x+a*0.5e0))

well1(a,b,x)=0.5e0*(sgn((abs(2e0*(x-b)/a)-1e0)+ka(1e0,1e0,abs(2e0*(x-b)/a))-0.5e0))+0.5e0
well2(a,b,x)=0.5e0*(sgn((abs(2e0*(x-b)/a)-1e0)+kb(1e0,1e0,abs(2e0*(x-b)/a))-0.5e0))+0.5e0

– 1 = 1 ?

が発端です。間違いであることは直観で分かります。
問題はどこに間違いがあるか?です。

改めて問題を書けば、
\(
\begin{align}
-1&=i\times i \\
&=\sqrt{(-1)}\times\sqrt{(-1)} \\
&=\sqrt{(-1)\times(-1)} \\
&=\sqrt{1} \\
&=1
\end{align}
\)
です。

結論を先に言えば、2行目から3行目にかけての変形がダメです。
定義域の考慮が抜けています。
なぜダメなんでしょうか。詳しく見ていきましょう。

間違いが生じるのはなぜ?


\(i\)を極座標形式で考えます。
すると、
\(
\displaystyle i=0+i=e^{i\frac{\pi}{2}}
\)

ですよね。これは正しいです。
そして次に2行目から3行目を極形式でゆっくりと書いていけば、
\(
\begin{align}
-1&=i\times i \\
&=e^{i\frac{\pi}{2}}\times e^{i\frac{\pi}{2}} \\
&=\left(e^{i\pi}\right)^{1/2} \times\left(e^{i\pi}\right)^{1/2} \cdots \mbox{2行目に対応} \\
&=\left(e^{i2\pi}\right)^{1/2}\cdots \mbox{3行目に対応}
\end{align}
\)
です。

もしも、\(e^{i\theta}\)の\(\theta\)の範囲を勝手に\(0 \le \theta \lt 2\pi\)にしていたら。
この場合、角度\(2\pi\)はこの定義域内に含まれていません。
\(e^{i\theta}\)は\(2\pi\)の周期性を持つはずだ、だから
\(
e^{i2\pi}=e^{i0}=1
\)

であるはずだ、と思ってしまうんです。
これを行ってしまうと、最後の式は、
\(
\begin{align}
\left(e^{i2\pi}\right)^{1/2}&=\left(e^{i0}\right)^{1/2} \\
&=1^{1/2} \\
&=1
\end{align}
\)

となります。

よって間違った答え\(-1=1\)が生まれます。

間違いを起こさないためには


この問題を解決するためには定義域をきちっと示してあげればいいのです。
別の複素数を掛ける場合、通常、\(\theta\)の定義域も考慮しなければなりません。
故に、厳密に書けば、
\(
\displaystyle i=e^{i\frac{\pi}{2}}\ \ \ (0\le\theta \lt 2\pi)
\)

であり、
\(
\begin{align}
-1&=i\times i \\
&=e^{i\frac{\pi}{2}}\times e^{i\frac{\pi}{2}}\ \ \ \ (0\le\theta \lt 4\pi)\\
&=\left(e^{i2\pi}\right)^{1/2}\ \ \ (0\le\theta \lt 4\pi)
\end{align}
\)
なのです。\(e^{i\theta}\)は本来, 周期関数であって多価関数であることを忘れてはいけません。
故に、定義域内に収まっていないからと言って\(2\pi\)を加える必要はないのです。
だから、形はそのままであり、計算は
\(
\begin{align}
&\left(e^{i2\pi}\right)^{1/2}\ \ \ (0\le \theta\lt 4\pi)\\
&=e^{i\pi}\ \ \ (0\le \theta\lt 2\pi)\\
&=-1
\end{align}
\)
だから、やはり\(-1=-1\)なのです。

※上の式\(-1=i\times i\cdots\)は長いので、
\(
\sqrt{(-1)^2}
\)

の値は何ですか?という問いかけでもいいと思います。
この場合、この答えは\(\pm 1\)です。どちらでも正解です。
なぜならば、\(X=\sqrt{(-1)^2}\)とおいて、
\(
\begin{align}
X&=\sqrt{(-1)^2} \\
X^2&=1 \\
&(X+1)(X-1)=0 \\
\end{align}
\)
だからです。


この問題が顕著に表れるのは数値計算です。コンピュータは定義域の考慮はできません。人為的に入れるしか手段は無く、必ず\(2\pi\)の範囲に収まっていなければならないのです。なので、数値計算で複素数を扱う場合、この問題が発生していないか確かめましょう。符号が知らない間に変わっている、軽視いているととんでもない問題になります。

離散フーリエ変換と高速フーリエ変換(fortran90)

こちらのページは後程消去いたします。

以下のページに統合しますので、ご参照ください。
https://slpr.sakura.ne.jp/qp/dft-for-numerical-calculation/

本ページにはミスもあり、上の方が正しいですのでご参照ください。


数値計算で離散フーリエ変換を行う方法です。
言語は
fortran90

intelのMKL(マス・カーネル・ライブラリー)
を用いて離散フーリエ変換を行いたいと考えます。


高速フーリエ変換は離散フーリエ変換を高速に行う手法のことです。

通常の離散フーリエの計算量のオーダーは、\(O(N^2)\)であり、
基数2の高速フーリエの計算量のオーダーは、\(O(Nlog_2 N)\)です。

基数2は良く言われる通常の配列の個数\(2^m\)個のことを表しています。

離散フーリエ変換を行う際には周期境界条件が課されます。
分点の端で周期境界条件が満たされていない場合、それはその点でステップ関数が含まれることを意味します。
依って予期しない高周波成分が出現するので、注意してください。
一番良い判定方法は関数の対数微分が一致するかどうかを判定するのが良いでしょう。

MKLの離散フーリエ変換ルーチンの概要1


MKLの離散フーリエ変換ルーチンは非常に優秀です。
渡す配列サイズによってプログラムが自動的に判断し、最適な手法で離散フーリエ変換を行います。
MKLのマニュアルによると、

注 : DFT関数は任意の長さをサポートしている。
これらのルーチンは、基数 2 だけでなく、基数 3、5、7、11 に対しても高
性能で広範な機能性を提供する。対応する基数の一覧は、使用しているラ
イブラリー・バージョンの「インテルMKLテクニカル・ユーザー・ノー
ト」を参照のこと。

とあります。
高速フーリエ変換の考え方は\(2^m\)の時だけでなく、\(3^m, 5^m, 7^m, 11^m, \cdots\)の時にも同様に考えることができます。
この時、何の整数乗にするかを区別するために “基数” という言葉を使います。
プログラムを作る場合、通常は一番シンプルな基数”2″が選ばれます。

具体的に配列サイズ\(N\)が
\(N= 2^m\)、ただしmは正の整数(配列をAとするとサイズ\(A(0:2^m-1)\))の時に基数2の高速フーリエ変換が行われます。
それ以外の時は通常の離散フーリエ変換が行われているはずです。

余り自信が無いのではっきりとしたことは言えませんが、分点量(2~20000程度まで)に対するMKLのフーリエ変換の計算速度はほとんどわからないくらいでした。特定の基数の時だけ早いんだろうと予想していましたが、そんなことはなく、それだけ優秀であるという事でしょう。

もう6年前に書かれている記事ですので今はどうなっているかわかりませんが、FFTWと計算速度を比較したページがありましたので載せておきます。
一言でいえば、MKLによる離散フーリエ変換はFFTWよりも早いよ、ということです。
インテル謹製の数値演算ライブラリ「MKL」を使ってプログラムを高速化 3ページ -OSDN, (2009)

MKLの離散フーリエ変換ルーチンの概要2


MKLでの1次元、長さnの順方向離散フーリエ変換は、
\(
\displaystyle z_k=\sum_{j=0}^{n-1}w_je^{-i2\pi jk/n}
\)

に従い行われます(MKLリファレンスマニュアル、11-26)。
数値計算の世界では、余分な係数\(\frac{1}{2\pi}\)をなくすため、指数関数の肩に\(2\pi\)が掛けられたものが良く使用されます。

※ここでは、フーリエ変換前の空間を”位置空間“と呼び、フーリエ変換後の空間を”波数空間“と呼ぶことにします。
位置波数ではなく、時間周波数としても同じことなのでどちらでもいいのですが、ここでは、位置波数の呼び方に統一いたします。

DFTexampleNeven
上図のように位置空間上で区間\(L\)の領域で\(N\)個に分割します。
上図はNが偶数の場合です。
MKLの離散フーリエ変換ルーチンはいかなるNに対してもフーリエ変換が可能ですが、奇数の場合、以下のように波数成分の格納が変わります。
周波数格納順

\(
f(x)=\sin(\alpha x)
\)

を離散フーリエ変換した場合、
波数空間でのフーリエ変換後のピークは\(k=\frac{\alpha}{2\pi}\)に現れます。
この時、f(x)のフーリエ変換後の関数\(g(k)=F_{[f(x)]}\)は複素数として格納され、上記の場合では
\(
\displaystyle g(k)=i\left\{\frac{N}{2}\delta(k-\frac{\alpha}{2\pi})+\frac{N}{2}\delta(k+\frac{\alpha}{2\pi})\right\}
\)

となります。ここで\(i\)は虚数単位を表し、\(\delta(k)\)はデルタ関数を表します。
波数空間での最大の周波数値\(f_c\)はナイキストの定理より、
\(
\begin{align}
f_c = \frac{1}{2\Delta}, \Delta=\frac{L}{N}
\end{align}
\)
と表されます。これは、波数空間では\(\pm f_c\)までサンプリング可能であることを示しています。
フーリエ変換後の値\(g(k)\)の分点上の和\(S\)、すなわち
\(
\displaystyle S=\sum_{j=0}^{N-1}g(k_j)
\)

は離散フーリエ変換の性質により、\(S\)は分点の総数\(N\)に等しくなります。

逆離散フーリエ変換によって元の空間に戻る場合、規格化因子\(N\)で割らなければなりません。

MKLの1次元離散フーリエ変換ルーチンのプログラム例


使い方はFFT Code Examples -Intel®Developer Zone を参考としました。

例として
関数
\(
f(x)=\sin(4x)
\)
をフーリエ変換し、元に戻すプログラムを書きましょう。
プログラムの流れとしては、

関数\(f(x)\)の位置空間での書き出し(ファイル”before.d”に書き出し、横軸:位置\(x\), 縦軸\(f(x)\))
↓ 順方向フーリエ変換(規格化因子で割られない)
関数\(g(k)=F_{[f(x)]}\)の書き出し(ファイル”k.d”に書き出し、横軸:\(k/(2\pi)\), 縦軸\(g(k)\))
↓ 逆方向フーリエ変換(規格化因子Nで割られる)
関数\(f(x)=F^{-1}_{[g(k)]}\)の書き出し(ファイル”after.d”に書き出し、横軸:x, 縦軸f(x))
になっています。

コンパイルを行うにあたり重要な事が2点あります。
1, MKLを使う事
2, fftサブルーチンのにinclude文を付け加える事(かも?)

このincludeは、MKLにある離散フーリエ変換ルーチンを使いますよーというサインであり、パスはMKLのバージョンや、インストールした状況によって変わります。
うまく、ファイル”mkl_dfti.f90″を見つけてください。パスを上から辿っていくのがいいと思います。

※2016/02/27時点での最新バージョン(ifort –version で確認できます)
ifort (IFORT) 16.0.2 20160204
において、mkl_dfti.fのデフォルトでの場所は、
“/opt/intel/compilers_and_libraries_2016/linux/mkl/include/mkl_dfti.f90”
です。また、このバージョンだけかどうかわかりませんが、include文が無くてもどうやら動くようです。

プログラムのコンパイルと実行は

$ ifort -mkl main.f90
$ ./a.out

で良いでしょう。

サブルーチンのコンセプトは、
ただ順/逆方向離散フーリエ変換をしてくれるサブルーチンです。速度は追求していません。

速度を追及する書き方をしているのは下のベンチマークの項目です。プログラムの可読性が悪くなるので、中身を知らないと調整が出来ません。
ベンチマークに書いてあるように、サブルーチンdftの中身の

  Status = DftiCreateDescriptor(hand,DFTI_DOUBLE,DFTI_COMPLEX,1,n)
  Status = DftiCommitDescriptor(hand)
  Status = DftiFreeDescriptor(hand)

はFFTを使うための準備で、これは配列の大きさが変わらない限り1度実行するだけで良いのです。
これを排除すると3-4倍ほど速くなります。

上で用いているサブルーチン
dft,dfts,dftfはそれぞれフーリエ変換、フーリエ変換後の空間の値、ソート用のルーチンであることを意味し、その中身は以下のようなものです。例のごとく、MKLのルーチンはそのままでは使いにくいので1クッション挟みます。

ここ↑に書いてあるサブルーチン

  1. dft(N,z,FB)
  2. dftf(N,f,h)
  3. dfts(N,f,z,mf,mz)

を説明します。

  1. dft(N,z,FB)

    このルーチンによって順/逆方向フーリエ変換が行われます。
    引数はそれぞれ

    • (入力) N : integer, データ配列のサイズ
    • (入出力) z : complex(kind(0d0)), データ配列z(0:N-1)
    • (入力) FB : character, 順方向(“forward”)、逆方向(“backward”)の指定

    です。データ配列に上書きして値が返ってきます。
    サイズNのデータ配列z,順方向の離散フーリエ変換をしたい場合は、

    call dft(size(z,1),z,"forward")

    とするのがいいでしょう。
    規格化は順方向の時には行わず、逆方向の時にだけ\(1/N\)を掛けています。

  2. dftf(N,f,h)

    このルーチンは波数空間での横軸の値f(0:N-1)を与えます。
    引数はそれぞれ

    • (入力) N : integer, データ配列のサイズ
    • (出力) f : double precision, 波数空間の横軸の値f(0:N-1)
    • (入力) h : double precision, 位置空間でのサンプルレート(刻み幅)

    です。
    波数fの順番は少しややこしく、
    周波数格納順
    となっています。
    また、ここで出力されるfは波数を\(2\pi\)で割ったものが出力されます。
    具体的には、\(\sin(x)\)をMKLのフーリエ変換を行いグラフを書くとフーリエ変換後の空間(波数空間)で、ピークの位置は\(\pm 0.159115494309\cdots (\sim \frac{1}{2\pi})\)に表れることを意味します。
    また、変換先の空間をグラフで描画したい時に値を小さい順に並べ替えたい必要が出てきたときは並べ替え用のルーチンdftsを使用してください。

  3. dfts(N,f,z,mf,mz)

    このルーチンは変換先の波数空間でグラフを描画したい時に、順番を値を小さい順に並べ替えます。
    引数はそれぞれ

    • (入力) N : integer, データ配列のサイズ
    • (入力) f : double precision, 波数空間の横軸の値f(0:N-1)
    • (入力) z : complex(kind(0d0)), 波数空間でのデータ配列z(0:N-1)
    • (出力) mf : double precision, 並び替えられた波数空間の横軸の値mf(0:N-1)
    • (出力) mz : complex(kind(0d0)), 並び替えられた波数空間でのデータ配列z(0:N-1)

    です。上書きして引数を返す必要性がないだろう、と判断して別の配列に書き出させています。
    もしもメモリが足らないとか、気に食わない人は書き換えてください。

[adsense1]

出力ファイル”before.d”, “k.d”, “after.d”を書いてみます。
位置空間での区間\([-3\pi\sim3\pi]\)で、分点数\(N=288\)で行った場合、
位置空間での波形、この場合は\(\sin(4x)\)が描かれます。
位置空間

波数空間では
\(
\displaystyle g(k)=i\left\{\frac{288}{2}\delta(k-\frac{4}{2\pi})+\frac{288}{2}\delta(k+\frac{4}{2\pi})\right\}
\)

が得られるはずで、実際にプロットして確かめてみると
N288_c
となります。
※Nは2以上のどんな整数でもokです。偶数、奇数、素数であっても。

※実部に現れる小さなピークに関して
原因はよくわかりません。\(N=2^m\)ではない時になんかこれが出てきます。
不安な人は\(N=2^m\)に固定して使ってください。
N=1024で行うとこうなります。
N1024

ベンチマーク

同じ計算機上でのベンチマーク結果を載せます。
コンパイラ:ifort (IFORT) 16.0.2 20160204
MKL:/opt/intel/compilers_and_libraries_2016.2.181
計算スレッド数1
ベンチマークdft_mkl_c

ベンチマーク用プログラムはこちら↓

並列計算時の計算速度

call mkl_set_num_threads(Ncpu)

を用いてFFTを並列計算で行った時、計算時間のCPU個数(8コア16スレッド)と配列の要素数依存性を調べます。
フーリエ変換するのはガウス関数とします。
結果はこのようになりました。

複数の線は試行回数を表しています。プログラムは全く同じで、実行した時刻だけが違います。
”計算1回”とは(順方向→逆方向フーリエ変換)の1セットを”1回”としています。
8コア16スレッドの環境で実行したので、物理的なCPUの個数8個で計算速度はおおよそ打ち止めになっています。

計算に用いたコードはこちら

2次元のフーリエ変換について


ルーチン等々はほぼ同じです。
下のプログラムは
\(
f(x,y)=\sin(4x)+\sin(3y)
\)
をフーリエ変換するものです。
フーリエ変換実行前の,位置空間での関数の形(“before.d”, x軸:\(x\), y軸:\(y\), z軸\(f(x,y)\)),
フーリエ変換し、波数空間で見た関数の形(“k.d”, x軸:\(k_x/(2\pi)\), y軸:\(k_y/(2\pi)\), z軸\(Imag\{g(k_x,k_y)\}\)),
逆フーリエ変換した後の関数の形(“after.d”, x軸:\(x\), y軸:\(y\), z軸\(f(x,y)\))は
before_k_after_2D_c
となります。
波数空間で\(k_x\)だけを見た時,\(k_x=0\)にあたかもピークが見えます。
これはy成分があるために出てくるもので、証明は以下のようにできます。
関数\(f(x,y)\)は
\(
\begin{align}
f(x,y)&=\sin(4x)+\sin(3y)\\
&=i\left\{-\frac{1}{2}e^{i4x}+\frac{1}{2}e^{-i4x}-\frac{1}{2}e^{i3y}+\frac{1}{2}e^{-i3y}\right\}
\end{align}
\)
なので、連続のフーリエ変換を考えるとすると、
\(
\begin{align}
&\int f(x,y)e^{-ik_x x}e^{-ik_y y}dxdy\\
&=i\left\{-\frac{1}{2}\int e^{i(4-k_x)x} dx \int e^{-ik_y y} dy+\frac{1}{2}\int e^{-i(4+k_x)x} dx \int e^{-ik_y y} dy \right. \\
&\;\;\;\;\;\;\;\;\;\;\;\; \left.-\frac{1}{2}\int e^{-ik_x x} dx \int e^{i(3-k_y)y} dy+\frac{1}{2}\int e^{-ik_x x} dx \int e^{-i(3+k_y)y} dy\right\} \\
&=i\pi\left\{-\delta(k_x-4)\delta(k_y)+\delta(k_x+4)\delta(k_y)-\delta(k_x)\delta(k_y-3)+\delta(k_x)\delta(k_y+3)\right\}
\end{align}
\)
となります。なので、フーリエ変換によってピークが出てくる位置というのは、
\((k_x,k_y)=(4,0), (-4,0), (0,3), (0,-3)\)
の4点となります。なので、y成分がいるために\(k_x=0\)にあたかもピークがあるように見えてしまうんですね。(証明終わり)

2次元のフーリエ変換はそのまま2次元データの配列を渡すことは出来ないため、一度、1次元配列への変換(サブルーチンswap1d2d)を呼び出しています。本来は2次元データと1次元データの間の関係ではEquivalenceを使うべきなのですが、何をしているのかの(個人的な)見やすさを考慮してわざわざ別の配列を用意して格納しています。

include "/opt/intel/mkl/include/mkl_dfti.f90"
program main
  implicit none
  integer::i,j,Nx,Ny
  double precision,allocatable::x(:),kx(:),mkx(:)
  double precision,allocatable::y(:),ky(:),mky(:)
  double precision::hx,xmin,xmax
  double precision::hy,ymin,ymax
  complex(kind(0d0)),allocatable::z(:,:),mz(:,:)

double precision,parameter::pi=dacos(-1.d0)
  complex(kind(0d0))::func
  external::func

Nx=100; Ny=100
  allocate(x(0:Nx-1),kx(0:Nx-1),mkx(0:Nx-1))
  x=0d0; kx=0d0; mkx=0d0
  allocate(y(0:Ny-1),ky(0:Ny-1),mky(0:Ny-1))
  y=0d0; ky=0d0; mky=0d0
  allocate(z(0:Nx-1,0:Ny-1),mz(0:Nx-1,0:Ny-1))
  z=dcmplx(0d0,0d0); mz=dcmplx(0d0,0d0)

xmin=-3d0<em>pi
  xmax=3d0</em>pi
  hx=(xmax-xmin)/dble(Nx)
  ymin=-3d0<em>pi
  ymax=3d0</em>pi
  hy=(ymax-ymin)/dble(Ny)

do i=0,Nx-1
     x(i)=xmin+dble(i)<em>hx
  enddo
  do j=0,Ny-1
     y(j)=ymin+dble(j)</em>hy
  enddo
  do i=0,Nx-1
     do j=0,Ny-1
        z(i,j)=func(x(i),y(j))<br />
     enddo
  enddo

open(21,file="before.d")
  do i=0,Nx-1
     do j=0,Ny-1
        write(21,'(4e20.10e3)')x(i),y(j),dble(z(i,j)),dimag(z(i,j))
     enddo
     write(21,*)
  enddo
  close(21)

call dftf(Nx,kx,hx); call dftf(Ny,ky,hy) ! get frequency

call dft2d(Nx,Ny,z,"forward") ! forward dft

call dfts2d(Nx,Ny,kx,ky,z,mkx,mky,mz) !sort frequency
  open(21,file="k.d")
  do i=0,Nx-1
     do j=0,Ny-1
        write(21,'(4e20.10e3)')mkx(i),mky(j),dble(mz(i,j)),dimag(mz(i,j))
     enddo
     write(21,*)
  enddo
  close(21)

call dft2d(Nx,Ny,z,"backward") ! backward dft
  open(21,file="after.d")
  do i=0,Nx-1
     do j=0,Ny-1
        write(21,'(4e20.10e3)')x(i),y(j),dble(z(i,j)),dimag(z(i,j))
     enddo
     write(21,*)
  enddo
  close(21)

stop
end program main

function func(x,y)
  implicit none
  double precision::x,y
  complex(kind(0d0))::func

func=dcmplx(dsin(4d0<em>y)+dsin(3d0</em>x),0d0)

return
end function func

がメインのプログラムで、以下が上で呼び出しているサブルーチンの中身です。

subroutine dftf(N,f,h)
  implicit none
  integer,intent(in)::N
  double precision,intent(in)::h
  double precision,intent(out)::f(0:N-1)

integer::i
  double precision::mf(0:N-1)

if(mod(N,2).eq.0)then
     do i=0,N-1
        mf(i)=(dble(i+1)-dble(N)<em>0.5d0)/(dble(N)</em>h)
     enddo

<pre><code> do i=0,N-1
    if(i.le.N/2-2)then
       f(i+N/2+1)=mf(i)
    else
       f(i-N/2+1)=mf(i)
    endif
 enddo

else
do i=0,N-1
mf(i)=(dble(i)-dble(N-1)0.5d0)/(dble(N)h)
enddo


 do i=0,N-1
    if(i.le.(N-1)/2-1)then
       f(i+(N-1)/2+1)=mf(i)
    else
       f(i-(N-1)/2)=mf(i)
    endif
 enddo

endif

return
end subroutine dftf
!——————————–
subroutine dft2d(Nx,Ny,z,FB)
!sikinote
!date : 2015/12/29
!developer : sikino
!CC BY 4.0 (http://creativecommons.org/licenses/by/4.0/deed.ja)
use MKL_DFTI
implicit none
integer,intent(in)::Nx,Ny
complex(kind(0d0)),intent(inout)::z(0:Nx-1,0:Ny-1)
character(*),intent(in)::FB

complex(kind(0d0))::z1(0:Nx*Ny-1)
integer::Status
TYPE(DFTI_DESCRIPTOR),POINTER::hand
integer::id(1:2)

!DFT : Discrete Fourier Transform
!
!n –> number of data.
!z(i,j) –> value of data at x and y
!FB –> “forward” : Forward DFT
! –> “backward” : Backward DFT

call swap1d2d(nx,ny,z,z1,1)
id(1)=nx; id(2)=ny

Status = DftiCreateDescriptor(hand,DFTI_DOUBLE,DFTI_COMPLEX,2,id)
Status = DftiCommitDescriptor(hand)

if(trim(FB).eq.”forward”)then
Status = DftiComputeForward(hand,z1)
elseif(trim(FB).eq.”backward”)then
Status = DftiComputeBackward(hand,z1)
else
write(6,*)”DFT string different”
stop
endif
Status = DftiFreeDescriptor(hand)

call swap1d2d(nx,ny,z,z1,-1)

if(trim(FB).eq.”backward”)then
z=z/dble(nx*ny)
end if

return
end subroutine dft2d
!———————————————
subroutine swap1d2d(nx,ny,z2,z1,isign)
!change array matrix between 1D and 2D
! if isign == 1 –> from 2D to 1D
! if isign == -1 –> from 1D to 2D
implicit none
integer,intent(in)::nx,ny,isign
complex(kind(0d0)),dimension(0:nx-1,0:ny-1),intent(inout)::z2
complex(kind(0d0)),dimension(0:nx*ny-1),intent(inout)::z1
integer::j1,j2,k

if(isign.eq.1)then
do j2=0,ny-1
do j1=0,nx-1
k=j2nx+j1
z1(k)=z2(j1,j2)
enddo
enddo
elseif(isign.eq.-1)then
do j2=0,ny-1
do j1=0,nx-1
k=j2
nx+j1
z2(j1,j2)=z1(k)
enddo
enddo
endif

return
end subroutine swap1d2d
!—————————-
subroutine dfts2d(Nx,Ny,fx,fy,z,mfx,mfy,mz)
implicit none
integer,intent(in)::Nx,Ny
double precision,intent(in)::fx(0:Nx-1),fy(0:Ny-1)
complex(kind(0d0))::z(0:Nx-1,0:Ny-1)
double precision,intent(out)::mfx(0:Nx-1),mfy(0:Ny-1)
complex(kind(0d0)),intent(out)::mz(0:Nx-1,0:Ny-1)
complex(kind(0d0))::mmz(0:Nx-1,0:Ny-1)
integer::i,j,k,l

if(mod(Ny,2).eq.0)then
do i=0,Ny-1
if(i.le.Ny/2)then
j=i+Ny/2-1
mfy(j)=fy(i)
mz(0:Nx-1,j)=z(0:Nx-1,i)
else
j=i-Ny/2-1
mfy(j)=fy(i)
mz(0:Nx-1,j)=z(0:Nx-1,i)
endif
enddo
else
do i=0,Ny-1
if(i.le.(Ny-1)/2)then
j=i+(Ny-1)/2
mfy(j)=fy(i)
mz(0:Nx-1,j)=z(0:Nx-1,i)
else
j=i-(Ny-1)/2-1
mfy(j)=fy(i)
mz(0:Nx-1,j)=z(0:Nx-1,i)
endif
enddo
endif

mmz=mz

if(mod(Nx,2).eq.0)then
do k=0,Nx-1
if(k.le.Nx/2)then
l=k+Nx/2-1
mfx(l)=fx(k)
mz(l,0:Ny-1)=mmz(k,0:Ny-1)
else
l=k-Nx/2-1
mfx(l)=fx(k)
mz(l,0:Ny-1)=mmz(k,0:Ny-1)
endif
enddo
else
do k=0,Nx-1
if(k.le.(Nx-1)/2)then
l=k+(Nx-1)/2
mfx(l)=fx(k)
mz(l,0:Ny-1)=mmz(k,0:Ny-1)
else
l=k-(Nx-1)/2-1
mfx(l)=fx(k)
mz(l,0:Ny-1)=mmz(k,0:Ny-1)
endif
enddo
endif

return
end subroutine dfts2d

昔のMKLの離散フーリエ変換ルーチンについて


MKLは汎用性を高くするために仕様変更が行われ、
以前は

call zfft1d(...)

とするだけで順/逆方向フーリエ変換は使えていたようですが、いつからか(2004~2006年辺り?)廃止され、使えなくなっています。

間違い、勘違いがあると思います。
いかなる問題が発生しても私は責任を一切負いません。
それを念頭に置いたうえ使用してください。

参考文献


インテル®マス・カーネル・ライブラリーリファレンスマニュアル(2006年)
ニューメリカルレシピinC

[adsense2]

ラグランジュの未定乗数法

忘れやすいラグランジュの未定乗数法のメモです。
一通り学んだ人が使い方を思い出す、という状況を想定しています。

変数が独立な場合


3変数x,y,zが独立(xが変化してもy,zは変化しない、\(\vec{x}\cdot\vec{y}=0\) (y,zも同様))で、その関数\(f(x,y,z)\)の極値は
\(
\displaystyle \frac{\partial f}{\partial x}=0,\ \ \frac{\partial f}{\partial y}=0,\ \ \frac{\partial f}{\partial z}=0 \ \ \cdots (1)
\)

を連立させて解くことで得られます。

変数が独立ではない(従属な)場合


変数x,y,zの間に関係式
\(
g(x,y,z)=c, \ \mbox{$c$は定数} \ \ \cdots (2)
\)

という条件がある場合、

  1. 式(2)が(例えば)zについて解けるならば、\(f(x,y,z(x,y))\)として2変数\(x,y\)の極値問題として解ける。
  2. \(z=z(x,y)\)の形に書けない場合 →ラグランジュの未定乗数法を使う
  1.  の場合、”関数\(f(x,y,z)\)が極値をとる”、とは
    \(
    \displaystyle df=\frac{\partial f}{\partial x}dx+\frac{\partial f}{\partial y}dy+\frac{\partial f}{\partial z}dz=0 \ \ \cdots (3)
    \)

    である点(極値点)である。
    →極値点から\(x,y,z\)を任意の微小量\(dx,dy,dz\)だけ変化させても関数\(f(x,y,z)\)の変化分である\(df\)は1次の範囲でゼロです。
    故に任意の\(dx,dy,dz\)について(3)が成立する、よって(1)が導かれることになります。
  2.  の場合、\(dx,dy,dz\)は独立に選ぶことができません。その取り方は条件(2)に従います。
    極値点まわりで(2)が成立しているならば、
    \(
    g(x+dx,y+dy,z+dz)=g(x,y,z)\ \ \cdots (4)
    \)

    を満たすような\(dx,dy,dz\)の変化しか許されないことになります。この条件から\(dx,dy,dz\)の動かし方は、
    \(
    \displaystyle \frac{\partial g}{\partial x}dx+\frac{\partial g}{\partial y}dy+\frac{\partial g}{\partial z}dz=0 \ \ \cdots (5)
    \)

    に制限されます。
    条件(5)を\(dz\)について変形すると、
    \(
    \displaystyle dz=-\frac{\frac{\partial g}{\partial x}dx+\frac{\partial g}{\partial y}dy}{\frac{\partial g}{\partial z}} \ \ \cdots (6)
    \)

    となります。式(3)へ式(6)を代入すると

    \(
    \begin{align}
    \displaystyle df &=\left( \frac{\partial f}{\partial x}-\frac{\frac{\partial g}{\partial x}}{\frac{\partial g}{\partial z}}\frac{\partial f}{\partial z}\right)dx
    +\left( \frac{\partial f}{\partial y}-\frac{\frac{\partial g}{\partial y}}{\frac{\partial g}{\partial z}}\frac{\partial f}{\partial z}\right)dy=0 \\
    &=\left( \frac{\partial f}{\partial x}-\lambda\frac{\partial g}{\partial x}\right)dx
    +\left( \frac{\partial f}{\partial y}-\lambda\frac{\partial g}{\partial y}\right)dy=0 \ \ \cdots (7)
    \end{align}
    \)

    ここで
    \(
    \displaystyle \lambda=\frac{\frac{\partial f}{\partial z}}{\frac{\partial g}{\partial z}}\ \ \cdots (8)
    \)

    と置きました。この\(\lambda\)はラグランジュの未定乗数と呼ばれます。
    未定乗数という所以は、この\(\lambda\)をあらわに決める必要はなく、決まらない定数のままでも極値点を求めることができるという事を表しています。
    今、\(dx,dy\)は独立であるので(※1)、その係数は\(0\)になるはずです。故に、式(7)と式(8)より、

    \(
    \begin{eqnarray}
    \left\{
    \begin{aligned}
    \frac{\partial f}{\partial x}-\lambda\frac{\partial g}{\partial x}&=0 \\
    \frac{\partial f}{\partial y}-\lambda\frac{\partial g}{\partial y}&=0 \\
    \frac{\partial f}{\partial z}-\lambda\frac{\partial g}{\partial z}&=0
    \end{aligned}
    \right.
    \end{eqnarray}
    \)

    が導けます。変形をすれば、条件式(2)も含めて、

    \(
    \begin{eqnarray}
    \left\{
    \begin{aligned}
    \frac{\partial}{\partial x}\left(f-\lambda g\right)&=0 \\
    \frac{\partial}{\partial y}\left(f-\lambda g\right)&=0 \\
    \frac{\partial}{\partial z}\left(f-\lambda g\right)&=0 \\
    g(x,y,z)&=c
    \end{aligned}
    \right.
    \end{eqnarray}
    \ \ \ \cdots (9)
    \)

    と書くことができます。未知の変数は\(x,y,z,\lambda\)の4つで、方程式は4本なので解くことができます。
    この式が言うことは、束縛条件\(g(x,y,z)=c\)があった場合、関数\(f-\lambda g\)を考えて、その極値を求めればよいことを表しています。

[adsense1]

まとめ


条件\(g(x,y,z)=c\)がある関数f(x,y,z)の極値問題は、
\(
\tilde{f}=f-\lambda g \ \ \cdots (10)
\)

という新たな関数\(\tilde{f}\)を考えるとx,y,zが独立に変化するものと考えて\(\tilde{f}(x,y,z)\)の極値問題を考えればよい、となります。

例題 ~楕円に内接する長方形の面積の最大値を求める~


楕円の方程式は
\(
\displaystyle \frac{x^2}{a^2}+\frac{y^2}{b^2}=1
\)

であり、この方程式の許す\(x,y\)を満たしながら、内接する長方形の面積
\(S(x,y)=4xy\)
を最大にする\(x,y\)を求めます。

楕円面積

これは、関数\(f(x,y)=4xy\)の極値を\(\displaystyle g(x,y)=\frac{x^2}{a^2}+\frac{y^2}{b^2}=1\)という条件下で解く、という意味になります。
まず、\(\tilde{S}\)を式(10)より求めます。
\(
\begin{align}
\tilde{S}&=S-\lambda g \\
&=4xy-\lambda\left(\frac{x^2}{a^2}+\frac{y^2}{b^2}\right)
\end{align}
\)
式(9)より、
\(
\begin{eqnarray}
\left\{
\begin{aligned}
\frac{\partial \tilde{S}}{\partial x}&=0 \\
\frac{\partial \tilde{S}}{\partial y}&=0
\end{aligned}
\right.
\end{eqnarray}
\)
を考えればいいので、
\(
\begin{eqnarray}
\left\{
\begin{aligned}
\frac{\partial \tilde{S}}{\partial x}=4y-\lambda \frac{2x}{a^2}&=0 \ \ \cdots (i)\\
\frac{\partial \tilde{S}}{\partial y}=4x-\lambda \frac{2y}{b^2}&=0 \ \ \cdots (ii)\\
\frac{x^2}{a^2}+\frac{y^2}{b^2}&=1 \ \ \cdots (iii)
\end{aligned}
\right.
\end{eqnarray}
\)
を満たす未知の変数\(x,y,\lambda\)が\(S\)の極値となっています。
(i)と(ii)より\(\lambda\)を消去すると
\(
\begin{align}
(i) &\rightarrow \lambda=\frac{2y}{x}a^2 \\
(ii) &\rightarrow 4x-\left(\frac{2y}{x}a^2\right)\frac{2y}{b^2}=0
\end{align}
\)
なので
\(
\displaystyle \frac{x^2}{a^2}=\frac{y^2}{b^2}
\)

(iii)に代入して
\(
\displaystyle 2\frac{x^2}{a^2}=1
\)

より
\(\displaystyle x=\pm\frac{a}{\sqrt{2}},\ \ y=\pm\frac{b}{\sqrt{2}}\)
のとき\(S(x,y)\)が極値を取ることがわかります。

ちなみに、この時の長方形の面積\(S_{max}\)は\(S_{max}=2ab\)であり、
これは楕円の面積\(S=\pi ab\)の\(\frac{2ab}{\pi ab}\sim 0.64\)となり、約64%を占めていることになります。

[adsense2]

※1
x,yが独立であるのは、x,y,zをつなぐ1本の条件式\(g(x,y,z)=c\)によって消え得る変数は1つだけであるためです。

参考


小野寺 嘉孝著 『物理のための応用数学』裳華房(1988)p.6~10

縮約

縮約はアインシュタインが一般相対性理論を説明するために最初に導入した数式のお約束です。
これが考え出されたのはアインシュタインが

和記号を書くのに飽きた

からであると予想されます。
このお約束なんですが,大学の物理科ではあまり触れられない癖に教授たちは常識のように語るので,
身につけておいて損はないですね。

さて,あるテンソル量\(A_{i}\)と\(B_{i}\)があったとしましょう。
この二つの縮約をとるということは,同じテンソル同士の和を取る,という意味になります。
アインシュタインの記法に則れば次のように書きます。

\(
A_{i}B^{i}=\sum_{i}{A_{i}B_{i}}
\)

ようするに同じ添え字が右下と右上に来たら和をとりましょうという約束です。
上の式は内積を表していることがわかります。
これを使うと外積の\(i\)成分も次のようになります。

\(
(A \times B)_i= \epsilon_{i,j,k}A^{j}B^{k}
\)
\(
\epsilon_{i,j,k}= 1 ~~ {\rm at} ~~ i,j,k=(1,2,3),(2,3,1),(3,1,2)
\)
\(
\epsilon_{i,j,k}= -1 ~~ {\rm at} ~~ i,j,k=(1,3,2),(3,2,1),(2,1,3)
\)
\(
\epsilon_{i,j,k}= 0 ~~ {\rm at} ~~ i,j,k=(otherwise)
\)

この\(\epsilon_{i,j,k}\)をレヴィチビタの完全反対称テンソルといいます。