其他部分

第一章 矩阵

一、矩阵的转置

 

问题描述:

编写函数,把给定的任意一个二维整型矩阵转换为其转置矩阵。

输入:

1 2 3

4 5 6

输出:

1 4

2 5

3 6

分析

题目要求编写一个能转置任意二维矩阵的函数,显然这个函数需要得到一个二维数组的参数,还应该有一个作为输出的二维数组,我们可以将这个数组也传递给函数,然后函数交换行列的下标赋值给输出数组(即b[j][i] = a[i][j])即可。这样看似问题可以得到解决了,但是,二维数组作为参数时必须指定列数,也就是第二维的大小,比如:void transpose ( int a[][3], int b[][2]),那么这个函数就只能将=转置2行3列的矩阵,无法达到题目中转置任意矩阵的要求。

如何让函数具有通用性呢?我们考虑到数组在内存中的存放方式是按行线性连续排列的,比如:

数组

a[2][3] = {

{ 1,2, 3 },

{ 4,5, 6 }

};

b[3][2] = {

{ 1,2 },

{ 3,4 },

{ 5,6 }

};

看起来这两个数组的布局不一样但是它们在内存中的排列方式是相同的:

a:

a[0][0]

a[0][1]

a[0][2]

a[1][0]

a[1][1]

a[1][2]

1

2

3

4

5

6

b:

b[0][0]

b[0][1]

b[1][0]

b[1][1]

b[2][0]

b[2][1]

1

2

3

4

5

6

这样看来,一维数组在某些时候可以代替二维数组,并且最重要的是,一维数组作为参数传递给函数时,不用指定长度,这个正好符合题意。我们能不能使用一维数组完成题目的要求呢?

首先看一下在二维数组中,转置是如何发生的:

1 2 3

4 5 6

转置

→

1 4

2 5

3 6

很明显,在二维视角下,矩阵的转置就是交换行标和列标,即:b[j][i] =a[i][j],而2X3的二维数组中的元素a[i][j],在一维视角下就变为a[i*3+j],转置后的3X2二维数组中对应的元素b[j][i]变为b[j*2+i]。如此a[i*3+j]= b[j*2+i]就可以实现转置了。

至此,我们已经找到了使用一维数组实现二维数组转置的方法,现在看看如何将一个二维数组变成一维数组参数。由于一维数组在作为函数参数时就是一个指向int类型的指针,而二维数组名a是一个指向以为数组的指针,因此*a就是一个一维数组,使用它作为函数参数时也是一个指向int类型的指针。

下面是完整的程序:

/*
* 转置矩阵的通用函数
*/
void transpose ( int *m1, int *m2, int x, int y ); //转置函数
static int m2[3][2]; //自动初始化 int main ( void )
{
int m1[2][3] = {
{1, 2, 3},
{4, 5, 6},
}; int i;
int j; transpose ( *m1, *m2, 2, 3 ); //使用参数*m1、*m2 for ( i=0; i<3; i++ )
{
for ( j=0; j<2; j++ )
printf ( "%3d", m2[i][j] );
printf ( "\n" );
} return 0;
} void transpose ( int *m1, int *m2, int x, int y )
{
int i;
int j; for ( i=0; i<x; i++ )
for ( j=0; j<y; j++ )
m2[j*x+i] = m1[i*y+j];
}

小结:利用二维数组的内存分布特点,可以用一维数组来处理二维数组的问题,并且这种方法还具有通用性。

二、矩阵的乘法

 

问题描述:编写一个实现两个任意矩阵乘法的函数(两个矩阵中一个的行数和另一个的列数相同)

输入:

m1:

2   -6

3    5

1   -1

m2:

4    -2   -4   -5

-7   -3    6    7

输出:

r:

50    14   -44   -52

-23   -21    18    20

11    1    -10   -12

    分析:

    我们首先考虑矩阵乘法的法则:

    就以上这个例子而言,显然有:

    r[0][0] = m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0];

    r[0][1] = m1[0][0] * m2[0][1] + m1[0][1] * m2[1][1];

    ......

    观察发现:以上等式中,所有m1的行标都与r的行标一致,所有m2的列标都与r的列标一致,而m1中和m2中剩下的没有确定的下标都是在遍历,更巧的是,这两个没有确定的下标的遍历范围是一致的。

    我们假设m1的大小为x*y,m2的为y*z,m3自然就应该是x*z,分别用i、j、k遍历x、y、z。那么,对于一个给定了具体下标值的元素r[i][k](假定r中所有元素的初始值为0),在计算此元素的表达式中,所有m1的行标都已经确定为i,所有m2的列标也确定为k,此时只需要使用j来遍历y,并将j作为m1和m2中那个还没有确定的下标值,可以通过遍历i和k来实现遍历所有r的元素,伪代码如下:

    i: 0 → x

        k: 0 → z

            j: 0 → y

            r[i][k] += m1[i][j] * m2[j][k];

    最后,我们仍然要使用一维数组来解决通用性的问题:

    m1[i][j] = m1[i*y+j];

    m2[j][k] = m2[j*z+k];

    r[i][k] = r[i*z+k];

    最终的函数和测试程序如下:

/*
* 矩阵乘法
*/
#include <stdio.h>
void multiply ( int *m1, int *m2, int *r, int x, int y, int z ); static int r[3][4]; int main ( void )
{
int m1[3][2] = {
{2, -6},
{3, 5},
{1, -1}
}; int m2[2][4] = {
{4, -2, -4, -5},
{-7, -3, 6, 7}
}; int i,j; multiply ( *m1, *m2, *r, 3, 2, 4 ); for ( i=0; i<3; i++ )
{
for ( j=0; j<4; j++ )
printf ( "%5d", r[i][j] );
printf ( "\n" );
} return 0;
} void multiply ( int *m1, int *m2, int *r, int x, int y, int z )
{
int i, j, k; for ( i=0; i<x; i++ )
for ( j=0; j<y; j++ )
for ( k=0; k<z; k++ )
r[i*z+k] += m1[i*y+j] * m2[j*z+k];
} /*
* 测试结果:
*
* 50 14 -44 -52
* -23 -21 18 20
* 11 1 -10 -12
*
*/

    小结:注意分析乘法法则,得到下标值之间的关系。

声明

1、此博文版权归断絮所有,如需转载请注明出处http://blog.csdn.net/duanxu_yzc/article/details/12049955,谢谢。

2、在未经博主断絮允许的情况下,任何个人和机构都不得以任何理由出版此文章,否则必将追究法律责任!

[置顶] 【其他部分 第一章 矩阵】The C Programming Language 程序研究 【持续更新】的更多相关文章

  1. 第一章 “我要点爆”微信小程序云开发之项目建立与我的页面功能实现

    第一章 “我要点爆”微信小程序云开发之项目建立与我的页面功能实现 开发环境搭建 使用自己的AppID新建小程序项目,后端服务选择小程序·云开发,点击新建,完成项目新建. 新建成功后跳转到开发者工具界面 ...

  2. 【第一章】 第一个spring boot程序

    环境: jdk:1.8.0_73 maven:3.3.9 spring-boot:1.2.5.RELEASE(在pom.xml中指定了) 注意:关于spring-boot的支持, 最少使用jdk7(j ...

  3. 我们曾经心碎的C#之 第一章.我的第一个C#程序

    第一章.      C#入门 1.1        .NET与C#            001..NET是Microsoft.NET的简称,是基于Windows平台的一种技术            ...

  4. php将数组中某个元素置顶设为第一个元素

    一个数组$a0有N个元素,要将其中第3个元素,排在数组的首位. 第一种做法是: 取出第3个元素,赋值给变量$a unset 第3个元素 array_unshift 将$a添加到数组头部. 如果是数字下 ...

  5. [置顶] CopyU!v2插件合集 [2013年7月18日更新]

    这里提供了所有可供CopyU!v2使用的功能插件,您可以根据自己的需要下载安装使用,需要提醒您的是,安装过多的插件会影响CopyU!的运行性能,请合理的安装使用! 1.打包插件 版本:1.0.12.1 ...

  6. 第一章开发简单的Java应用程序

    1.什么是程序? 程序一词来源于生活,通俗点讲就是把生活的的事用程序编写出来 并执行. 2.为什么要学习Java呢? Java是Sun Microsystems于1995年推出的高级编程语言 Java ...

  7. CLR Via 第一 章 知识点整理(2)程序集和CLR的启动

    这一节先简单的讨论一下程序集以及CLR的初始化 虽然对应的编译器会生成托管模块,但实际上CLR不与托管模块工作,编译器除了编译还有将生成的托管模块转换为程序集的功能,微软还提供了工具AL.exe(程序 ...

  8. 第一章 C#入门 (Windows窗体应用程序)(三)

    [案例] 编写一个Windows窗体应用程序,窗体上有一个文本框和两个按钮([显示]和[清除]按钮). 单击[显示]时,文本框的背景变为蓝色并且居中显示“努力学习C#”: 单击[清除]按钮,文本框的背 ...

  9. 第一章 C#入门(Windows窗体应用程序)(二)

    C#窗体应用程序(二) [案例]设计登录界面,效果如下: [案例实现步骤] 1.新建项目(Windows控制台应用程序 文件→新建→项目:选择“项目类型”为Visual C#,“模板”为Windows ...

随机推荐

  1. java DI 框架spring(web)、Guice(web)、Dagger&Dagger2(android)

    java DI 框架spring(web).Guice(web).Dagger&Dagger2(android) (待续...)

  2. text-overflow简单使用

    text-overflow属性配合overflow才有效果,还记得把文字强制一行显示,如下代码: <!DOCTYPE html> <html lang="zh-cn&quo ...

  3. Object-c学习之路十二(OC的copy)

    oc中的拷贝分为:copy(浅拷贝)和mutablecopy(深拷贝). 浅拷贝也为指针拷贝,拷贝后原来的对象计数器会+1: 深拷贝为对象拷贝,原来的对象计数器不变. 注意:自定义对象拷贝时要实现NS ...

  4. JavaScript 滚动页面到指定元素位置

    页面评论功能,当评论较多时,有时须要滚动到评论头部. 能够使用scrollTop方法,加上一点延时动画(animate),可訪问在线演示,代码大体例如以下: <html> <scri ...

  5. [RxJS] Handling a Complete Stream with Reduce

    When a stream has completed, you often need to evaluate everything that has happened while the strea ...

  6. 执行eclipse,迅速failed to create the java virtual machine。

    它们必须在一排,否则会出现The Eclipse executable launcher was unable to locate its companion shared library的错误 打开 ...

  7. 基于注解的Spring MVC

    1.加入�jar 2.web.xml配置: <?xml version="1.0" encoding="UTF-8"?> <web-app v ...

  8. JavaScripts学习日记——ECMAscript

    1.Function对象 Function是一个很特殊的对象,特殊在该对象就像java中的方法一样,可以运行,可以传参数. 三种定义function对象的方法: 1.function fun1(a,b ...

  9. unison实时双向数据同步

    软件下载 ocamlopt下载地址:http://caml.inria.fr Unison下载地址:http://www.seas.upenn.edu/~bcpierce/unison 1.安装uni ...

  10. silverlight+wcf 项目 silverlight获得web程序的参数

    silverlight 可以通过属性InitParams 获得参数,如果参数是动态的需要web程序传递的,具体操作如下: web程序后台:AppID,USERID需要的参数 this.frmRepor ...