数据结构课程设计报告n维矩阵乘法样本

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

数据结构
课程设计报告
设计题目: n维矩阵乘法: A B-1
专业计算机科学与技术
班级计051本
学生林垂敏
学号 29
指导教师潘崇
起止时间 .12.3- .12.11
- 年第I学期
一、具体任务
功能:
设计一个矩阵相乘的程序, 首先从键盘输入两个矩阵a, b 的内容, 并输出两个矩阵, 输出ab-1结果。

分步实施:
1.初步完成总体设计, 搭好框架, 确定人机对话的界面, 确定函数个数;
2.完成最低要求: 建立一个文件, 可完成2维矩阵的情况;
3.进一步要求: 经过键盘输入维数n。

有兴趣的同学能够
自己扩充系统功能。

要求:
1.界面友好, 函数功能要划分好
2.总体设计应画一流程图
3.程序要加必要的注释
4.要提供程序测试方案
5.程序一定要经得起测试, 宁可功能少一些, 也要能运
行起来, 不能运行的程序是没有价值的。

二、软件环境
Microsoft Visual C++ 6.0
三、问题的需求分析
程序以二维数组作为矩阵的存储结构, 经过键盘输入矩阵
维数n, 动态分配内存空间, 创立n维矩阵。

矩阵建立后再经过键盘输入矩阵的各个元素值; 也能够经过文件读入矩阵的各项数据( 维数及各元素值) 。

当要对矩阵作进一步操作( A*B或A*B^(-1)) 时, 先判断内存中是否已经有相关的数据存在, 若还未有数据存在则提示用户先输入相关数据。

当要对矩阵进行求逆时, 先利用矩阵可逆的充要条件: |A| != 0 判断矩阵是否可逆, 若矩阵的行列式 |A| = = 0 则提示该矩阵为不可逆的; 若 |A| !=0 则求其逆矩阵, 并在终端显示其逆矩阵。

四、算法设计思想及流程图
1.抽象数据类型
ADT MatrixMulti{
数据对象: D = {a(I,j)|i = 1,2,3,…,n;j =
1,2,…,n;a(i,j)∈ElemSet,n为矩阵维
数}
数据关系: R = {Row,Col}
Row = {<a(i,j),a(i,j+1)>| 1 <= i <= n , 1 <= j <= n-1}
Col = {<a(i,j),a(i+1,j)>| 1 <= i <= n-1 , 1 <= j <= n}
基本操作:
Swap(&a,&b);
初始条件: 记录a,b已存在。

操作结果: 交换记录a,b的值。

CreateMatrix(n);
操作结果: 创立n维矩阵, 返回该矩阵。

Input(&M);
初始条件: 矩阵M已存在。

操作结果: 从终端读入矩阵M的各个元素值。

Print(&M)
初始条件: 矩阵M已存在。

操作结果: 在终端显示矩阵M的各个元素值。

ReadFromFile();
操作结果: 从文件读入矩阵的相关数据。

Menu_Select();
操作结果: 返回菜单选项。

MultMatrix(&M1,&M2,&R);
初始条件: 矩阵M1, M2, R已存在。

操作结果: 矩阵M1, M2作乘法运算, 结果放在R 中。

DinV(&M,&V);
初始条件: 矩阵M, V已存在。

操作结果: 求矩阵M的逆矩阵,结果放入矩阵V中。

MatrixDeterm(&M,n);
初始条件: 矩阵M已存在。

操作结果: 求矩阵M的行列式的值。

} ADT MatrixMulti
2.矩阵求逆算法设计思想
算法采用高斯-约旦法( 全选主元) 求逆, 主要思想如下:
首先, 对于k从0到n-1作如下几步:
①从第k行、第k列开始的右下角子阵中选取绝对值
最大的元素, 并记住此元素所在的行号与列号, 再
经过行交换和列交换将它交换到主元素位置上。

这一
步称为全选主元。

②主元求倒: M(k,k) = 1 / M(k,k)
③M(k,j) = M(k,j) * M(k,k); j = 0,1,…,n-1;j != k
④M(i,j) = M(i,j) – M(i,k) * M(k,j); i,j =
0,1,…,n-1;i,j!=k
⑤M(i,k) = - M(i,k) * M(k,k), i = 0,1…,n-1; i !=
k
最后, 根据在全选主元过程中所记录的行、列交换的信息进行恢复, 恢复原则如下:
在全选主元过程中, 先交换的行( 列) 后进行恢复; 原来的
行( 列) 交换用列( 行) 交换来恢复。

3.矩阵行列式求值运算算法设计思想
利用行列式的性质: 行列式等于它的任一行( 列) 各元素与
其对应的代数余子式乘积, 即
D = ∑a(i,k)*A(i,k) ; k = 1,2,…,n;
D = ∑a(k,j)*A(k,j) ; k = 1,2,…,n;
再利用函数的递归调用法实现求其值。

4.各函数间的调用关系
5.流程图
五、源代码
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <malloc.h>
#include <string.h>
#define YES 1
#define NO 0
typedef float ElemType;
ElemType **A; //矩阵A
ElemType **B; //矩阵B
ElemType **R; //矩阵R,用于存放运算结果
ElemType **V; //矩阵V,存放逆矩阵
int n=0; //矩阵维数
int flag=-1; //标记
void swap(ElemType *a,ElemType *b) //交换记录a,b的值{
ElemType c;
c=*a;
*a=*b;
*b=c;
}
ElemType **CreateMatrix(int n) //创立n维矩阵,返回该矩阵
{
int i,j;
ElemType **M;
M = (ElemType **)malloc(sizeof(ElemType *)*n);
if(M == NULL)
exit(1);
for(i=0;i<n;i++)
{
*(M+i) = (ElemType *)malloc(sizeof(ElemType)*n);
for(j=0;j<n;j++)
*(*(M+i)+j) = 0;
}
return M;
}
ElemType MatrixDeterm(ElemType **M,int n)
/*递归法求n维矩阵行列式的值,返回运算结果*/
{
int i,j,k,l,s;
ElemType **T1;
ElemType **T2;
T1=CreateMatrix(n);
T2=CreateMatrix(n);
ElemType u;
ElemType value=0; //运算结果
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
T1[i][j]=M[i][j];
T2[i][j]=M[i][j];
}
}
if(n==2) //若为2维矩阵,则直接运算并返回运算结果
{
value=T2[0][0]*T2[1][1]-T2[0][1]*T2[1][0];
return value;
}
else
{
for(j=0;j<n;j++) //将矩阵的行列式以第一行展开
{
u=T1[0][j];
for(i=1,l=0;i<n;i++) //求矩阵行列式的余子式M(0,j)
{
for(k=0,s=0;k<n;k++)
{
if(k==j)
continue;
else
{
T2[l][s]=T1[i][k];
s++;
}
}
l++;
}
value=value+u*((int)pow(-1,j))*MatrixDeterm(T2,n-1) ;
/*行列式等于某一行的各个元素与其代数余子式的乘积之和*/
}
return value;
}
}
int DinV(ElemType **M,ElemType **V)
/*全选主元法求矩阵M的逆矩阵,结果存入矩阵V中*/
{
int i,j,k;
ElemType d;
ElemType u;
int *JS,*IS;
JS=(int *)malloc(sizeof(int)*n);
IS=(int *)malloc(sizeof(int)*n);
u=MatrixDeterm(M,n); //返回矩阵A的行列式值if(u==0)
return -1;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
V[i][j]=M[i][j];
for(k=0;k<n;k++)
{
d=0;
for(i=k;i<n;i++) //找出矩阵M从M[k][k]开始绝对值最大的元素
{
for(j=k;j<n;j++)
{
if(fabs(V[i][j])>d)
{
d=fabs(V[i][j]); //d记录绝对值最大的元素的值
/*把绝对值最大的元素在数组中的行、列坐标分别存入IS[K],JS[K]*/
IS[k]=i;
JS[k]=j;
}
}
}
if(d+1.0 == 1.0)
return 0; //所有元素都为0
if(IS[k] != k)
/*若绝对值最大的元素不在第k行,则将矩阵IS[K]行的元素与k行的元素相交换*/
for(j=0;j<n;j++)
swap(&V[k][j],&V[IS[k]][j]);
if(JS[k]!=k)
/*若绝对值最大的元素不在第k列,则将矩阵JS[K]列的元素与k列的元素相交换*/
for(i=0;i<n;i++)
swap(&V[i][k],&V[i][JS[k]]);
V[k][k]=1/V[k][k]; //绝对值最大的元素求倒
for(j=0;j<n;j++)
/*矩阵M第k行除元素M[k][k]本身外都乘以M[k][k]*/
if(j!=k)
V[k][j]=V[k][j]*V[k][k];
for(i=0;i<n;i++)
/*矩阵除第k行的所有元素与第k列的所有元素外,都拿本身减去M[i][k]*M[k][j], 其中i,j为元素本身在矩阵的位置坐标*/
if(i!=k)
for(j=0;j<n;j++)
if(j!=k)
V[i][j]=V[i][j]-V[i][k]*V[k][j];
for(i=0;i<n;i++)
/*矩阵M第k列除元素M[k][k]本身外都乘以-M[k][k]*/ if(i!=k)
V[i][k]=-V[i][k]*V[k][k];
}
for(k=n-1;k>=0;k--)
/*根据上面记录的行IS[k],列JS[k]信息恢复元素*/ {
for(j=0;j<n;j++)
if(JS[k]!=k)
swap(&V[k][j],&V[JS[k]][j]);
for(i=0;i<n;i++)
if(IS[k]!=k)
swap(&V[i][k],&V[i][IS[k]]);
}
free(IS);
free(JS);
return 0;
}
void MultMatrix(ElemType **M1,ElemType **M2,ElemType **R)
/*矩阵M1乘M2 结果存入矩阵R*/
{
int i,j,k;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
R[i][j]=0;
}
}
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
for(k=0;k<n;k++)
{
R[i][j]=R[i][j]+M1[i][k]*M2[k][j];
}
}
}
}
void Input(ElemType **M) //输入矩阵M的各个元素值{
int i,j;
char str[10];
char c='A';
if(flag==1)
c='B';
system("cls");
printf("\n\n输入矩阵%c(%d*%d)\n",c,n,n);
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
scanf("%f",*(M+i)+j);
}
}
flag=1;
gets(str); //吸收多余的字符
}
void Print(ElemType **M) //显示矩阵M的各个元素值{
int i,j;
printf("\t");
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
printf(" %.3f",M[i][j]);
}
puts("");
printf("\t\t");
}
}
int Menu_Select()
{
char c;
do{
system("cls");
puts("\t\t*************n维矩阵乘法器
*************");
puts("\t\t| 1. 经过键盘输入各项数据
|");
puts("\t\t| 2. 显示矩阵A,B |");
puts("\t\t| 3. 矩阵求逆,并显示逆矩阵
|");
puts("\t\t| 4. 求矩阵运算A*B,并显示运算结果
|");
puts("\t\t| 5. 求矩阵运算A*B^(-1),并显示运算结果
|");
puts("\t\t| 6. 从文件读入矩阵A,B与维数n |");
puts("\t\t| 0. 退出
|");
puts("\t\t***************************************")
;
printf("\t\t请选择(0-6):");
c=getchar();
}while(c<'0'||c>'6');
return (c-'0');
}
void ReadFromFile() //从指定文件读入矩阵的维数及矩
阵各元素的值
{
int i,j;
FILE *fp;
if((fp=fopen("tx.txt","r"))==NULL)
{
puts("无法打开文件!!!");
system("pause");
exit(0);
}
fscanf(fp,"%d",&n); //读入矩阵维数
A=CreateMatrix(n); //创立矩阵A B V R B=CreateMatrix(n);
V=CreateMatrix(n);
R=CreateMatrix(n);
for(i=0;i<n;i++) //读入矩阵A
{
for(j=0;j<n;j++)
{
fscanf(fp,"%f",&A[i][j]);
}
}
for(i=0;i<n;i++) //读入矩阵A
{
for(j=0;j<n;j++)
{
fscanf(fp,"%f",&B[i][j]);
}
}
puts("\n\n读文件成功");
fclose(fp);
flag=1;
}
int main()
{
int i;
char c,h;
char str[10];
for(;;)
{
switch(Menu_Select())
{
case 1: flag=-1;
for(;;)
{
system("cls");
printf("\n\n\t矩阵维数n:");
scanf("%d",&n);
gets(str);
if(n>0)
break;
else
{
printf("\n\t输入有误,请重新输入!\n");
puts("");
system("pause");
}
}
A=CreateMatrix(n);
B=CreateMatrix(n);
V=CreateMatrix(n);
R=CreateMatrix(n);
Input(A);
Input(B);
break;
case 2: system("cls");
if(flag==-1)
{
puts("\n\n\t不存在任何矩阵数据,请先输入数据");
system("pause");
break;
}
puts("\n");
printf("\tA = ");
Print(A);
puts("\n");
printf("\tB = ");
Print(B);
puts("");
system("pause");
break;
case 3: system("cls");
if(flag==-1)
{
puts("\n\n\t不存在任何矩阵数据,请先输入数据");
system("pause");
break;
}
for(;;)
{
printf("\n\n\t输入需要求逆的矩阵(A/B):");
h=getchar();
c=getchar();
//h=getchar();
if(c=='A'||c=='a')
{
i=DinV(A,V);
if(i==-1)
{
puts("\n\n\t矩阵A的行列式等于0,不可逆!");
system("pause");
break;
}
printf("\tA = ");
Print(A);
puts("\n");
printf("A^(-1) = ");
Print(V);
puts("");
system("pause");
break;
}
else if(c=='B'||c=='b')
{
i=DinV(B,V);
if(i==-1)
{
puts("\n\n\t矩阵B的行列式等于0,不可逆!");
system("pause");
break;
}
printf("\tB = ");
Print(B);
puts("\n");
printf("B^(-1) = ");
Print(V);
puts("");
system("pause");
break;
}
else
puts("\n\n\t输入有误,请重新输入!\n");
}
break;
case 4: system("cls");
if(flag==-1)
{
puts("\n\n\t不存在任何矩阵数据,请先输入数据");
system("pause");
break;
}
MultMatrix(A,B,R);
printf("\n\n\tA*B = ");
Print(R);
puts("");
system("pause");
break;
case 5: system("cls");
if(flag==-1)
{
puts("\n\n\t不存在任何矩阵数据,请先
输入数据");
system("pause");
break;
}
i=DinV(B,V);
if(i==-1)
{
puts("\n\n\t矩阵B的行列式等于0,不可逆!");
system("pause");
break;
}
MultMatrix(A,V,R);
printf("\n\nA*B^(-1) = ");
Print(R);
puts("");
system("pause");
break;
case 6: system("cls");
ReadFromFile();
puts("");
system("pause");
break;
case 0: puts("\t\t正常退出");
exit(0);
break;
}
}
return 0;
}
六、运行结果
1.主界面:
2.输入6, 回车, 从文本文件tx.txt中读入矩阵数据:
3.回车, 回到主菜单界面; 输入2回车, 显示从文件读入的矩
阵数据:
4.回车, 回到主菜单界面; 输入3回车, 对指定矩阵求逆: ( 由于这里矩阵A是不可逆的, 因此仅以矩阵B为例)
5.回车, 回到主菜单界面; 输入4回车, 求矩阵运算A*B:
6.回车回到主菜单界面, 输入5回车, 求A*B^(-1)的值:
7.回车回到主菜单界面, 输入0回车, 退出程序; 如果需要自定矩阵维数及各元素值, 请利用主菜单里的1号功能自行输入数据, 再进行以上几种运算操作。

七、收获及体会
经过这次课程设计, 让我再次复习了线性代数里矩阵的相关知识, 比如n维矩阵的求逆、矩阵可逆的充分必要条件( |A| ! = 0) 、矩阵与矩阵的乘法运算、行列式求值方法等。

同样的, 还让我复习了大量C语言里有关数组的一些重要概念, 比如多维数组的动态分配问题、数组与指针的关系等。

记得在这个学期新开设的单片机基础课上, 吴涛老师曾多次强调, 让我们一定要经常锻炼自己的编程能力, 她常对我们说: ”编程是思维的体操。

”尽管我在这方面的能力和实力非常得有限, 也远远不及班上的其它同学, 但我经过这次课程设计充分体会到了这句话的精华。

电脑程序作为人体大脑思维的延伸, 程序的功能也会因为大脑思维的不断完善而变得更加强大, 因此我决定今后要加强在这方面的锻炼和学习, 以此来激励自己不断前进!
八、参考文献
《数据结构( C语言版) 》严蔚敏, 吴伟民编著清华大学出版社
《C语言程序设计》洪维恩编著中国铁道出版社
《C语言程序设计教程》谭浩强张基温唐永炎编著高
等教育出版社
《工程数学——线性代数第四版》同济大学应用数学系编高等教育出版社
计051本林垂敏
-12-13。

相关文档
最新文档