ぼんさい塾

ぼんさいノートと補遺に関する素材や注釈です.ミスが多いので初稿から1週間を経た重要な修正のみ最終更新日を残しています.

フーリエ変換

2010-08-31 17:45:17 | 暮らし
記事一覧
copied from [1]

「フーリエ変換」の Google 検索では

[1] フーリエ変換 - Wikipedia
    http://ja.wikipedia.org/wiki/%E3%83%95%E3%83%BC%E3%83%AA%E3%82%A8%E5%A4%89%E6%8F%9B
[2] フーリエ変換の第一歩 [物理のかぎしっぽ]
    http://hooktail.sub.jp/fourieralysis/Fourier/

をはじめ多くの解説がありますが,少々長い蛇足を付記します.

蛇足: 線形時不変なシステム T に x(t) を入力したときの出力は,インパルス応答 h(t) を用いて

  (Tx)(t) = ∫R h(u)x(t-u)du

と表現できます.ここで x(t) = e-iωt とおくと

  (Tx)(t) = e-iωtR h(u)e-iωudu= H(ω) x(t)

すなわち,e-iωt は T の固有関数であることが分かります.固有値 H(ω) がh(t) のフーリエ変換で,伝達関数とよばれています.

[1],[2]でも説明されているように h(t) のフーリエ変換を

  (F h)(ν) = ∫R h(t)e-i2πνtdt

で定義する方が,公式がスマートになります(多変数でも同様).例えば

  (F2 h)(t) = h(-t)

したがって F4 h = h です.F comb = comb, F rect = sinc 等については別途説明します.

発展: 両側ラプラス変換 ∫R h(t)e-stdt については

[3] ラプラス変換(ディジタル信号処理)
    http://ufcpp.net/study/dsp/laplace.html

で説明されていますが,筆記体の「L」が文字化けで「□」になるかもしれません.


Cの文法 (9)

2010-08-28 22:40:46 | 暮らし
「まず覚えるCの文法」校正中)

P35: ポインタの配列
//-------------------------------------
#include <stdio.h>
char s[]="Hello.";
int main(void){
  char *p[2];
  p[0]=s; p[1]="How are you?";
  printf("%s %s\n", p[0], p[1]);
  printf("p[0]==%d, p[1]==%d\n",
         p[0], p[1]);
  printf("p[1][4]==%c\n", p[1][4]);
  return 0;
}
//-------------------------------------

P36: ポインタへのポインタ
//-------------------------------------
#include <stdio.h>
char s[]="Hello.";
int main(void){
  char *p[2], **q;
  p[0]=s; p[1]="How are you?";
  q=p; printf("q[1][4]==%c\n", q[1][4]);
  return 0;
}
//-------------------------------------

P37: 紛らわしい例
//-------------------------------------
#include <stdio.h>
int m[2][3]={{5},{6,7}};
int main(void){
  int *p[2]={m[1], m[0]}, (*q)[3]=m[1];
  printf("p[0][1]==%d\n", p[0][1]);
  printf("q[0][1]==%d\n", q[0][1]);
  printf("m[1]==%d\n", m[1]);
  printf("m+1==%d\n", m+1);
  printf("&m[1]==%d\n", &m[1]);
  printf("m==%d\n", m);
  return 0;
}
//-------------------------------------

P35 のプログラムの「char *p[2];」はポインタの配列の
宣言で,p[0], p[1]を独立に文字列へのポインタとして
使うことができます.

・「[]」の方が「*」よりも「p」との演算の優先度が高い
  ので「char *p[2];」は「char *(p[2]);」を意味します
  [#37].Cの表現ではありませんが,便宜的に
      char     *(p[2]);
      (char*)     p[2]; p[2] の配列要素は(char*)
      (char*)[2]     p; p は(char*)の配列
  と解釈することができます.
・「p[1]="How are you?";」を実行すると,メモリ内に
  適当な領域を確保して文字列 "How are you?" を格納し
  その先頭アドレスを p[1] に設定します.
・p[1][4] は アドレス p[1]+4 のデータ *(p[1]+4) を
  表わしています.

P36 のプログラムの「char **q;」はポインタへのポインタ
の宣言です.「q=p;」の実行により p の代わりに q を使
うことができます. 

・このプログラムでは q の有効性を見出せませんが,関数
  の引数ではポインタへのポインタが必要になります.上記
  の「 p の代わりに q を使える」というのがポイントです.

Cには多数の演算子が定義されてどの教科書・参考書にも
演算子の優先度と結合規則の表が示されています(@37).
「x < (y & 7)」の括弧は除去できない等,間違いやすい
関係もあるので,慣れるまでは栞を挟んで適宜参照できる
ようにしましょう.
「char *p[2];」の意味は P35 で述べましたが,P37 のプロ
グラムで「char (*p)[2];」との違いを見てください.

・p は整数へのポインタの配列,q は整数の配列へのポイン
  タです.
・m+1==&m[1] ですが, &m[1]==m[1] として扱われます. 
・つねに p[i][k]==(p[i])[k], p[0][k]==(*p)[k].
・q[1] は存在しないので「q[1]=m;」はエラーになりますが
 「q[i][k]=0;」は i!=0 でもエラーにならず,例えば
    printf("q[-1][0]==%d\n", q[-1][0]);
  を実行すると m[0][0] の値が出力されます.p も同様です.

参考資料:自分で調べるCのポインタ


属性の可視化

2010-08-27 17:06:48 | 暮らし
記事一覧

広く用いられている可視化の例として

[1] スペクトログラム分析 by w3voice
    http://w3voice.jp/specgram/
[2] CFDによる自動車エンジンルーム内の熱環境予測 - 流体工学部門:活動 ...
    http://www.jsme-fed.org/newsletters/2007_4/no2.html

のようなスペクトログラムや温度分布の表示がありますが,拙文に関する「可視化 数式」の Google 検索では数式のグラフ表示を行う資料しか見つかりませんでしたので,以下に拙文の一部を紹介します.

数式中の( )は演算順序だけでなく,関数の引数やベクトルの表現にも使われます.このため,文脈から切り離された v(t+⊿t) だけでは v と (t+⊿t) の積か v(・) のt+⊿t における値か分かりません.数学の苦手な学生の中には

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

のように『分配法則』もどきの変形をすることもあるらしいので,括弧の種類を色で明示して誤解の軽減を図ることが考えられます.上図の例では,関数用の括弧や独立変数・従属変数を赤で表示して注意を喚起しています.

蛇足: プログラムの統合開発環境ではソース・プログラムの表示で色やフォントを使い分けています.初心者でなくてもこのような表示を好むからでしょう.


Cの文法 (8)

2010-08-25 19:20:27 | 暮らし
「まず覚えるCの文法」校正中)

P32: ポインタ
//-------------------------------------
#include <stdio.h>
char s[]="Hello.";
int main(void){
  char *p; int k=0, *q=&k;
  for(p=s; *p!='\0'; p++){
    printf("%c", *p); k++;
  }
  printf("  k==%d, *q==%d\n", k, *q);
  return 0;
}
//-------------------------------------

P32A: 配列もどきの表現
//-------------------------------------
#include <stdio.h>
char s[]="abcdefgh";
int main(void){
  char *p=s+2; int i, *q=s;
  for(i=-1; i<3; i++){
    printf("i=%2d, p[i]==%c,==%x, "
           "*(p+i)==%x\n",
           i, p[i], p[i], *(p+i));   
  }
  printf("q[0]==%x\n", q[0]);
  printf("q[1]==%x\n", q[1]);
  return 0;
}
//-------------------------------------

P34: 2次元の配列
//-------------------------------------
#include <stdio.h>
int m[2][3]={{5},{6,7}};
char s[2][8]={"abcde", "xyz"};
int main(void){
  int i,k;
  for(i=0; i<2; i++){
    for(k=0; k<3; k++){
      printf("m[%d][%d]==%d, ",
             i, k, m[i][k]);
    }
    printf("s[%d]==%s\n", i, s[i]);
  }
  return 0;
}
//-------------------------------------

P34A: アドレス
//-------------------------------------
#include <stdio.h>
int m[2][3]={{5},{6,7}};
int main(void){
  int i, k, *p=&m[0][2];
  for(i=0; i<2; i++){
    for(k=0; k<3; k++){
      printf("&m[%d][%d]==%d, ",
             i, k, &m[i][k]);
    }
    printf("\nm[i]==%d\n", m[i]);
  }
  printf("m==%d\n", m);
  printf("p[1]==%d, p+1==%d\n",
         p[1], p+1);
  return 0;
}
//-------------------------------------

変数の先頭アドレスを記憶する変数をポインタといいます
(#32).P32 のプログラムの「char *p;」はポインタ p の
値を先頭アドレスとする 1 バイト(sizeof(char)==1)の
領域を char 型の変数として使うことを宣言します.
  Cではこの領域が char 型の変数用に確保されているか
否かはチェックしません.p の値がおかしいと *p にデー
タを書き込むことにより,機械語命令を破壊する可能性が
あります.

・Cのプログラムは関数の集まりですが,引数はすべて値
  呼びのため,ポインタが多用されます.
・ポインタは先頭アドレスのみを記憶し,データの型は
  コンパイルラが所定の場所に記憶します. 
・「*p」の「*」を間接演算子,「&k」の「&」をアドレス
  演算子といいます.*(&k)==k; q==&(*q) です.
・配列要素 m[i] も *(m+i) と表現できます.

ポインタが指すデータの構造はポインタの宣言のみで決まり
ます.このことを P32A のプログラムで確認してください.

  p[i] は常に(i<0 でも)*(p+i) と等価

ですが,p[i] を配列要素とする配列は存在しません(#31&).

・printf() の書式「%x」は16進表示の指定です.
・宣言「int i, *q=s;」によって(*q ではなく)q が s に
  初期化され
    q[0]==64636261
    q[1]==68676665
  と表示されます.これは
    q[0]=(((s[3]*16)+s[2])*16+s[1])*16+s[0]
    q[1]=(((s[7]*16)+s[6])*16+s[5])*16+s[4]
  を意味します.
・「int *q;」を「int* q;」と同じですが,「int* p, q;」は
  「int *p, *q;」とは異なります(#32&).

Cでは2行3列の int 型変数の配列を「int m[2][3];」の
ように宣言します.文字列の配列は char 型の2次元配列と
して宣言します.P34 のプログラムをコピー,実行して配列
要素がどのように初期化されるか調べてください.

・m,s は外部変数なので初期値を指定していない要素は 0
  に初期化されます(#30).
・m[i][k],s[i][k] の先頭アドレスは &m[i][k],&s[i][k]
  です.また,m[i],s[i],m,s はアドレスで
    m[i]==&m[i][0], s[i]==&s[i][0], m==m[0], s==s[0]
・int m[][3]={{5},{6,7}}; char s[][8]={"abcde", "xyz"};
  は
    int m[][2]={{5},{6,7}};
    char s[][8]={"abcde", "xyz"};
  と等価です.

配列要素の先頭アドレスを P34A のプログラムで調べること
ができます.実行例は
  
  &m[0][0]==4235560, &m[0][1]==4235564, &m[0][2]==4235568, 
  m[i]==4235560
  &m[1][0]==4235572, &m[1][1]==4235576, &m[1][2]==4235580, 
  m[i]==4235572
  m==4235560
  p[1]==6, p+1==4235572

・プログラムを変更することにより
    &m[0][2]==m[0]+2 
    &m[0][5]==m[0]+5, m[0]+5==&m[1][2]
  等も確認できます.


Cの文法 (7)

2010-08-21 18:18:57 | 暮らし
「まず覚えるCの文法」校正中)

P31: 配列
//-------------------------------------
#include <stdio.h>
int m[8]={0, 1, 2, 3, 4, 5};
int main(void){
  int i, k=0;
  for(i=0; i<8; i++){
    k=k+m[i];
    printf("i==%d, m[i]==%d, k==%d\n",
           i, m[i], k);
  }
  return 0;
}
//-------------------------------------

P31A: アドレスとサイズ
//-------------------------------------
#include <stdio.h>
int m[8]={0, 1, 2, 3, 4, 5};
int main(void){
  int i,k;
  for(i=0; i<8; i++){
    printf("&m[%d]==%d\n", i, &m[i]);
  }
  printf("sizeof m[0]==%d, ",
         sizeof m[0]);
  printf("sizeof m==%d\n", sizeof m);
  printf("m[1]="); scanf("%d", m+1);
  printf("m+1==%d\nm[1]==%d\n",
         m+1, m[1]);
  k=m;
  printf("m+1==%d\nk+1==%d\n"
         "&m[1]-m==%d\n&m[1]-k==%d\n\n", 
         m+1, k+1, &m[1]-m, &m[1]-k);
  return 0;
}
//-------------------------------------

P31B: 文字列
//-------------------------------------
#include <stdio.h>
char s[]="Hello.";
int main(void){
  int i;
  for(i=0; i<7; i++){
    printf("i==%d, %%c==%c, %%s==%s\n",
           i, s[i], s+i);
  }
  for(i=0; i<7; i++){
    printf("&s[%d]==%d, s+%d==%d\n",
           i, &s[i], i, s+i);
  }
  s[3]=0; printf("s==%s\n", s);
  printf("sizeof s[0]==%d\n", sizeof s[0]);
  printf("sizeof s==%d\n", sizeof s);
  printf("strlen(s)==%d\n", strlen(s));
  return 0;
}
//-------------------------------------

[#30]の「int m[8]={0, 1, 2, 3, 4, 5};」は整数の配列の
宣言で,
  int m[8];
  m[0]=0; m[1]=1; m[2]=2; m[3]=3;
  m[4]=4; m[5]=5; m[6]=0; m[7]=0;
と等価です(#30).配列の要素 m[i] は変数として使えます.
P31 にこれらの値の総和を求める過程を表示するプログラム
を示します.

・m[8]が外部変数(大域変数)だから m[6],m[7] が 0 に
  初期化されます.
・「k=k+m[i];」はコンパイラに親切な表現「k+=m[i];」と
  等価です(#31&37).

配列要素 m[i] のアドレスとサイズは P31A のようなプロ
グラムで調べることができます.

・配列要素 m[i] の先頭アドレスは&m[i],配列 m の先頭アド
  レスは m で,m==&m[0] です.
・配列要素 m[i] のサイズは sizeof m[i],配列 m のサイズは
  sizeof m で,m==(sizeof m[i])×8 です.
・紛らわしいことに,Cでは &m[i] を m+i とも書きます.
  P31A のプログラムを実行して &m[i]==m+i であることを確認
  してください.scanf() では &m[1] の代わりに m+1 を使って
  います.表示例は
    &m[0]==4247860       // ==m
    &m[1]==4247864       // ==m+1
    &m[2]==4247868
          :
          :
    &m[7]==4247888
    sizeof m[0]==4, sizeof m==32
    m[1]=m+1==4247864
    m[1]==100
    m+1=4247864          // m==4247860
    k+1=4247861          // k==4247860
    &m[1]-m==1           //
    &m[1]-k==-12743576   // ==4248764-4247860*4
・printf() の書式ではアドレスの表示に「%p」(処理系に依存)
  を使いますが,「%d」「%i」「%u」(10進)「%o」(8進)「%x」
 「%X」(16進)のいずれでも代用できます.

C言語には文字列型がありません.文字列は文字の配列を用いて
処理します.文字列の先頭はアドレスで,末尾は終端文字 '\0'
(==0) で指定します.まず P31B のプログラムをコピーして実行
してください.

・宣言 char s[]="Hello."; は
    char s[7]={'H', 'e', 'l', 'l', 'o', '.', '\0'};
  と等価です.「s[]」とかくと "Hello." の格納に必要な最小の
  領域が割り当てられます.
・文字型の変数のサイズは 1 バイトです.
・printf() の書式「%s」では文字列の先頭アドレスを指定します.
  印刷されるのは先頭文字から '\0' の直前までです.
・sizeof s は配列のサイズで,文字列の長さとは関係ありません.
  先頭アドレスが s である文字列の長さは標準関数 strlen() が
  計算してくれます[#32].