相関積分の計算

相関積分の計算量

 一般的な m 次元空間に再構成されたアトラクタ上の点 X i によって定義される 相関積分は,

で与えられる.ここで, N はデータ点の数,H はヘビサイド関数

である.
 相関積分は単純な距離計算なのでいくつかの高速化が考えられる.
 まず,次式により計算量を1/2にする.

この式により,各点どうしの距離をもとめ,その距離に応じて C m ( r ) をインクリメントすればよい.(効率のよいインクリメントの仕方は後に述べよう.)とにかく,距離関数の計算 には次の図の部分の計算が必要である..

ここで m は埋め込み次元であるわけだが,もし,タケンスの埋め込み定理などを利用して 1次元の時系列データから遅れ時間 τ によりアトラクタを再構成しているならば, 埋め込み部分の計算はほとんど除外できる.すなわち,


みたいな感じで,上図の黄色いラインの部分を先に計算してから,一度に計算するとよい.

相関積分のインクリメント

ここでは, C m ( r ) を配列化し,効率の良くカウントする方法について述べる.
最も単純な方法は,距離関数|X i - X j | 2 をもとめ,R = 1/2 log |X i - X j | 2 を計算する.あらかじめ作っておいた配列 D[]に対して,D[ceil(pR)]++; (C言語です) などの関数により整数化したインデックスの成分をインクリメントする.もちろん,pは配列に格納するための倍率である. 注意することは,対数を求める時点でRが負になる可能性があるため,
D[(ceil(pR)<0) 0: (int)ceil(pR)]++; (C言語です)
などの条件文が必要となるだろう.また,R = R 0 + 1/2 log |X i - X j | 2 として最適な R 0 を加える必要があるかもしれない. これらの値は全てもとのアトラクタのデータ値の範囲に依存する.データはあらかじめ正規化しておくのが理想的だろう.
ちなみに,

であることはいうまでも無い.

 しかしながら,毎回毎回距離計算の後にlogをとるという作業はデータ数が N が膨大になると 逆に低速化につながりかねない.logをとることによって相関積分のRを非線形にマッピングでき,そのまま相関指数の計算へと 転用できるため有用であるし,logをとらない場合は相関積分を格納する配列の大きさは膨大である.
 1つめの改善は,log 2 をとる方法である.コンピュータの数値の内部表現は2進数であるため,実数より, 指数部分の切り出しは,frexp(X, &n) (C言語です)などの関数により瞬時に切り出せる(もちろん,機械語をつかえば もっと速いが).
 2つめの改善として,現在私が使用しているのは,クイックソートの利用である.配列D[r]をインクリメントするということは, (例えば)距離が exp(r) 以内であるというように,D[r]に対応する距離半径 l[r] を定義することができる.即ち,2点間の距離を 求め,どの D[r] をインクリメントすればよいかどうかは l[r] と比較して決定すればよい.ただし,距離を1回求める毎に l[r] と照らし合わせていては低速化につながるので, N 回程度連続して距離だけ求め配列 L[i] (i=0,1,...N) に格納しておき, 一度にそれをクイックソートする.後は,rを0から増やしながら,範囲(l[r-1],l[r]]に入る L[i] の個数を求め,D[r]をカウントすればよい. ちなみに私はl[r]には2乗距離を格納している.

プログラムリスト

上記の文字に対応していない場合があります.コメントを参照してください.
デバッグしてません.あくまで参考までってことで動作は保証しません.


#define TAU 1			// 埋め込みの遅れ時間
#define N   10000			// データの総点数
#define MAXJSPHERE (2*10*1000)	// 相関積分の配列の要素数
				// 2は2乗距離,10はディケード,1000は分割数を表す
#define chk_fast false		// 高速化のためランダムサンプリングする

double m_data[N];			//1次元の時系列データ

//指定した次元までの相関積分を計算する
// Dimension:埋め込み次元
void CFractalDim::RunAnalysisCorrelationMS( int Dimension )
{
	register int i, j, m, l;
	register double f, g, p;

	int ltaumax = (Dimension - 1) * TAU;	//埋め込みに用いる過去のデータ点数
	int n = N - ltaumax;			//実際に埋め込むアトラクタの点数
	data = &m_data[ltaumax]; 	//相関積分の計算に用いるデータの先頭番地
				//m_data[0]〜m_data[ltaumax-1]は埋め込みに用いる

	//データを正規化するための定数lengthを計算する
	//データの最大最小値を求める
	f = g = m_data[0];
	for(i = 0; i < N; i++){
		if(f < m_data[i]) f = m_data[i];
		if(g > m_data[i]) g = m_data[i];
	}
	//以下の1024は適当に変更してよい
	double length = f - g;
	length = 1024.0 / length, length = length * length;	//2乗距離に変換

	//各次元における相関積分の値を格納する配列の定義
	_int64** atrsuu = new _int64* [Dimension];
	for(m = 0; m < Dimension; m++){
		atrsuu[m] = new _int64[MAXJSPHERE + 1];
		for(j = 0; j <= MAXJSPHERE; j++){
			atrsuu[m][j] = 0;	//初期化
		}
	}

	//相関積分のインデックスでの最大半径の2乗を計算する
	double lengthindex[MAXJSPHERE + 1];
	//int z = (int)ceil((log10( x * length )) * 1000.0) の逆関数
	for(j = 0; j <= MAXJSPHERE; j++){
		lengthindex[ j ]=pow(2.0, j * 0.001) / length;
	}

	//ソーティングのためのクラス
	CSort sort;
	double* sortbox = new double [n * Dimension];

	//高速化のため,ランダムサンプリングするかどうか?
	if(chk_fast){
		//ランダムサンプリングする場合
		int k;
		//100分の1のデータを利用する
		for(int loop=0; loop < n/100; loop++){
			//中心点を決定する
			i = (int)((n / (RAND_MAX + 1.0)) * (rand() + 0.5));

			//各次元における,中心点からの2乗距離を計算する
			for(j = 0; j < n; j++){
				for(g = 0.0, m = l = 0; m < Dimension; l += TAU, m++){
					f = data[i-l] - data[j-l], g += f * f, sortbox[m * n + j] = g;
				}
			}

			//ヒストグラムの計算
			for(m = 0; m < Dimension; m++){
				//距離分布をクイックソートする(データはn個 [0, n-1]の範囲)
				sort.QuickSortRiseD(&sortbox[m * n], 0, n-1);

				//距離の短いものからヒストグラムのカウントをおこなう
				for(k = j = 0; j <= MAXJSPHERE; j++){
					for(f = lengthindex[j]; k < n && sortbox[m * n + k] <= f; atrsuu[m][j]++, k++);
				}
						
				//相関次元の場合は自分の点は計算しない
				atrsuu[m][0]--; //自分の点を除外する
			}
		}
		p = (double)(n - 1) * (double)(n / 100); //トータルの点の数
	}else{
		//ランダムサンプリングしない場合
		int k;
		double* len = new double[N];
		double* plen = &len[ltaumax];

		// n * nの相関積分マップの作成
		// 条件 N=n+ltaumax , ltaumax=(Dimension-1)*tau;

		for(i = 1; i < n; i++){
			// 距離テーブルを対角線上に計算する
			for(j = 0; j < N - i; f = m_data[i + j] - m_data[j], len[j] = f * f, j++);

			//各次元の埋め込みによる距離関数を求める
			for(j = 0; j < n-i; j++){
				for(l = j, g = 0.0, m = 0; m < Dimension; l -= TAU, m++){
					g += plen[l], sortbox[m * n + j]=g;
				}
			}

			//距離関数を昇順ソートし,相関積分のインクリメントをする
			for(m = 0; m < Dimension; m++){
				sort.QuickSortRiseD(&sortbox[m * n], 0, n-i-1);
				for(k = 0,j = 0;j <= MAXJSPHERE; j++){
					for(f = lengthindex[j]; k < n-i && sortbox[m * n + k] <= f; atrsuu[m][j]++, k++);
				}
			}
		}
		delete [] len;
		p = (double)(n - 1) * (double)n / 2.0;

	}
	delete [] sortbox;

	//相関関数の積分
	for(m = 0; m < Dimension; m++){
		for(j = 0; j < MAXJSPHERE; j++){
			atrsuu[m][j + 1] += atrsuu[m][j];
		}
	}

	//ファイルへの出力(Excelで開ける形式ってことで)
	FILE* fp;
	fp=fopen("corre.dat","w");
	fprintf(fp, "↓log(ε)\t→Nm(ε)\tN=%d\tm=%d\tτ=%d\n", N, Dimension, TAU);
	for(j = 0; j <= MAXJSPHERE; j+=10){
		fprintf(fp,"%e", j / 1000.0 / 2.0);
		for(m = 0; m < Dimension; m++){
			if(atrsuu[m][j] != 0)
				fprintf(fp,"\t%e",log10((double)atrsuu[m][j] / p) / log10(2.0));
			else
				fprintf(fp,"\t");
		}
		fprintf(fp,"\n");
	}
	fclose(fp);

	//メモリの開放
	for(m = 0; m < Dimension; m++){
		delete [] atrsuu[m];
	}
	delete [] atrsuu;
}

言語は一応C++です.(C言語も混ざっていますが).
バグがありましたら連絡してください.お願いします.

戻る
2002/4
by Hiroyuki Koga