正準相関分析

<?php
/****************************/
/* 正準相関分析             */
/*      coded by Y.Suganuma */
/****************************/

	fscanf(STDIN, "%d %d %d", $r, $s, $n);   // 各組の変数の数とデータの数
	$q   = $r + $s;

	$ryz = array($r);
	$v0  = array($r);
	$x   = array($q);
	$ab  = array($r);
	for ($i1 = 0; $i1 < $q; $i1++)
		$x[$i1]  = array($n);
	for ($i1 = 0; $i1 < $r; $i1++) {
		$ab[$i1] = array($q);
		$v0[$i1] = 1.0;
	}

	for ($i1 = 0; $i1 < $n; $i1++) {   // データ
		$str       = trim(fgets(STDIN));
		$x[0][$i1] = floatval(strtok($str, " "));
		for ($i2 = 1; $i2 < $q; $i2++)
			$x[$i2][$i1] = floatval(strtok(" "));
	}

	$sw = canonical($r, $s, $n, $x, $ryz, $ab, 1.0e-10, $v0, 15, 200);

	if ($sw > 0) {
		for ($i1 = 0; $i1 < $sw; $i1++) {
			printf("相関係数 %f\n", $ryz[$i1]);
			printf("   a");
			for ($i2 = 0; $i2 < $r; $i2++)
				printf(" %f", $ab[$i1][$i2]);
			printf("\n");
			printf("   b");
			for ($i2 = 0; $i2 < $s; $i2++)
				printf(" %f", $ab[$i1][$r+$i2]);
			printf("\n");
		}
	}
	else
		printf("***error  解を求めることができませんでした\n");

/***********************************/
/* 正準相関分析                    */
/*      r,s : 各組における変数の数 */
/*      n : データの数             */
/*      x : データ                 */
/*      ryz : 相関係数             */
/*      ab : 各組の係数(a,bの順) */
/*      eps : 正則性を判定する規準 */
/*      v0 : 固有ベクトルの初期値  */
/*      m : 丸め誤差の修正回数     */
/*      ct : 最大繰り返し回数      */
/*      return : >0 : 相関係数の数 */
/*               =0 : エラー       */
/***********************************/
function canonical($r, $s, $n, $x, &$ryz, &$ab, $eps, $v0, $m, $ct)
{
	$sw = 0;
					// 領域の確保
	$q    = $r + $s;
	$mean = array($q);
	$w1   = array($s);
	$v1   = array($r);
	$v2   = array($r);
	$A    = array($s);
	$B    = array($r);
	$C    = array($q);
	$C11  = array($r);
	$C11i = array($r);
	$C12  = array($r);
	$C21  = array($s);
	$C22  = array($s);
	$C22i = array($s);
	$w    = array($s);
	for ($i1 = 0; $i1 < $q; $i1++)
		$C[$i1] = array($q);
	for ($i1 = 0; $i1 < $r; $i1++) {
		$B[$i1]    = array($r);
		$C11[$i1]  = array($r);
		$C11i[$i1] = array($r);
		$C12[$i1]  = array($s);
	}
	for ($i1 = 0; $i1 < $s; $i1++) {
		$A[$i1]    = array($s);
		$C21[$i1]  = array($r);
		$C22[$i1]  = array($s);
		$C22i[$i1] = array($s);
		$w[$i1]    = array(2*$s);
	}
					// 平均値の計算
	for ($i1 = 0; $i1 < $q; $i1++) {
		$mean[$i1] = 0.0;
		for ($i2 = 0; $i2 < $n; $i2++)
			$mean[$i1] += $x[$i1][$i2];
		$mean[$i1] /= $n;
	}
					// 分散強分散行列の計算
	for ($i1 = 0; $i1 < $q; $i1++) {
		for ($i2 = $i1; $i2 < $q; $i2++) {
			$vv = 0.0;
			for ($i3 = 0; $i3 < $n; $i3++)
				$vv += ($x[$i1][$i3] - $mean[$i1]) * ($x[$i2][$i3] - $mean[$i2]);
			$vv        /= ($n - 1);
			$C[$i1][$i2]  = $vv;
			if ($i1 != $i2)
				$C[$i2][$i1] = $vv;
		}
	}
					// C11, C12, C21, C22 の設定
						// C12
	for ($i1 = 0; $i1 < $r; $i1++) {
		for ($i2 = 0; $i2 < $s; $i2++)
			$C12[$i1][$i2] = $C[$i1][$i2+$r];
	}
						// C21
	for ($i1 = 0; $i1 < $s; $i1++) {
		for ($i2 = 0; $i2 < $r; $i2++)
			$C21[$i1][$i2] = $C[$i1+$r][$i2];
	}
						// C11とその逆行列
	for ($i1 = 0; $i1 < $r; $i1++) {
		for ($i2 = 0; $i2 < $r; $i2++) {
			$w[$i1][$i2]   = $C[$i1][$i2];
			$C11[$i1][$i2] = $C[$i1][$i2];
		}
		for ($i2 = $r; $i2 < 2*$r; $i2++)
			$w[$i1][$i2] = 0.0;
		$w[$i1][$i1+$r] = 1.0;
	}
	$sw1 = gauss($w, $r, $r, 1.0e-10);
	if ($sw1 == 0) {
		for ($i1 = 0; $i1 < $r; $i1++) {
			for ($i2 = 0; $i2 < $r; $i2++)
				$C11i[$i1][$i2] = $w[$i1][$i2+$r];
		}
	}
	else
		$sw = 1;
						// C22とその逆行列
	for ($i1 = 0; $i1 < $s; $i1++) {
		for ($i2 = 0; $i2 < $s; $i2++) {
			$w[$i1][$i2]   = $C[$i1+$r][$i2+$r];
			$C22[$i1][$i2] = $C[$i1+$r][$i2+$r];
		}
		for ($i2 = $s; $i2 < 2*$s; $i2++)
			$w[$i1][$i2] = 0.0;
		$w[$i1][$i1+$s] = 1.0;
	}
	$sw1 = gauss($w, $s, $s, $eps);
	if ($sw1 == 0) {
		for ($i1 = 0; $i1 < $s; $i1++) {
			for ($i2 = 0; $i2 < $s; $i2++)
				$C22i[$i1][$i2] = $w[$i1][$i2+$s];
		}
	}
	else
		$sw = 1;
					// 固有値λ及び固有ベクトルaの計算
	if ($sw == 0) {
						// 行列の計算
		for ($i1 = 0; $i1 < $s; $i1++) {
			for ($i2 = 0; $i2 < $r; $i2++) {
				$A[$i1][$i2] = 0.0;
				for ($i3 = 0; $i3 < $s; $i3++)
					$A[$i1][$i2] += $C22i[$i1][$i3] * $C21[$i3][$i2];
			}
		}

		for ($i1 = 0; $i1 < $r; $i1++) {
			for ($i2 = 0; $i2 < $s; $i2++) {
				$w[$i1][$i2] = 0.0;
				for ($i3 = 0; $i3 < $s; $i3++)
					$w[$i1][$i2] += $C12[$i1][$i3] * $A[$i3][$i2];
			}
		}

		for ($i1 = 0; $i1 < $r; $i1++) {
			for ($i2 = 0; $i2 < $r; $i2++) {
				$A[$i1][$i2] = 0.0;
				for ($i3 = 0; $i3 < $r; $i3++)
					$A[$i1][$i2] += $C11i[$i1][$i3] * $w[$i3][$i2];
			}
		}
						// 固有値と固有ベクトル(べき乗法)
		for ($i1 = 0; $i1 < $r; $i1++) {
			for ($i2 = 0; $i2 < $r; $i2++)
				$B[$i1][$i2] = 0.0;
			$B[$i1][$i1] = 1.0;
		}

		$sw = power(0, $r, $m, $ct, $eps, $A, $B, $C, $ryz, $ab, $v0, $v1, $v2);

		if ($sw > 0) {

			for ($i1 = 0; $i1 < $r; $i1++) {
							// 相関係数
				$ryz[$i1] = sqrt($ryz[$i1]);
							// 大きさの調整(a)
				for ($i2 = 0; $i2 < $r; $i2++) {
					$w1[$i2] = 0.0;
					for ($i3 = 0; $i3 < $r; $i3++)
						$w1[$i2] += $C11[$i2][$i3] * $ab[$i1][$i3];
				}
				$len = 0.0;
				for ($i2 = 0; $i2 < $r; $i2++)
					$len += $ab[$i1][$i2] * $w1[$i2];
				$len = sqrt($len);
				for ($i2 = 0; $i2 < $r; $i2++)
					$ab[$i1][$i2] /= $len;
							// bの計算
				for ($i2 = 0; $i2 < $s; $i2++) {
					$w1[$i2] = 0.0;
					for ($i3 = 0; $i3 < $r; $i3++)
						$w1[$i2] += $C21[$i2][$i3] * $ab[$i1][$i3];
				}
				for ($i2 = 0; $i2 < $s; $i2++) {
					$ab[$i1][$i2+$r] = 0.0;
					for ($i3 = 0; $i3 < $s; $i3++)
						$ab[$i1][$i2+$r] += $C22i[$i2][$i3] * $w1[$i3];
				}
				for ($i2 = 0; $i2 < $s; $i2++)
					$ab[$i1][$i2+$r] /= $ryz[$i1];
							// 大きさの調整(b)
				for ($i2 = 0; $i2 < $s; $i2++) {
					$w1[$i2] = 0.0;
					for ($i3 = 0; $i3 < $s; $i3++)
						$w1[$i2] += $C22[$i2][$i3] * $ab[$i1][$i3+$r];
				}
				$len = 0.0;
				for ($i2 = 0; $i2 < $s; $i2++)
					$len += $ab[$i1][$i2+$r] * $w1[$i2];
				$len = sqrt($len);
				for ($i2 = 0; $i2 < $s; $i2++)
					$ab[$i1][$i2+$r] /= $len;
			}
		}
	}
	else
		$sw = 0;

	return $sw;
}

/*******************************************************/
/* 線形連立方程式を解く(逆行列を求める)              */
/*      w : 方程式の左辺及び右辺                       */
/*      n : 方程式の数                                 */
/*      m : 方程式の右辺の列の数                       */
/*      eps : 正則性を判定する規準                     */
/*      return : =0 : 正常                             */
/*               =1 : 逆行列が存在しない               */
/*******************************************************/
function gauss(&$w, $n, $m, $eps)
{
	$ind = 0;
	$nm  = $n + $m;

	for ($i1 = 0; $i1 < $n && $ind == 0; $i1++) {

		$y1 = .0;
		$m1 = $i1 + 1;
		$m2 = 0;

		for ($i2 = $i1; $i2 < $n; $i2++) {
			$y2 = abs($w[$i2][$i1]);
			if ($y1 < $y2) {
				$y1 = $y2;
				$m2 = $i2;
			}
		}

		if ($y1 < $eps)
			$ind = 1;

		else {

			for ($i2 = $i1; $i2 < $nm; $i2++) {
				$y1          = $w[$i1][$i2];
				$w[$i1][$i2] = $w[$m2][$i2];
				$w[$m2][$i2] = $y1;
			}

			$y1 = 1.0 / $w[$i1][$i1];

			for ($i2 = $m1; $i2 < $nm; $i2++)
				$w[$i1][$i2] *= $y1;

			for ($i2 = 0; $i2 < $n; $i2++) {
				if ($i2 != $i1) {
					for ($i3 = $m1; $i3 < $nm; $i3++)
						$w[$i2][$i3] -= $w[$i2][$i1] * $w[$i1][$i3];
				}
			}
		}
	}

	return($ind);
}

/****************************************/
/* 最大固有値と固有ベクトル(べき乗法) */
/*      i : 何番目の固有値かを示す      */
/*      n : 次数                        */
/*      m : 丸め誤差の修正回数          */
/*      ct : 最大繰り返し回数           */
/*      eps : 収束判定条件              */
/*      A : 対象とする行列              */
/*      B : nxnの行列,最初は,単位行列 */
/*      C : 作業域,nxnの行列           */
/*      r : 固有値                      */
/*      v : 各行が固有ベクトル(nxn)   */
/*      v0 : 固有ベクトルの初期値       */
/*      v1,v2 : 作業域(n次元ベクトル) */
/*      return : 求まった固有値の数     */
/*      coded by Y.Suganuma             */
/****************************************/
function power($i, $n, $m, $ct, $eps, $A, $B, $C, &$r, &$v, $v0, $v1, $v2)
{
					// 初期設定
	$ind = $i;
	$k   = 0;
	$l1  = 0.0;
	for ($i1 = 0; $i1 < $n; $i1++) {
		$l1      += $v0[$i1] * $v0[$i1];
		$v1[$i1]  = $v0[$i1];
	}
	$l1 = sqrt($l1);
					// 繰り返し計算
	while ($k < $ct) {
						// 丸め誤差の修正
		if ($k%$m == 0) {
			$l2 = 0.0;
			for ($i1 = 0; $i1 < $n; $i1++) {
				$v2[$i1] = 0.0;
				for ($i2 = 0; $i2 < $n; $i2++)
					$v2[$i1] += $B[$i1][$i2] * $v1[$i2];
				$l2 += $v2[$i1] * $v2[$i1];
			}
			$l2 = sqrt($l2);
			for ($i1 = 0; $i1 < $n; $i1++)
				$v1[$i1] = $v2[$i1] / $l2;
		}
						// 次の近似
		$l2 = 0.0;
		for ($i1 = 0; $i1 < $n; $i1++) {
			$v2[$i1] = 0.0;
			for ($i2 = 0; $i2 < $n; $i2++)
				$v2[$i1] += $A[$i1][$i2] * $v1[$i2];
			$l2 += $v2[$i1] * $v2[$i1];
		}
		$l2 = sqrt($l2);
		for ($i1 = 0; $i1 < $n; $i1++)
			$v2[$i1] /= $l2;
						// 収束判定
							// 収束した場合
		if (abs(($l2-$l1)/$l1) < $eps) {
			$k1 = -1;
			for ($i1 = 0; $i1 < $n && $k1 < 0; $i1++) {
				if (abs($v2[$i1]) > 0.001) {
					$k1 = $i1;
					if ($v2[$k1]*$v1[$k1] < 0.0)
						$l2 = -$l2;
				}
			}
			$k     = $ct;
			$r[$i] = $l2;
			for ($i1 = 0; $i1 < $n; $i1++)
				$v[$i][$i1] = $v2[$i1];
			if ($i == $n-1)
				$ind = $i + 1;
			else {
				for ($i1 = 0; $i1 < $n; $i1++) {
					for ($i2 = 0; $i2 < $n; $i2++) {
						$C[$i1][$i2] = 0.0;
						for ($i3 = 0; $i3 < $n; $i3++) {
							$x            = ($i1 == $i3) ? $A[$i1][$i3] - $l2 : $A[$i1][$i3];
							$C[$i1][$i2] += $x * $B[$i3][$i2];
						}
					}
				}
				for ($i1 = 0; $i1 < $n; $i1++) {
					for ($i2 = 0; $i2 < $n; $i2++)
						$B[$i1][$i2] = $C[$i1][$i2];
				}
				for ($i1 = 0; $i1 < $n; $i1++) {
					$v1[$i1] = 0.0;
					for ($i2 = 0; $i2 < $n; $i2++)
						$v1[$i1] += $B[$i1][$i2] * $v0[$i2];
				}
				for ($i1 = 0; $i1 < $n; $i1++)
					$v0[$i1] = $v1[$i1];
				$ind = power($i+1, $n, $m, $ct, $eps, $A, $B, $C, $r, $v, $v0, $v1, $v2);
			}
		}
							// 収束しない場合
		else {
			for ($i1 = 0; $i1 < $n; $i1++)
				$v1[$i1] = $v2[$i1];
			$l1 = $l2;
			$k++;
		}
	}

	return $ind;
}

/*
---------データ例(コメント部分を除いて下さい)---------
2 2 100   // 各組の変数の数(r と s, r ≦ s)とデータの数(n)
66 22 44 31   // x1, x2, x3, x4
25 74 17 81
50 23 53 71
25 57 19 81
74 47 64 47
39 33 48 46
14 22 9 69
67 60 49 26
42 40 77 65
11 80 0 86
32 0 43 74
68 69 44 68
24 49 9 71
42 74 28 46
60 58 73 28
36 37 33 68
24 44 19 83
30 40 31 50
55 40 60 49
63 47 94 41
72 30 100 45
19 22 13 75
43 39 43 34
90 83 92 31
51 77 52 82
53 70 34 31
28 51 53 44
40 62 42 79
31 48 22 68
57 29 51 30
64 89 57 42
49 82 72 29
53 31 55 43
79 52 70 10
45 19 43 57
35 34 34 89
4 69 0 100
49 49 66 66
92 82 97 6
5 89 0 100
65 26 83 28
56 36 64 38
48 50 25 22
30 30 15 55
40 65 38 42
14 67 9 67
84 96 90 8
53 64 51 54
50 89 60 52
76 41 68 9
49 40 53 49
78 66 66 17
76 58 90 29
41 15 40 49
63 60 55 33
40 36 49 67
78 54 71 18
62 72 69 12
64 47 42 53
56 64 9 15
77 35 56 25
44 12 46 87
80 9 56 19
36 21 52 78
48 63 64 48
43 61 50 47
58 23 28 50
90 12 100 0
13 33 11 77
67 44 48 28
75 45 68 17
81 22 89 9
46 45 59 55
56 49 64 55
65 62 72 27
34 49 29 77
45 33 60 63
20 45 14 99
33 38 26 87
44 51 69 52
64 57 64 48
44 64 51 28
63 48 56 11
29 39 33 84
40 48 51 54
40 38 26 62
68 46 61 26
58 45 68 48
64 44 77 63
59 62 44 66
81 53 93 19
23 34 12 68
51 35 55 46
74 70 84 17
42 33 56 44
46 31 46 53
33 57 38 63
40 24 20 42
53 36 60 31
0 34 0 100
*/

?>