1. 程式人生 > >《計算方法》 李曉紅等 第七章 矩陣特徵值 例題

《計算方法》 李曉紅等 第七章 矩陣特徵值 例題

#include "stdafx.h"

#include <math.h>

// 矩陣特徵值

int Maximum(double a[], int n)

{

int p=0;

int i;

for (i=1;i<n;i++) {

if (abs(a[i])>abs(a[p]))

p = i;

}

return p;

}

void MiChengMethod()

{

printf("求矩陣特徵值: 冪乘法\n");

double A[3][3] = {{4,-1,0},{0,4,-1},{0,-1,4}};

double x0[3] = {0,0,1};

double x1[3];

double lamda0,lamda1;

int k,i,j;

int n=3;

double epsilon = 1e-3;

int p;

p = Maximum(x0,n);

lamda0 = x0[p];

for (i=0;i<n;i++)

x0[i] /= lamda0;       

k = 0;

for (;;) {

for (i=0;i<n;i++) {

x1[i] = 0;

for (j=0;j<n;j++)

x1[i] += A[i][j]*x0[j];        

}

p = Maximum(x1,n);

lamda1 = x1[p];

k++;       

if (k>1&&abs(lamda1-lamda0)<epsilon)

{

for (i=0;i<n;i++) x1[i]=x1[i]/lamda1;  // 特徵向量

break;

}

else

{

lamda0 = lamda1;   // 迭代

for (i=0;i<n;i++) x0[i]=x1[i]/lamda1;

}

}

PS(&k,1,"迭代次數");

PS(&lamda1,1,"按模特徵值lamda");

PS(x1,n,"特徵向量");

}

void

 PingYiYuanDianMethod()

{

printf("冪法的加速: 平移原點法\n");

double A[3][3] = {{4,-1,0},{0,4,-1},{0,-1,4}};

double x0[3] = {0,0,1};

double x1[3];

double lamda0,lamda1;

int k,i,j;

int n=3;

double epsilon = 1e-3;

int p;

double mu=2.9; //  平移量

for (i=0;i<n;i++)    // 平移

A[i][i]-=mu;

p = Maximum(x0,n);

lamda0 = x0[p];

for (i=0;i<n;i++)

x0[i] /= lamda0;

k = 0;

for (;;) {

for (i=0;i<n;i++) {

x1[i] = 0;

for (j=0;j<n;j++)

x1[i] += A[i][j]*x0[j];        

}

p = Maximum(x1,n);

lamda1 = x1[p];

k++;       

if (k>1&&abs(lamda1-lamda0)<epsilon)

{

for (i=0;i<n;i++) x1[i]=x1[i]/lamda1;  // 特徵向量

lamda1 += mu;

break;

}

else

{

lamda0 = lamda1;   // 迭代

for (i=0;i<n;i++) x0[i]=x1[i]/lamda1;

}

}

PS(&k,1,"迭代次數");

PS(&lamda1,1,"按模特徵值lamda");

PS(x1,n,"特徵向量");

}

void AitkenJiaSuMethod()

{

printf("冪法的加速: 埃特肯加速法\n");

double A[3][3] = {{4,-1,0},{0,4,-1},{0,-1,4}};

double x0[3] = {0,0,1};

double x1[3];

double lamda[3];

int k,i,j;

int n=3;

double epsilon = 1e-3;

int p;

double lam[2]={0}; // 加速

double maxv;

p = Maximum(x0,n);

lamda[0] = x0[p];

for (i=0;i<n;i++)

x0[i] /= lamda[0];     

k = 0;

for (;;)

{

for (i=0;i<n;i++) {

x1[i] = 0;

for (j=0;j<n;j++)

x1[i] += A[i][j]*x0[j];        

}

p = Maximum(x1,n);

maxv = x1[p];

k++;       

if (k<3)

lamda[k] = maxv;

else

{

lamda[0]=lamda[1];

lamda[1]=lamda[2];

lamda[2]=maxv;

}

if (k>2) // 加速公式

{

lam[0] = lam[1];

lam[1] = lamda[0]

-(lamda[1]-lamda[0])*(lamda[1]-lamda[0])/(lamda[2]-2*lamda[1]+lamda[0]);

}

if (k>3&&abs(lam[1]-lam[0])<epsilon)

break;

else

for (i=0;i<n;i++) x0[i]=x1[i]/maxv;

}

PS(&k,1,"迭代次數");

printf("按模特徵值lamda=%.7f\n",lam[1]);

}

void FanMiMethod()

{

printf("反冪法: 求矩陣最小特徵值\n");

double a[3][3] = {{1,-1,2},{-2,0,5},{6,-3,6}};

double b[3] = {1,1,1};

double l[3][3],u[3][3];

double y[3]={0};

int n=3;

int i,j;   

double x[3];

double lamda0,lamda1;

double epsilon=1e-3;

for (i = 0; i < n; i++) {

for (j = 0; j < n; j++) {

l[i][j] = u[i][j] = 0.;

}

}

// LU分解

int k = 0;;

int r;

for (k = 0; k < n; k++) {

// 先計算u

for (j = k; j < n; j++) {

u[k][j] = a[k][j];

for (r = 0; r < k && k > 0; r++) {

u[k][j] -= l[k][r]*u[r][j];

}

}

// 後計算l

l[k][k] = 1.;

for (i = k+1; i < n; i++) {

l[i][k] = a[i][k];

for (r = 0; r < k && k > 0; r++) {

l[i][k] -= l[i][r]*u[r][k];

}

l[i][k] /= u[k][k];

}

}

printf("l=\n");

for (i = 0; i < n; i++)

{

for (j = 0; j < n; j++)

printf("%f\t",l[i][j]);

printf("\n");

}

printf("u=\n");

for (i = 0; i < n; i++)

{

for (j = 0; j < n; j++)

printf("%f\t",u[i][j]);

printf("\n");

}

int p= Maximum(b,n);

lamda0=b[p];

for (i=0;i<n;i++) {

b[i]/=lamda0;

}

// 迭代

k=0;

for(;;)

{

// 回代求解

// 回代 求y

// Ly = b

y[0] = b[0];

for (i = 1; i < n; i++) {

y[i] = b[i];

for (j = 0; j < i; j++) {

y[i] = y[i] - l[i][j]*y[j];

}

}

// 求x

//Ux=y

x[n-1] = y[n-1]/u[n-1][n-1];

for (i = n-2; i >=0; i--) {

x[i] = y[i];

for (j = i+1; j < n; j++) {

x[i] = x[i] - u[i][j]*x[j];

}

x[i] /= u[i][i];

}

k++;

p=Maximum(x,n);

lamda1 =x[p];

printf("t%d=%f\n",k,lamda1);

printf("y%d= ",k);

for (i = 0; i < n; i++) {

printf("%.4f\t",x[i]/lamda1);

}

printf("\n");

if (abs(lamda1-lamda0)<epsilon)

{

break;

}

else

{

lamda0=lamda1;

for (i=0;i<n;i++) {

b[i]=x[i]/lamda1;

}

}

}

}

void FanMiMethod_2()

{

printf("反冪法: 已知某特徵值的近似值求更精確的特徵值\n");

double a[3][3] = {{4,-1,0},{0,4,-1},{0,-1,4}};

double b[3] = {0,0,1};

double l[3][3],u[3][3];

double y[3]={0};

int n=3;

int i,j;   

double x[3];

double lamda0,lamda1;

double epsilon=1e-3;

double lam=4.93;

for (i = 0; i < n; i++) {

a[i][i] -= lam;              // 減掉對角元

}

// LU分解, 先算u,後算l

int k = 0;;

int r;

for (k = 0; k < n; k++) {

// 先計算u, 行k

for (j = 0; j < n; j++) {

if (j<k)

u[k][j]=0.;

else

{

u[k][j] = a[k][j];

for (r = 0; r < k && k > 0; r++) {

u[k][j] -= l[k][r]*u[r][j];

}

}

}

// 後計算l,列k

for (i = 0; i < n; i++)

{

if (i<k) l[i][k]=0.;

else if(i==k) l[i][k]=1.;

else

{

l[i][k] = a[i][k];

for (r = 0; r < k && k > 0; r++) {

l[i][k] -= l[i][r]*u[r][k];

}

l[i][k] /= u[k][k];

}

}

}

printf("l=\n");

for (i = 0; i < n; i++)

{

for (j = 0; j < n; j++)

printf("%f\t",l[i][j]);

printf("\n");

}

printf("u=\n");

for (i = 0; i < n; i++)

{

for (j = 0; j < n; j++)

printf("%f\t",u[i][j]);

printf("\n");

}

int p= Maximum(b,n);

lamda0=b[p];

for (i=0;i<n;i++) {

b[i]/=lamda0;

}

// 迭代

k=0;

for(;;)

{

// 回代求解

// 回代 求y

// Ly = b

y[0] = b[0];

for (i = 1; i < n; i++) {

y[i] = b[i];

for (j = 0; j < i; j++) {

y[i] = y[i] - l[i][j]*y[j];

}

}

// 求x

//Ux=y

x[n-1] = y[n-1]/u[n-1][n-1];

for (i = n-2; i >=0; i--) {

x[i] = y[i];

for (j = i+1; j < n; j++) {

x[i] = x[i] - u[i][j]*x[j];

}

x[i] /= u[i][i];

}

k++;

p=Maximum(x,n);

lamda1 =x[p];

printf("t%d=%f\n",k,lamda1);

//      printf("t%d=%f\n",k,1./lamda1+lam);

printf("y%d= ",k);

for (i = 0; i < n; i++) {

printf("%f\t",x[i]/lamda1);

}

printf("\n");

if (abs(lamda1-lamda0)<epsilon)

break;

else

{

lamda0=lamda1;

for (i=0;i<n;i++) {

b[i]=x[i]/lamda1;

}

}

}

printf("更精確的特徵值=%f\n",1./lamda1+lam);

}

// 將矩陣化成二次型的方法

void JacobiMethod()

{

printf("求實對稱矩陣特徵值的雅可比方法:\n");

double a[3][3] = {{-1,2,1},{2,-4,1},{1,1,-6}};

double b[3][3];

int i,j;

int p,q;

int n=3;

double epsilon = 5e-10;

double w;

double mu,lamda,s,c;

int k=0;

double B[3][3]={{1,0,0},{0,1,0},{0,0,1}};  // 特徵向量

double U[3][3]={{1,0,0},{0,1,0},{0,0,1}};  // 旋轉矩陣

double Tmp[3][3];

printf("輸入矩陣a%d=\n",k);

for (i = 0; i < n; i++)

{

for (j = 0; j < n; j++)

printf("%f\t",a[i][j]);

printf("\n");

}

for (;;) {

// 找到矩陣中非對角元最大值位置(p,q)

p = 0;

q = 1;

for (i=0;i<n;i++) {

for (j=0;j<n;j++) {

if (j!=i&&abs(a[i][j])>abs(a[p][q]))

{

p=i;q=j;

}

}

}

// 如果最大元充分小,結束

if (abs(a[p][q])<epsilon)

break;

lamda = 2*a[p][q];

mu = (a[p][p]-a[q][q]);

//      printf("tan=%f\n",lamda/mu);

if (abs(mu)<epsilon)

{

w=1.;

}

else

{

if (mu>0)

w=lamda/sqrt(lamda*lamda+mu*mu);

else

w=-lamda/sqrt(lamda*lamda+mu*mu);

}

s=w/sqrt(2*(1+sqrt(1-w*w)));

c=sqrt(1-s*s);

// 做一次旋轉

for (i = 0; i < n; i++)

{

for (j = 0; j < n; j++)

b[i][j]=a[i][j];

}

// p行q行

for (j=0;j<n;j++)

{

if (j!=p&&j!=q)

{

b[p][j] = a[p][j]*c+a[q][j]*s;

}

if (j!=p&&j!=q)

{

b[q][j]= -a[p][j]*s+a[q][j]*c;

}

}

// p列q列

for (i=0;i<n;i++)

{

if (i!=p&&i!=q)

{

b[i][p] = a[i][p]*c+a[i][q]*s;

}

if (i!=p&&i!=q)

{

b[i][q]= -a[i][p]*s+a[i][q]*c;

}

}

// 對角元

b[p][p]=a[p][p]*c*c+a[q][q]*s*s+2*a[p][q]*s*c;

b[q][q]=a[p][p]*s*s+a[q][q]*c*c-2*a[p][q]*s*c;

b[p][q]=b[q][p] = 0.;

//      b[p][q]=b[q][p] = (a[q][q]-a[p][p])*s*c+a[p][q]*(2*c*c-1);

// 特徵向量

for (i=0;i<n;i++) {

U[i][i]=1;

for (j=0;j<n;j++) {

if (j!=i)

{

U[i][j]=0;

}

}

}

U[p][p] = U[q][q]=c;

if (p<q)

{

U[p][q] = -s;

U[q][p] = s;

}

else

{

U[p][q] = s;

U[q][p] = -s;

}

for (i=0;i<n;i++) {

for (j=0;j<n;j++) {

Tmp[i][j] =0;

for (int k=0;k<n;k++) {

Tmp[i][j]+=B[i][k]*U[k][j];

}

}

}

k++;

// 如果旋轉後的值非常小, 置為0

for (i = 0; i < n; i++)

{

for (j = 0; j < n; j++)

{

if (i!=j&&abs(b[i][j])<5e-10)

{

b[i][j] = 0;

}

a[i][j]=b[i][j];

B[i][j]=Tmp[i][j];

}

}

}

printf("a%d=\n",k);

for (i = 0; i < n; i++)

{

printf("%.14f\t",a[i][i]);

}

printf("\n");

printf("B%d=\n",k);

for (i = 0; i < n; i++)

{

for (j = 0; j < n; j++)

{

printf("%.14f\t",B[i][j]);

}

printf("\n");

}

}