畳込みについて

畳込みに関して

はじめに

いま、上の図のようにその月に100個仕入れると、

その月は20個来月は30個さ来月は50個

売れる商品があります。

このように商品の売れ行きが分かっている
(つまり未来の状況があらかじめ予測できる)
ならば、

1月に100個
2月に50個
3月に150個

仕入れると、

1月に20個
2月に40個
3月に95個
4月に70個
5月に75個

売れることが予測できます。

グラフをもう一度見てみましょう。

つまり、その月に入荷した個数が、左の割合で出荷していくと考えることができます。

これをプログラムしてみましょう。

プログラムしてみる。

まずデータを

      1月  2月 3月 4月 5月
x[5]={100, 50, 150, 0, 0 };

としましょう。

つまり、x[2](3月)が現在ならば、x[1](2月)、x[0](1月)は過去を表しています。

それでは、1月,2月,3月,4月,5月の出荷数を計算してみます。


一般式は下記で表されます。

y[n]= \sum_{k=0}^{N-1} (h[k] x[n-k])

= h[0]x[n-0] + h[1]x[n-1] + h[2]x[n-3] + ... + h[N-1]x[n-(N-1)]

予測できる係数h[N]が決まれば計算できそうです。

hn[3]={0.2, 0.3, 0.5};
x[5]={100, 50, 150, 0,0 };

ここで

hn[3]=0.2 ... その月の予想

     0.3 ... 1か月後の予想~
     0.5 ... 2か月後の予想~

です。2か月先まで予想できるわけですね。

一般式はこうなります。

y[n]= \sum_{k=0}^{2} h[k] x[n-k]
    = h[0]x[n-0] + h[1]x[n-1] + h[2]x[n-2]

では計算します。

===============================================

■1月の計算です。

y[0] = h[0]*x[0] + h[1]x[-1] + h[2]x[-2];
     = 0.2*100 + 0.3*0 + 0.5*0;~
     = 20;
1月(現在) x "1月の予想" = "1月の出荷"~

ここでx[-1],x[-2]は,1か月前及び2か月前のデータですが、
データは1月から始まってますのでこれはゼロとみなすことができます。

を表しています。

========================================================

■2月の計算です。

y[n] = h[0]x[n-0] + h[1]x[n-1] + h[2]x[n-2];
y[1] = h[0]*x[1] + h[1]x[0] + h[2]x[-1];
     = 0.2*50 + 0.3*100 + 0.5*0;
     = 10 + 30
     = 40;
2月(現在) x "2月の予想"                            = "2月の出荷"
+
1月(ひとつ前の過去) x "1月からの1か月後の未来予想" = 1月の入荷から予想される"2月の出荷"

を表しています。

=========================================================

■3月の計算です。

y[n] = h[0]x[n-0] + h[1]x[n-1] + h[2]x[n-2];
y[2] = h[0]*x[2] + h[1]x[1] + h[2]x[0];
     = 0.2*150 + 0.3*50 + 0.5*100;
     = 30 + 15 + 50
     = 95;
3月(現在) x "3月の予想"                         = "3月の出荷"
+
2月(1つ前の過去) x "2月からの1カ月後の未来予想" = 2月の入荷から予想される"3月の出荷"
+
1月(2つ前の過去) x "1月からの2か月後の未来予想" = 1月の入荷から予想される"3月の出荷"

を表しています。

============================================================

■4月の計算です。

y[n] = h[0]x[n-0] + h[1]x[n-1] + h[2]x[n-2];
y[3] = h[0]*x[3] + h[1]x[2] + h[2]x[1];
     = 0.2*0 + 0.3*150 + 0.5*50;
     = 0 + 45 + 25
     = 70;
4月(現在) x "4月の予想"                         = "4月の出荷"
+
3月(1つ前の過去) x "3月からの1カ月後の未来予想" = 3月の入荷から予想される"4月の出荷"
+
2月(2つ前の過去) x "2月からの2カ月後の未来予想" = 2月の入荷から予想される"4月の出荷"

を表しています。

============================================================

■5月の計算です。

y[n] = h[0]x[n-0] + h[1]x[n-1] + h[2]x[n-2];
y[4] = h[0]*x[4] + h[1]x[3] + h[2]x[2];
     = 0.2*0 + 0.3*0 + 0.5*150;
     = 75;
5月(現在) x "5月の予想"                         = "5月の出荷"
+
4月(1つ前の過去) x "4月からの1カ月後の未来予想" = 4月の入荷から予想される"5月の出荷"
+
3月(1つ前の過去) x "3月からの2カ月後の未来予想" = 3月の入荷から予想される"5月の出荷"

を表しています。

================================================================

■6月の計算です。

y[n] = h[0]x[n-0] + h[1]x[n-1] + h[2]x[n-2];
y[5] = h[0]*x[5] + h[1]x[4] + h[2]x[3];
     = 0.2*0 + 0.3*0 + 0.5*0;
     = 0;
6月(現在) x "6月の予想"                         = "6月の出荷"
+
5月(1つ前の過去) x "5月からの1カ月後の未来予想" = 5月の入荷から予想される"6月の出荷"
+
4月(1つ前の過去) x "4月からの2カ月後の未来予想" = 4月の入荷から予想される"6月の出荷"

を表しています。

ここで重要なこと係数の個数分のデータが入力として与えられたとき、出力は正常に出力されると考えられます。

係数の個数分以下のデータが入力として与えられたとき、それは過度状態と等価と考えられます。

例えば、係数3で、データが1つしかない場合、
現在は1つ前の過去からの予測と2つ前の過去からの予測と現在の予測からなりたっているため、
1つ前及び2つ前の予測が定まらないためこの状態を過度状態と呼びます。

この場合は1月の結果 20ヶ及び2月の結果 40ヶはそれが本当かどうか分かりません。
この1月,2月のデータはあてになりません。

つまり、
現在は過去が分からないと不安定になってしまいます。
逆に線形性が成り立てば未来は予測できるのです。
)

また、
結果は、5月まで出力しています。5月,4月のデータがないにもかかわらず...。
これは存在したデータの最後、つまり3月のデータと2ヶ月後の未来予想の結果が
残っているからです。
そのためMatlabのconv関数では係数+2個のデータまで出力してくれます。
しかしながら4月、5月のデータもまたあまりあてにはできないことに注意してください!

1月,2月及び4月,5月はあてにできず、3月のみあてにできそうということが分かります。

この例では、
1月,2月は過度状態としてあてにはできませんが、4月,5月はデータを用意していないため
あてにできていないことが分かります。

つまり、
フィルタの出力は

データが係数個用意されたときの2つ後のデータから有効となり

また

係数と同じ量のデータは常に最新のものに置き換わらないとその後のデータは有効とならない

ということが分かります。


      1月  2月 3月 4月 5月
x[5]={100, 50, 150, 0, 0 };

は、

100    0    0    0    0
 50  100    0    0    0
150   50  100    0    0
  -  150   50  100    0   <--dataを入れ続けないといけない!
  -    -  150   50  100   <--dataを入れ続けないといけない!

それを加味するとC言語のコードは以下のようになります。

一般にグラフの横軸が正のときで、5月までプロットしてある場合は、
5月まで知っていることになるわけですから5月が現在とすると,
1月は4ヶ月前の過去になるわけです(あたりまえのことですが大事です)

つまり静止したグラフの場合は、最新が右側となります。
オシロスコープなどの動いているグラフの場合これも最新は右側になります。
そして過去は左になります。
つまりオシロの場合は、右から左にデータが流れているといえるのです。

C言語のコード

#include <stdio.h>

int main(void){
  
 int k=0;
 int count=0;
 int currentData;
 double x[3]={0,0,0};
 double hn[3]={0.2, 0.3, 0.5};
 const int hn_length=3;
 int yn=0;
  
 count=1;
 while(1){
  printf("現在のデータを入れてください\n");
  scanf("%d",&currentData);

  x[0]=(double)currentData;

  yn=0;
  for(k=0;k<hn_length;k++){
    yn = yn+(int)(hn[k]*x[k]);
  }
 
  printf("y[%d]=%d\n",count++,yn);
  //バッファの更新
  for(k=hn_length-1;k>0;k--) x[k] = x[k-1];
   
 }//end of while

 return 0;
}

C言語の結果

現在のデータを入れてください
100
y[1]=20 
現在のデータを入れてください
50
y[2]=40
現在のデータを入れてください
150
y[3]=95 
現在のデータを入れてください
0
y[4]=70
現在のデータを入れてください
0
y[5]=75
現在のデータを入れてください
0
y[6]=0
現在のデータを入れてください
0
y[7]=0
現在のデータを入れてください
0
y[8]=0
現在のデータを入れてください

MATLABのconvを使うには注意が必要である!

係数が

hn[3]={0.2, 0.3, 0.5};

でデータが

x=[100 50 150 200 30 100 500]

のように入ってきたとき、結果yは、

y=[20 40 95 110 141 129 145]

となります。データを更新した場合には4月、5月のデータもこのように変わっていきます。
これをMatlabでやってみましょう。

新しいデータは常に右端である。

Matlabのconvを使う場合、あらかじめデータと係数が分かっていなければならない。
その場合

x=[100 50 150 200 30 100 500]
  古いデータ      新しいデータ

となる。
ところがさきほど説明したとおり、結果yは、

(1) 係数個番目以上が真の値
(2) 入力データは常に更新しないと係数個より未来の値は真値にならない。

ためデータを更新しながら係数番目のyの値を取り出す必要がある。

Matlabコード test_conv.m

 hn=[0.2 0.3 0.5];
 [hnm hnn]=size(hn);

 buffer=[100 50 150 200 30 100 500];
 [M N]=size(buffer);

 x=zeros(hnm,hnn);
 
 for i=1:1:N
    x(hnn)=buffer(i)
 
 ybuf=conv(hn,x);
 y=ybuf(hnn)
 
 for j=1:1:hnn-1
    x(j) = x(j+1);
 end

end

■結果

>> test_conv

x = 0     0   100
y = 20

x = 0   100    50
y = 40

x = 100    50   150
y = 95

x = 50   150   200
y = 110

x = 150   200    30
y = 141

x = 200    30   100
y = 129

x = 30   100   500
y = 145
 

Matlabのconvと同等のMatlabソース

function y=myconv(hn,x)

[M N]=size(hn);
anum=zeros(M,2);
hn=cat(2,hn,anum)

N=N+2;
y=zeros(M,N);

[MX NX]=size(x);
if(NX <  N)
 anum=zeros(MX,N-NX);
 x=cat(2,x,anum)
end

for n=1:1:N
    for k=1:1:N
        num=n+1-k;
        if(num<1)
          y(n)=y(n);
        else
          y(n)=y(n)+hn(k)*x(num);
        end
     end
 end