最近频繁用到矩阵相乘运算, 所以想写一个函数。遇到的问题不少!
关于矩阵乘法规则,请查看: [050] 求两个矩阵的乘积矩阵
算法参考了徐士良的《常用算法程序集》里的一个实矩阵相乘函数,如下:
void brmul(a, b, m, n, k, c)
int m, n, k;
double a[], b[], c[];
{
int i, j, l, u;
for (i = 0; i<= m - 1; i++)
for (j = 0; j <= k - 1; j++)
{
u = i * k + j;
c[u] = 0.0;
for (l = 0; l <= n - 1; l++)
c[u] = c[u] + a[i * n + l] * b[l * k + j];
}
}
参数说明:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
形参与函数类型 说明
————————————————————————————
double a[m][n] 存放矩阵A的元素
————————————————————————————
double b[n][k] 存放矩阵B的元素
————————————————————————————
int m 矩阵A与乘积矩阵C的行数
————————————————————————————
int n 矩阵A的列数,矩阵B的行数
————————————————————————————
int k 矩阵B与乘积矩阵C的列数
————————————————————————————
double c[m][k] 返回乘积矩阵C=AB的元素
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
一个验证程序:
#include <stdio.h>
void brmul(a, b, m, n, k, c)
int m, n, k;
double a[], b[], c[];
{
int i, j, l, u;
for (i = 0; i<= m - 1; i++)
for (j = 0; j <= k - 1; j++)
{
u = i * k + j;
c[u] = 0.0;
for (l = 0; l <= n - 1; l++)
c[u] = c[u] + a[i * n + l] * b[l * k + j];
}
}
int main()
{
int i, j;
static double a[4][5] = { { 1.0, 3.0, -2.0, 0.0, 4.0},
{-2.0, -1.0, 5.0, -7.0, 2.0},
{ 0.0, 8.0, 4.0, 1.0, -5.0},
{ 3.0, -3.0, 2.0, -4.0, 1.0}};
static double c[4][3], b[5][3] = { { 4.0, 5.0, -1.0},
{ 2.0, -2.0, 6.0},
{ 7.0, 8.0, 1.0},
{ 0.0, 3.0, -5.0},
{ 9.0, 8.0, -6.0}};
brmul(a, b, 4, 5, 3, c);
for (i = 0; i <= 3; i++)
{
for (j = 0; j <= 2; j++)
printf("%14.7e", c[i][j]);
printf("\n");
}
printf("\n");
getch();
return 0;
}
运行结果(TC):
===================================================================
3.200000e+01 1.500000e+01 -9.000000e+00
4.300000e+01 2.700000e+01 2.400000e+01
-1.000000e+00 -2.100000e+01 7.700000e+01
2.900000e+01 3.300000e+01 -5.000000e+00
===================================================================
★ 这个程序可以在TC, Win-TC里运行,但在VC, Dev-C++里都会在函数声明那里报错,于是注意到了这个函数声明部分有些特别, 以前还真没看过这种形式, 参数的类型声明在函数定义和函数体之间,并且这里所用的形式还有不同,比如参数里用的a, 在类型声明时却用的是 double a[] 。想必是这里出了问题,开始怀疑这种方式不是C的标准而是Turbo C里自己定义的,后来查了一下谭老的书才明白,原来这是一种对形参类型声明的传统方式。将书中部分内容摘录如下:
对形参的声明的传统方式:
在老版本的C语言中,对形参类型的声明是放在函数定义的第2行,也就是不在第1行的括号内指定形参的类型,而在括号外单独指定。一般把这种方法称为传统的对形参的声明方式,而把现在常用的这种方法称为现代的方式。Turbo C和目前使用的多数C版本对这两种方法都允许使用,两种用法等价,ANSI新标准推荐后者,即现代方式。它与PASCAL语言中所用的方法是类似的。本书中的程序采用新标准推荐的现代方式。但由于有些过去写的书籍和程序使用传统方式,因此读者应对它有所了解,以便能方便地阅读它们。
又一次感到谭老的书内容还是比较全的。
★ 那么是不是将上面的声明改成现代方式就可以在VC里运行了呢?经试验是不行的,改成如下形式可以在TC里运行:
void brmul(double a[], double b[], int m, int n, int k, double c[])
{
int i, j, l, u;
for (i = 0; i<= m - 1; i++)
for (j = 0; j <= k - 1; j++)
{
u = i * k + j;
c[u] = 0.0;
for (l = 0; l <= n - 1; l++)
c[u] = c[u] + a[i * n + l] * b[l * k + j];
}
}
验证:
#include <stdio.h>
void brmul(double a[], double b[], int m, int n, int k, double c[])
{
int i, j, l, u;
for (i = 0; i<= m - 1; i++)
for (j = 0; j <= k - 1; j++)
{
u = i * k + j;
c[u] = 0.0;
for (l = 0; l <= n - 1; l++)
c[u] = c[u] + a[i * n + l] * b[l * k + j];
}
}
int main()
{
int i, j;
static double a[4][5] = { { 1.0, 3.0, -2.0, 0.0, 4.0},
{-2.0, -1.0, 5.0, -7.0, 2.0},
{ 0.0, 8.0, 4.0, 1.0, -5.0},
{ 3.0, -3.0, 2.0, -4.0, 1.0}};
static double c[4][3], b[5][3] = { { 4.0, 5.0, -1.0},
{ 2.0, -2.0, 6.0},
{ 7.0, 8.0, 1.0},
{ 0.0, 3.0, -5.0},
{ 9.0, 8.0, -6.0}};
brmul(a, b, 4, 5, 3, c);
for (i = 0; i <= 3; i++)
{
for (j = 0; j <= 2; j++)
printf("%14.7e", c[i][j]);
printf("\n");
}
printf("\n");
getch();
return 0;
}
TC运行结果:
===================================================================
3.200000e+01 1.500000e+01 -9.000000e+00
4.300000e+01 2.700000e+01 2.400000e+01
-1.000000e+00 -2.100000e+01 7.700000e+01
2.900000e+01 3.300000e+01 -5.000000e+00
===================================================================
★ 但在VC里调用这个函数时参数传递会出错,即上例中 brmul(a, b, 4, 5, 3, c); 这一句。又在Dev-C++里试了下,也是同样的错误。我想应该是参数传递时出现的问题,即这种参数传递方式也应该类似的是传统的一种方式,而非新标准所推荐的。 记得看过一篇文章讲的是数组名和指针的关系,作者阐述的是数组名并不等同于指针的观点。联系一下,我猜上例中的 brmul(a, b, 4, 5, 3, c); 一句就是将数组名用作指针的,而这种用法是老版本C所支持的,所以可以在TC里运行。而在VC,Dev-C++里执行的应该是新标准,不支持这种用法。那么用指针改写一下是不是就可以了呢? 作如下验证,调用时用 brmul(*a, *b, 4, 5, 3, *c); , 由结果看应该是正确的(由于是在VC里,所以将输出的位宽加了一下):
#include <stdio.h>
void brmul(double a[], double b[], int m, int n, int k, double c[])
{
int i, j, l, u;
for (i = 0; i<= m - 1; i++)
for (j = 0; j <= k - 1; j++)
{
u = i * k + j;
c[u] = 0.0;
for (l = 0; l <= n - 1; l++)
c[u] = c[u] + a[i * n + l] * b[l * k + j];
}
}
int main()
{
int i, j;
static double a[4][5] = { { 1.0, 3.0, -2.0, 0.0, 4.0},
{-2.0, -1.0, 5.0, -7.0, 2.0},
{ 0.0, 8.0, 4.0, 1.0, -5.0},
{ 3.0, -3.0, 2.0, -4.0, 1.0}};
static double c[4][3], b[5][3] = { { 4.0, 5.0, -1.0},
{ 2.0, -2.0, 6.0},
{ 7.0, 8.0, 1.0},
{ 0.0, 3.0, -5.0},
{ 9.0, 8.0, -6.0}};
brmul(*a, *b, 4, 5, 3, *c);
for (i = 0; i <= 3; i++)
{
for (j = 0; j <= 2; j++)
printf("%16.7e", c[i][j]);
printf("\n");
}
printf("\n");
return 0;
}
运行结果(VC):
===================================================================
3.2000000e+001 1.5000000e+001 -9.0000000e+000
4.3000000e+001 2.7000000e+001 2.4000000e+001
-1.0000000e+000 -2.1000000e+001 7.7000000e+001
2.9000000e+001 3.3000000e+001 -5.0000000e+000
===================================================================
★ 又在Dev-C++里运行了一下,通过! 最终得出的实矩阵相乘函数为(改了个函数名):
void MatMul(double a[], double b[], int m, int n, int k, double c[])
{
int i, j, t;
int u;
for (i = 0; i<= m - 1; i++)
for (j = 0; j <= k - 1; j++)
{
u = i * k + j;
c[u] = 0.0;
for (t = 0; t <= n - 1; t++)
c[u] = c[u] + a[i * n + t] * b[t * k + j];
}
}
参数说明:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
形参与函数类型 说明
————————————————————————————
double a[m][n] 存放矩阵A的元素
————————————————————————————
double b[n][k] 存放矩阵B的元素
————————————————————————————
int m 矩阵A与乘积矩阵C的行数
————————————————————————————
int n 矩阵A的列数,矩阵B的行数
————————————————————————————
int k 矩阵B与乘积矩阵C的列数
————————————————————————————
double c[m][k] 返回乘积矩阵C=AB的元素
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
调用方法: MatMul(*a, *b, m, n, k, *c);
######################################补充########################################
★ 又琢磨了一下, 函数参数中用的如 double a[] 的形式, 既然没有指定维数, 那实际上也就是传了个地址进去, 于是改写为 double *a 的形式:
void MatMul(double *a, double *b, int m, int n, int k, double *c)
{
int i, j, t;
int u;
for (i = 0; i<= m - 1; i++)
for (j = 0; j <= k - 1; j++)
{
u = i * k + j;
c[u] = 0.0;
for (t = 0; t <= n - 1; t++)
c[u] = c[u] + a[i * n + t] * b[t * k + j];
}
}
再次验证:
#include <stdio.h>
void MatMul(double *a, double *b, int m, int n, int k, double *c)
{
int i, j, t;
int u;
for (i = 0; i<= m - 1; i++)
for (j = 0; j <= k - 1; j++)
{
u = i * k + j;
c[u] = 0.0;
for (t = 0; t <= n - 1; t++)
c[u] = c[u] + a[i * n + t] * b[t * k + j];
}
}
int main()
{
int i, j;
static double a[4][5] = { { 1.0, 3.0, -2.0, 0.0, 4.0},
{-2.0, -1.0, 5.0, -7.0, 2.0},
{ 0.0, 8.0, 4.0, 1.0, -5.0},
{ 3.0, -3.0, 2.0, -4.0, 1.0}};
static double c[4][3], b[5][3] = { { 4.0, 5.0, -1.0},
{ 2.0, -2.0, 6.0},
{ 7.0, 8.0, 1.0},
{ 0.0, 3.0, -5.0},
{ 9.0, 8.0, -6.0}};
MatMul(*a, *b, 4, 5, 3, *c);
for (i = 0; i <= 3; i++)
{
for (j = 0; j <= 2; j++)
printf("%16.7e", c[i][j]);
printf("\n");
}
printf("\n");
return 0;
}
运行结果(VC):
====================================================================
3.2000000e+001 1.5000000e+001 -9.0000000e+000
4.3000000e+001 2.7000000e+001 2.4000000e+001
-1.0000000e+000 -2.1000000e+001 7.7000000e+001
2.9000000e+001 3.3000000e+001 -5.0000000e+000
====================================================================
评论