矩阵,简单的运行技巧

对于矩阵乘法 C =  A× B,通常的做法是将矩阵进行分块相乘,如下图所示:

 

 

 

从上图可以看出这种分块相乘总共用了8次乘法,当然对于子矩阵相乘(如A0×B0),还可以继续递归使用分块相乘。对于中小矩阵来说,很适合使用这种分块乘法,但是对于大矩阵来说,递归的次数较多,如果能减少每次分块乘法的次数,那么性能将可以得到很好的提高。

Strassen矩阵乘法就是采用了一个简单的运算技巧,将上面的8次矩阵相乘变成了7次乘法,看别小看这减少的1次乘法,因为每递归1次,性能就提高了1/8,比如对于1024*1024的矩阵,第1次先分解成7512*512的矩阵相乘,对于512*512的矩阵,又可以继续递归分解成256*256的矩阵相乘,,一直递归下去,假设分解到64*64的矩阵大小后就不再递归,那么所花的时间将是分块矩阵乘法的(7/8) * (7/8) * (7/8) * (7/8) = 0.586倍,提高了快接近一倍。当然这是理论上的值,因为实际上strassen乘法增加了其他运算开销,实际性能会略低一点。

下面就是Strassen矩阵乘法的实现方法,

   M1 = (A0 + A3) × (B0 + B3)

   M2 = (A2 + A3) × B0

   M3 = A0 × (B1 - B3)

   M4 = A3 × (B2 - B0)

   M5 = (A0 + A1) × B3

   M6 = (A2 - A0) × (B0 + B1)

   M7 = (A1 - A3) × (B2 + B3)

   C0 = M1 + M4 - M5 + M7

   C1 = M3 + M5

   C2 = M2 + M4

   C3 = M1 - M2 + M3 + M6

在求解M1,M2,M3,M4,M5,M6,M7时需要使用7次矩阵乘法,其他都是矩阵加法和减法。

下面看看Strassen矩阵乘法的串行实现伪代码:

Serial_StrassenMultiply(A, B, C) 

{

   T1 = A0 + A3;

   T2 = B0 + B3;

   StrassenMultiply(T1, T2, M1);

   T1 = A2 + A3;

   StrassenMultiply(T1, B0, M2);

   T1 = (B1 - B3);

   StrassenMultiply (A0, T1, M3);

 

   T1 = B2 - B0;

   StrassenMultiply(A3, T1, M4);

 

   T1 = A0 + A1;

   StrassenMultiply(T1, B3, M5);       

   

   T1 = A2 – A0;

   T2 = B0 + B1;

   StrassenMultiply(T1, T2, M6);

   T1 = A1 – A3;

   T2 = B2 + B3;

   StrassenMultiply(T1, T2, M7);

   C0 = M1 + M4 - M5 + M7

   C1 = M3 + M5

   C2 = M2 + M4

   C3 = M1 - M2 + M3 + M6

}

本文转载自http://software.intel.com/zh-cn/blogs/2009/12/08/400002843/?cid=sw:prccsdn893

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于MPI的高阶矩阵求逆的简单实现,仅供参考: ``` #include <stdio.h> #include <stdlib.h> #include <mpi.h> #define N 9 // 矩阵阶数 void print_matrix(double *matrix) { int i, j; for (i = 0; i < N; i++) { for (j = 0; j < N; j++) { printf("%f ", matrix[i*N+j]); } printf("\n"); } } void print_vector(double *vector) { int i; for (i = 0; i < N; i++) { printf("%f ", vector[i]); } printf("\n"); } void gauss_jordan(double *matrix, double *inv_matrix) { int i, j, k; double pivot, temp; // 初始化逆矩阵 for (i = 0; i < N; i++) { for (j = 0; j < N; j++) { inv_matrix[i*N+j] = (i==j)?1:0; } } // 高斯-约旦消元 for (i = 0; i < N; i++) { pivot = matrix[i*N+i]; if (pivot == 0) { printf("矩阵不可逆!\n"); return; } // 主元归一化 for (j = 0; j < N; j++) { matrix[i*N+j] /= pivot; inv_matrix[i*N+j] /= pivot; } // 消元 for (j = 0; j < N; j++) { if (j == i) continue; temp = matrix[j*N+i]; for (k = 0; k < N; k++) { matrix[j*N+k] -= temp*matrix[i*N+k]; inv_matrix[j*N+k] -= temp*inv_matrix[i*N+k]; } } } } int main(int argc, char *argv[]) { int rank, size, i, j; double *matrix, *inv_matrix, start_time, end_time; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); if (rank == 0) { printf("请输入一个%d阶矩阵:\n", N); matrix = (double*)malloc(N*N*sizeof(double)); for (i = 0; i < N; i++) { for (j = 0; j < N; j++) { scanf("%lf", &matrix[i*N+j]); } } printf("原矩阵:\n"); print_matrix(matrix); } // 矩阵分割 int local_n = N/size; double *local_matrix = (double*)malloc(local_n*N*sizeof(double)); double *local_inv_matrix = (double*)malloc(local_n*N*sizeof(double)); MPI_Scatter(matrix, local_n*N, MPI_DOUBLE, local_matrix, local_n*N, MPI_DOUBLE, 0, MPI_COMM_WORLD); // 高斯-约旦消元求逆矩阵 MPI_Barrier(MPI_COMM_WORLD); start_time = MPI_Wtime(); gauss_jordan(local_matrix, local_inv_matrix); MPI_Barrier(MPI_COMM_WORLD); end_time = MPI_Wtime(); // 矩阵合并 MPI_Gather(local_inv_matrix, local_n*N, MPI_DOUBLE, inv_matrix, local_n*N, MPI_DOUBLE, 0, MPI_COMM_WORLD); if (rank == 0) { printf("逆矩阵:\n"); print_matrix(inv_matrix); printf("运行时间:%f秒\n", end_time-start_time); } MPI_Finalize(); return 0; } ``` 需要注意的一些细节: 1. 程序中使用了MPI的Scatter和Gather函数将矩阵分割和合并,使用MPI_Wtime函数计算程序运行时间; 2. 高斯-约旦消元的逆矩阵部分使用了一些数学技巧,例如主元归一化和消元,需要仔细理解并实现; 3. 程序中没有进行数值精度的考虑,实际使用中需要进行一定的优化和测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值