如何为矩阵乘法器C程序获得未知矩阵的适当尺寸?

时间:2018-12-01 22:23:54

标签: c matrix matrix-multiplication

所以我的尝试是创建一个程序,该程序从.txt文件中自动获取两个矩阵的大小并将它们相乘。我可以将程序制作成给定的大小,所以它本身在计算列数和行数时只遇到问题。

输入类似(MxN矩阵):

1 2 3 4

1 2 3 4

1 2 3 4

具体来说,这是到目前为止的程序(我认为代码的开头与之相关):

#include <stdio.h>
#include <stdlib.h>

struct mat1
{
    int cols;
    int rows;
};

struct mat2
{
    int cols;
    int rows;
};

struct mat1 dim1(const char* file)
{
    struct mat1 m1;
    int rows = 0;
    int cols = 0;
    char c;
    FILE *f = fopen(file, "r+");

    while((c = fgetc(f) != EOF))
    {
        if(c != '\n' && rows == 0)
           {
            cols++;
           }
           else if(c == '\n')
            rows++;
    }
    rows++;
    return m1;
}

struct mat2 dim2(const char* file)
{
    struct mat2 m2;
    int rows = 0;
    int cols = 0;
    char c;
    FILE *f = fopen(file, "r+");

    while((c = fgetc(f) != EOF))
    {
        if(c != '\n' && rows == 0)
           {
            cols++;
           }
           else if(c == '\n')
            rows++;
    }
    rows++;
    return m2;
}


double* alloc_matrix(int cols, int rows) {
    double* m = (double*)malloc(cols * rows * sizeof(double));
    if (m == 0) {
        printf("Memory allocation error.\n");
        exit(-1);
    }
    return m;
}

void read_matrix(FILE* f, double* m, int cols, int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            fscanf(f, "%lf", &m[i * cols + j]);
        }
    }
}

void multiplication(double* m1, double* m2, double* m3, int cols, int rows) {
    for(int i = 0; i < rows; i++) {
        for(int j = 0; j < cols; j++) {
            m3[i * cols +j]=0;
            for(int k = 0; k < cols; k++) {
                m3[i * cols +j]+=m1[i * cols +k]*m2[k * cols +j];
            }
        }
    }
}

void write_matrix(double* m, int cols, int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%f ", m[i * cols + j]);
        }
        printf("\n");
    }
}


int main(int argc, char* argv[])
{
    char* matrix1 = argv[1];
    char* matrix2 = argv[2];

        if (argc < 3) {
        printf("Not enough arguments.\n");
        exit(-1);
    }

    struct mat1 m1 = dim1(matrix1);
    struct mat2 m2 = dim2(matrix2);
    printf(" %d   %d \n", m1.cols, m1.rows);
    printf(" %d   %d \n", m2.cols, m2.rows);
    int c1 = m1.cols;
    int r1 = m1.rows;
    int c2 = m2.cols;
    int r2 = m2.rows;

    if (r1!=c2)
    {
        printf("Matrixes are not suitable for multiplication. \n");
        exit(-1);
    }

    double* mtx1 = alloc_matrix(c1, r1);
    double* mtx2 = alloc_matrix(c2, r2);

    FILE* f1 = fopen(matrix1, "r");
    if (f1 == 0)
    {
        printf("Cannot open file %s.", argv[1]);
        exit(-1);
    }

    FILE* f2 = fopen(matrix2, "r");
    if (f1 == 0)
    {
        printf("Cannot open file %s.", argv[1]);
        exit(-1);
    }

    read_matrix(f1, mtx1, c1, r1);
    read_matrix(f2, mtx2, c2, r2);

    double* mtx3 = alloc_matrix(c1, r2);

    multiplication(mtx1, mtx2, mtx3, c1, r2);

    write_matrix(mtx3, c1, r2);

    free(mtx1);
    free(mtx2);
    free(mtx3);
    fclose(f1);
    fclose(f2);

    return 0;
}

当我尝试使用2个3x3矩阵进行测试时,仓库:

6422164 4199040 (来自我设置用于检查尺寸的2个printf())。

6422164 4199040

矩阵不适合乘法。(无关紧要)

所以基本上它不使用3x3。

我不知道是什么问题。

1 个答案:

答案 0 :(得分:1)

这是我的主要评论开头。

我必须重构dim来处理任意大的矩阵,因此我必须逐个字符地扫描文件的第一行,并计算空格字符串(得出的列数为1)。它处理/剥离前导/尾随空格[格式错误]

我有dim然后倒带文件,并分别使用fscanfrealloc动态创建矩阵。


这是工作代码[请原谅免费的样式清理]:

#include <stdio.h>
#include <stdlib.h>

struct mat {
    int cols;
    int rows;
    double *m;
};

// size and read in matrix
struct mat
dim(const char *file)
{
    struct mat m;
    int rows = 0;
    int cols = 0;
    int maxcnt;
    int curcnt;
    int ret;
    int c;
    int c2;

    FILE *f = fopen(file, "r+");

    // strip leading whitespace [if any] off first line
    while (1) {
        c = fgetc(f);
        if (c == EOF)
            break;
        if (c == '\n')
            break;
        if (c != ' ')
            break;
    }

    // scan first line and count columns (number of space separaters)
    while (1) {
        c2 = ' ';
        while (1) {
            c = fgetc(f);
            if (c == EOF)
                break;

            if (c == '\n') {
                if (c2 != ' ')
                    ++cols;
                break;
            }

            if (c == ' ') {
                if (c != c2)
                    ++cols;
                break;
            }

            c2 = c;
        }

        if (c == '\n')
            break;
    }

    // convert number of whitespace separaters into number of columns
    if (cols > 0)
        ++cols;

    rewind(f);

    m.rows = 0;
    m.cols = cols;
    m.m = NULL;
    curcnt = 0;
    maxcnt = 0;

    while (1) {
        if (curcnt >= maxcnt) {
            maxcnt += m.cols * 100;
            double *tmp = realloc(m.m,sizeof(double) * maxcnt);
            if (tmp == NULL) {
                printf("dim: realloc failure\n");
                exit(1);
            }
            m.m = tmp;
        }

        ret = 0;
        for (int idx = 0;  idx < cols;  ++idx, ++curcnt) {
            ret = fscanf(f, "%lf", &m.m[curcnt]);
            if (ret != 1)
                break;
        }

        if (ret != 1)
            break;

        rows += 1;
    }

    fclose(f);

    m.rows = rows;

    // trim matrix to actual size;
    m.m = realloc(m.m,sizeof(double) * rows * cols);

    return m;
}

double *
alloc_matrix(int cols, int rows)
{
    double *m = (double *) malloc(cols * rows * sizeof(double));

    if (m == 0) {
        printf("Memory allocation error.\n");
        exit(-1);
    }
    return m;
}

void
multiplication(double *m1, double *m2, double *m3, int cols, int rows)
{
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            m3[i * cols + j] = 0;
            for (int k = 0; k < cols; k++) {
                m3[i * cols + j] += m1[i * cols + k] * m2[k * cols + j];
            }
        }
    }
}

void
write_matrix(double *m, int cols, int rows)
{
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%f ", m[i * cols + j]);
        }
        printf("\n");
    }
}

int
main(int argc, char *argv[])
{

    if (argc < 3) {
        printf("Not enough arguments.\n");
        exit(1);
    }

    struct mat m1 = dim(argv[1]);
    struct mat m2 = dim(argv[2]);

    printf(" %d   %d \n", m1.cols, m1.rows);
    printf(" %d   %d \n", m2.cols, m2.rows);

    int c1 = m1.cols;
    int r1 = m1.rows;
    int c2 = m2.cols;
    int r2 = m2.rows;

    if (r1 != c2) {
        printf("Matrixes are not suitable for multiplication.\n");
        exit(-1);
    }

    double *mtx3 = alloc_matrix(c1, r2);

    multiplication(m1.m, m2.m, mtx3, c1, r2);

    write_matrix(mtx3, c1, r2);

    free(m1.m);
    free(m2.m);
    free(mtx3);

    return 0;
}

这是我使用的两个测试文件。请注意,尽管您看不到它,但第一行的末尾有空格[作为测试]:

这是m1.txt

 1 2  3  4
5 6 7  8
9 10  11  12

这是第二个文件:

 1  2  3
 4 5 6
 7 8 9
 10 11 12

这是程序输出:

 4   3
 3   4
38.000000 44.000000 202.000000 232.000000
98.000000 116.000000 438.000000 504.000000
158.000000 188.000000 674.000000 776.000000
9.000000 10.000000 87.000000 100.000000

更新:

这是一个备用的dim函数,它用换行扫描[以获取行长]替换了第一行的[比较脆弱]逐字符扫描,然后替换了malloc缓冲区fgets,然后在strtok上循环以计算行中的非空格字符串(即列数):

// size and read in matrix
struct mat
dim(const char *file)
{
    struct mat m;
    int rows = 0;
    int cols = 0;
    int maxcnt;
    int curcnt;
    int ret;
    char *buf;
    char *bp;
    char *tok;
    int c;
    int c2;

    FILE *f = fopen(file, "r+");

    // count number of chars in first line of the file
    curcnt = 0;
    while (1) {
        c = fgetc(f);
        if (c == EOF)
            break;
        ++curcnt;
        if (c == '\n')
            break;
    }

    ++curcnt;
    buf = malloc(curcnt);
    rewind(f);
    fgets(buf,curcnt,f);

    cols = 0;
    bp = buf;
    while (1) {
        tok = strtok(bp," \n");
        if (tok == NULL)
            break;
        ++cols;
        bp = NULL;
    }
    free(buf);

    rewind(f);

    m.rows = 0;
    m.cols = cols;
    m.m = NULL;
    curcnt = 0;
    maxcnt = 0;

    while (1) {
        if (curcnt >= maxcnt) {
            maxcnt += m.cols * 100;
            double *tmp = realloc(m.m,sizeof(double) * maxcnt);
            if (tmp == NULL) {
                printf("dim: realloc failure\n");
                exit(1);
            }
            m.m = tmp;
        }

        ret = 0;
        for (int idx = 0;  idx < cols;  ++idx, ++curcnt) {
            ret = fscanf(f, "%lf", &m.m[curcnt]);
            if (ret != 1)
                break;
        }

        if (ret != 1)
            break;

        rows += 1;
    }

    fclose(f);

    m.rows = rows;

    // trim matrix to actual size;
    m.m = realloc(m.m,sizeof(double) * rows * cols);

    return m;
}

更新#2:

我不喜欢使用任何一种解决方案来获取列数,因此这是一种更清洁的解决方案,它与第一种解决方案一样快,但更简单,更省力:

// scan first line and count columns
int
colcalc(FILE *f)
{
    int c;
    int noncur;
    int nonprev = 0;
    int cols = 0;

    while (1) {
        c = fgetc(f);
        if (c == EOF)
            break;

        if (c == '\n')
            break;

        // only count non-whitespace chars
        switch (c) {
        case ' ':
        case '\t':
            noncur = 0;
            break;
        default:
            noncur = 1;
            break;
        }

        // column starts on _first_ char in word
        if (noncur)
            cols += (noncur != nonprev);

        nonprev = noncur;
    }

    rewind(f);

    return cols;
}

更新#3:

  

我由您尝试了前面的2种方法,它运行非常顺利!再一次谢谢你!以及您关于使用更少的变量和东西简化我的程序的评论!

不客气!

我的编码风格/方法学来自[非常]旧书:Kernighan和Plauger撰写的“编程风格的元素”。

该书中的示例是用Fortran编写的,但其格言与Steve McConnell的“ Code Complete”相当。

第7章[效率和工具]:

  • 在使其变得更快之前将其做好。
  • 提高速度时要保持正确。
  • 在使其变得更快之前将其弄清楚。
  • 不要为了提高“效率”而牺牲清晰度。
  • 不要紧张地重复使用代码;改组。
  • 确保特殊情况确实很特殊。
  • 保持简单以使其更快。
  • 不要欺骗代码以使其更快-找到更好的算法。
  • 仪器程序。在进行“效率”更改之前先进行测量。