1. 程式人生 > >我的C語言矩陣庫01

我的C語言矩陣庫01

這裡實現的矩陣庫是將矩陣都分配在棧記憶體中的,這使得我在進行較大量的矩陣運算時將棧給撐爆了,所以更好的辦法是使用malloc動態分配記憶體,改進的矩陣庫點選:
http://blog.csdn.net/Egean/article/details/78387277
以下是原文。

由於要在stm32上實現矩陣運算,所以結合網上程式碼實現了一個C語言矩陣庫,進行一些矩陣的基本運算,包括:轉置,加減,乘法,求逆,拼接等,測試環境是MDK5。先給出下載地址:點選這裡
首先是標頭檔案math_matrix.h:

#ifndef _MATRIX_H_
#define _MATRIX_H_
#include "sys.h"
struct matrix_t{ float *m; u8 row; u8 column; }; #define MATRIX_INIT(a,b,c,d) \ a.m=b; \ a.row=c; \ a.column=d int8_t matrix_t_T(struct matrix_t *A, const struct matrix_t *B); void matrix_t_show(struct matrix_t *M); int8_t matrix_t_plus(struct
matrix_t *A, const struct matrix_t *B, const struct matrix_t *C, int8_t mode); int8_t matrix_t_mul(struct matrix_t *A, const struct matrix_t *B, const struct matrix_t *C, int8_t mode); int8_t matrix_t_inv(struct matrix_t *A, const struct matrix_t *B); int8_t matrix_t_copy(struct
matrix_t *A, const struct matrix_t *B); int8_t matrix_t_eye(struct matrix_t *A); int8_t matrix_t_k(struct matrix_t *A, float k, const struct matrix_t *B); int8_t matrix_t_concat(struct matrix_t *A, const struct matrix_t *B, const struct matrix_t *C, u8 mode); int8_t matrix_t_transport(struct matrix_t *A, const struct matrix_t *B, u8 x1, u8 x2, u8 y1, u8 y2); #endif

struct matrix_t為矩陣結構體,其中matrix_t.m指向一個二維陣列,也就是我們的矩陣。由於C語言中二維陣列作為函式傳參必須指定列數,就像這樣:

void fun(float a[][10])
{
    ...
}

這種傳參限制了矩陣的使用,所以這裡的二維陣列都使用一維陣列進行索引,具體索引方法見math_matrix.c檔案的實現。
需要注意的是matrix_t.m是一個指標,它指向一個二維陣列,而不是matrix_t中包含了一個二維陣列,這麼設計的原因是因為矩陣大小是不定的,若放到matrix_t中則結構體的大小也是不定的,而C語言不能根據陣列的大小動態改變結構體的大小,一個結構體在定義它的時候,它的大小已經固定了,因此只能使用這種指標的形式。這就意味著在初始化時必須預先定義好一個二維陣列,然後將matrix_t.m指向它。這裡的MATRIX_INIT巨集的作用就是如此,將定義好的二維陣列傳入,就能夠方便的初始化matrix_t結構體。

下面是math_matrix.c的具體實現:

/* 基本的矩陣運算,使用結構體,一部分來源於網路:
* http://blog.csdn.net/linaijunix/article/details/50358617
* 做了一些更改,將所有的二重指標換為了一重指標,資料型別做了一些替換,
* 並重新定義了一些函式以支援結構體的運算,函式傳參中不需要傳入行列數了,
* 而且運算之前進行了行列數的檢查,當行列數不符合運算規則時直接返回負數
*   2017/10/23      by colourfate
*/
#include "math_matrix.h"
#include "sys.h"
#include <math.h>
#include <stdio.h>

static void matrix_T(float *a_matrix, const float *b_matrix, u16 krow, u16 kline)  
////////////////////////////////////////////////////////////////////////////  
//  a_matrix:轉置後的矩陣  
//  b_matrix:轉置前的矩陣  
//  krow    :行數  
//  kline   :列數  
////////////////////////////////////////////////////////////////////////////  
{  
    int k, k2;     

    for (k = 0; k < krow; k++)  
    {  
        for(k2 = 0; k2 < kline; k2++)  
        {  
            //a_matrix[k2][k] = b_matrix[k][k2];
            a_matrix[k2*krow+k] = b_matrix[k*kline+k2];
        }  
    }  
}

static void matrix_plus(float *a_matrix, const float *b_matrix, const float *c_matrix,   
                    u8 krow, u8 kline, int8_t ktrl)  
////////////////////////////////////////////////////////////////////////////  
//  a_matrix=b_matrix+c_matrix  
//   krow   :行數  
//   kline  :列數  
//   ktrl   :大於0: 加法  不大於0:減法  
////////////////////////////////////////////////////////////////////////////  
{  
    int k, k2;  

    for (k = 0; k < krow; k++)  
    {  
        for(k2 = 0; k2 < kline; k2++)  
        {  
            //a_matrix[k][k2] = b_matrix[k][k2]  
            //  + ((ktrl > 0) ? c_matrix[k][k2] : -c_matrix[k][k2]);   
            a_matrix[k*kline+k2] = b_matrix[k*kline+k2]  
                + ((ktrl > 0) ? c_matrix[k*kline+k2] : -c_matrix[k*kline+k2]); 
        }  
    }  
}

static void matrix_mul(float *a_matrix, const float *b_matrix, const float *c_matrix,  
                u8 krow, u8 kline, u8 kmiddle, int8_t ktrl)  
////////////////////////////////////////////////////////////////////////////  
//  a_matrix=b_matrix*c_matrix  
//  krow  :b的行數  
//  kline :c的列數
//  kmiddle: b的列數和c的行數              
//  ktrl  : 大於0:兩個正數矩陣相乘 不大於0:正數矩陣乘以負數矩陣  
////////////////////////////////////////////////////////////////////////////  
{  
    int k, k2, k4;  
    float stmp;  

    for (k = 0; k < krow; k++)       
    {  
        for (k2 = 0; k2 < kline; k2++)     
        {  
            stmp = 0.0;  
            for (k4 = 0; k4 < kmiddle; k4++)    
            {  
                //stmp += b_matrix[k][k4] * c_matrix[k4][k2]; 
                stmp += b_matrix[k*kmiddle+k4] * c_matrix[k4*kline+k2]; 
            }  
            //a_matrix[k][k2] = stmp;  
            a_matrix[k*kline+k2] = stmp;
        }  
    }  
    if (ktrl <= 0)     
    {  
        for (k = 0; k < krow; k++)  
        {  
            for (k2 = 0; k2 < kline; k2++)  
            {  
                //a_matrix[k][k2] = -a_matrix[k][k2]; 
                a_matrix[k*kline+k2] = -a_matrix[k*kline+k2];               
            }  
        }  
    }  
}


static u8 matrix_inv(float *a_matrix, u8 ndimen)  
////////////////////////////////////////////////////////////////////////////  
//  a_matrix:矩陣  
//  ndimen :維數  
////////////////////////////////////////////////////////////////////////////  
{  
    float tmp, tmp2, b_tmp[20], c_tmp[20];  
    int k, k1, k2, k3, j, i, j2, i2, kme[20], kmf[20];  
    i2 = j2 = 0;  

    for (k = 0; k < ndimen; k++)    
    {  
        tmp2 = 0.0;  
        for (i = k; i < ndimen; i++)    
        {  
            for (j = k; j < ndimen; j++)    
            {  
                //if (fabs(a_matrix[i][j] ) <= fabs(tmp2))   
                if (fabs(a_matrix[i*ndimen+j] ) <= fabs(tmp2))
                    continue;  
                //tmp2 = a_matrix[i][j];  
                tmp2 = a_matrix[i*ndimen+j]; 
                i2 = i;  
                j2 = j;  
            }    
        }  
        if (i2 != k)   
        {  
            for (j = 0; j < ndimen; j++)     
            {  
                //tmp = a_matrix[i2][j];  
                //a_matrix[i2][j] = a_matrix[k][j];  
                //a_matrix[k][j] = tmp;  
                tmp = a_matrix[i2*ndimen+j]; 
                a_matrix[i2*ndimen+j] = a_matrix[k*ndimen+j];
                a_matrix[k*ndimen+j] = tmp;
            }  
        }  
        if (j2 != k)   
        {  
            for (i = 0; i < ndimen; i++)    
            {  
                //tmp = a_matrix[i][j2];  
                //a_matrix[i][j2] = a_matrix[i][k];  
                //a_matrix[i][k] = tmp;  
                tmp = a_matrix[i*ndimen+j2];  
                a_matrix[i*ndimen+j2] = a_matrix[i*ndimen+k];  
                a_matrix[i*ndimen+k] = tmp;  
            }      
        }  
        kme[k] = i2;  
        kmf[k] = j2;  
        for (j = 0; j < ndimen; j++)    
        {  
            if (j == k)     
            {  
                b_tmp[j] = 1.0 / tmp2;  
                c_tmp[j] = 1.0;  
            }  
            else   
            {  
                //b_tmp[j] = -a_matrix[k][j] / tmp2;  
                //c_tmp[j] = a_matrix[j][k];  
                b_tmp[j] = -a_matrix[k*ndimen+j] / tmp2;  
                c_tmp[j] = a_matrix[j*ndimen+k];
            }  
            //a_matrix[k][j] = 0.0;  
            //a_matrix[j][k] = 0.0;
            a_matrix[k*ndimen+j] = 0.0;  
            a_matrix[j*ndimen+k] = 0.0;             
        }  
        for (i = 0; i < ndimen; i++)    
        {  
            for (j = 0; j < ndimen; j++)    
            {  
                //a_matrix[i][j] = a_matrix[i][j] + c_tmp[i] * b_tmp[j];  
                a_matrix[i*ndimen+j] = a_matrix[i*ndimen+j] + c_tmp[i] * b_tmp[j];  
            }    
        }  
    }  
    for (k3 = 0; k3 < ndimen;  k3++)     
    {  
        k  = ndimen - k3 - 1;  
        k1 = kme[k];  
        k2 = kmf[k];  
        if (k1 != k)     
        {  
            for (i = 0; i < ndimen; i++)    
            {  
                //tmp = a_matrix[i][k1];  
                //a_matrix[i][k1] = a_matrix[i][k];  
                //a_matrix[i][k] = tmp;  
                tmp = a_matrix[i*ndimen+k1];  
                a_matrix[i*ndimen+k1] = a_matrix[i*ndimen+k];  
                a_matrix[i*ndimen+k] = tmp; 
            }    
        }  
        if (k2 != k)     
        {  
            for(j = 0; j < ndimen; j++)    
            {  
                //tmp = a_matrix[k2][j];  
                //a_matrix[k2][j] = a_matrix[k][j];  
                //a_matrix[k][j] = tmp;  
                tmp = a_matrix[k2*ndimen+j];  
                a_matrix[k2*ndimen+j] = a_matrix[k*ndimen+j];  
                a_matrix[k*ndimen+j] = tmp;  
            }  
        }  
    }  
    return (0);  
}
/* 矩陣拷貝函式,A = B,兩矩陣行列必須相同
* @A: 目標矩陣
* @B: 源矩陣
* @row: A和B的行數
* @colum: A和B的列數
*/
static void matrix_copy(float *A, const float *B, u8 row, u8 column)
{
    int i,j;
    for(i=0; i<row; i++){
        for(j=0; j<column; j++){
            A[column*i+j] = B[column*i+j];
        }
    }
}


/* 生成單位矩陣
* @A: 生成的單位矩陣
* @dimen: 矩陣維度
*/
static void matrix_eye(float *A, u8 dimen)
{
    int i,j;
    for(i=0; i<dimen; i++){
        for(j=0; j<dimen; j++){
            if(i==j){
                A[dimen*i+j] = 1;
            }else{
                A[dimen*i+j] = 0;
            }
        }
    }
}

/* 常數乘以一個矩陣,A = k * B
* @A: 目標矩陣
* @B: 源矩陣
* @k: 係數k
* @row: B的行數
* @column: B的列數
*/
static void matrix_k(float *A, float k, const float *B, u8 row, u8 column)
{
    int i,j;
    for(i=0; i<row; i++){
        for(j=0; j<column; j++){
            A[column*i+j] = k * B[column*i+j];
        }
    }
}

/* 矩陣拼接函式,兩矩陣必須列數相等
* vertical: A = |B|,horizontal: A = |B C|
*               |C|
* @A: 目標矩陣
* @B: 源矩陣1
* @C: 源矩陣2
* @a_row, a_column, b_row, b_column: B,C矩陣的行數和列數
* @mode: 為1表示豎向拼接,為0表示橫向拼接
@ return: 非零表示拼接失敗,0表示成功
*/
static int8_t matrix_concat(float *A, const float *B, const float *C, 
            u8 b_row, u8 b_column, u8 c_row, u8 c_column, int8_t mode)
{
    int i, j, k;
    if(mode == 0){
        if(b_row != c_row){
            return -1;
        }
        for(i=0; i<b_row; i++){
            for(j=0, k=0; j<b_column; j++, k++){
                A[(b_column+c_column)*i+k] = B[b_column*i+j];
            }
            for(j=0; j<c_column; j++, k++){
                A[(b_column+c_column)*i+k] = C[c_column*i+j];
            }
        }
    }else if(mode == 1){
        if(b_column != c_column){
            return -1;
        }
        matrix_copy(A, B, b_row, b_column);
        matrix_copy(A+b_row*b_column, C, c_row, c_column);
    }else{
        return -2;
    }
}

/* 顯示一個矩陣 
* @M: 要顯示的矩陣
* @row: M的行數
* @colum: M的列數
*/
static void matrix_show(float *M, u8 row, u8 column)
{
    int i,j;
    for(i=0; i<row; i++){
        printf("|");
        for(j=0; j<column; j++){
            printf("%f ", *(M+column*i+j));
        }
        printf("|\r\n");
    }
}

/* A = B的轉置,A的行數必須等於B的列數,A的列數必須等於B的行數 
* @A:轉置後的矩陣  
* @B:轉置前的矩陣  
* return: 返回0表示成功,返回非零表示失敗
*/
int8_t matrix_t_T(struct matrix_t *A, const struct matrix_t *B)  
{  
    if(A->column != B->row || A->row != B->column){
        return -2;
    }
    matrix_T(A->m, B->m, B->row, B->column);
    return 0;
}

void matrix_t_show(struct matrix_t *M)
{
    matrix_show(M->m, M->row, M->column);
}

/* A = B + C,B和C的行列數必須相等
* @mode: 大於0為加法,小於零為減法
*/
int8_t matrix_t_plus(struct matrix_t *A, const struct matrix_t *B, 
            const struct matrix_t *C, int8_t mode)
{
    if(B->row != C->row || B->column != C->column){
        return -1;
    }
    if(A->row != B->row || A->column != B->column){
        return -2;
    }
    matrix_plus(A->m, B->m, C->m, B->row, B->column, mode);
    return 0;
}

/* A = BC, B的列數必須等於C的行數
* @mode: 大於0:兩個正數矩陣相乘 不大於0:正數矩陣乘以負數矩陣
*/
int8_t matrix_t_mul(struct matrix_t *A, const struct matrix_t *B, 
            const struct matrix_t *C, int8_t mode)
{
    if(B->column != C->row){
        return -1;
    }
    if(A->row != B->row || A->column != C->column){
        return -2;
    }
    matrix_mul(A->m, B->m, C->m, B->row, C->column, B->column, mode);
    return 0;
}

/* A = B的逆, B必須是方陣
*/
int8_t matrix_t_inv(struct matrix_t *A, const struct matrix_t *B)
{
    if(B->row != B->column){
        return -1;
    }
    if(A->row != B->row || A->column != B->column){
        return -2;
    }
    matrix_copy(A->m, B->m, B->row, B->column);
    matrix_inv(A->m, A->row);
    return 0;
}

/* A = B
*/
int8_t matrix_t_copy(struct matrix_t *A, const struct matrix_t *B)
{
    if(A->row != B->row || A->column != B->column){
        return -2;
    }
    matrix_copy(A->m, B->m, B->row, B->column);
    return 0;
}

int8_t matrix_t_eye(struct matrix_t *A)
{
    if(A->row != A->column){
        return -2;
    }
    matrix_eye(A->m, A->row);
    return 0;
}

/* A = kB
*/
int8_t matrix_t_k(struct matrix_t *A, float k, const struct matrix_t *B)
{
    if(A->row != B->row || A->column != B->column){
        return -2;
    }
    matrix_k(A->m, k, B->m, B->row, B->column);
    return 0;
}

int8_t matrix_t_concat(struct matrix_t *A, const struct matrix_t *B,
                const struct matrix_t *C, u8 mode)
{
    return matrix_concat(A->m, B->m, C->m, B->row, B->column, C->row, C->column, mode);
}

/* A = B(x1:x2, y1:y2)
*/
int8_t matrix_t_transport(struct matrix_t *A, const struct matrix_t *B, 
                u8 x1, u8 x2, u8 y1, u8 y2)
{
    int i,j;
    if(x1>x2 || y1>y2){
        return -1;
    }
    if(A->row != x2-x1+1 || A->column != y2-y1+1){
        return -2;
    }
    for(i=0; i<A->row; i++){
        for(j=0; j<A->column; j++){
            A->m[i*A->column+j] = B->m[(x1+i)*B->column+y1+j];
        }
    }
    return 0;
}

使用方法如下:

#include "math_matrix.h"
int main(void)
{
    float A[2][2];
    float B[2][2] = {
        {1.0, 2.0},
        {3.0, 4.0}
    };
    struct matrix_t AA, BB;
    MATRIX_INIT(AA, *A, 2, 2);
    MATRIX_INIT(BB, *B, 2, 2);
    // AA = BB
    matrix_t_T(&AA, &BB);
    matrix_t_show(&AA);
}

需要注意的是這裡的

MATRIX_INIT(AA, *A, 2, 2);

需要將二維陣列A解引用一次,因為A的資料型別是陣列指標,指向一個數組,解引用一
次後就得到了該陣列的首地址。當然這只是為了讓編譯器不報錯的措施,不管是A還是*A它們的值實際上是相同的,所以這樣也是可以的:

MATRIX_INIT(AA, (float *)A, 2, 2);