正文

[057] 实矩阵相乘函数2006-05-20 15:47:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/wentao/14373.html

分享到:

最近频繁用到矩阵相乘运算, 所以想写一个函数。遇到的问题不少!


关于矩阵乘法规则,请查看: [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
====================================================================

阅读(4696) | 评论(5)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

loading...
您需要登录后才能评论,请 登录 或者 注册