Как я могу получить правильные размеры неизвестных матриц для программы умножения матриц C? - PullRequest
0 голосов
/ 02 декабря 2018

Поэтому я попытался создать программу, которая автоматически получает размер двух матриц из файла .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 голосов
/ 02 декабря 2018

Это предварено моими главными комментариями.

Мне пришлось провести рефакторинг dim, чтобы обработать произвольно большую матрицу, поэтому мне пришлось сканировать первую строку файла char-by-char, считая пробелыстроки (что дает количество столбцов - 1).Он обрабатывает / удаляет начальные / конечные пробелы [искажено]

У меня было dim, затем перематывайте файл и используйте fscanf и realloc для динамического создания матрицы.


Вот рабочий код [прошу прощения за бесполезную очистку стиля]:

#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 предыдущих способа, и он работает так гладко!Еще раз, спасибо!и ваши комментарии о том, как сделать мою программу проще с меньшим количеством переменных и прочего!

Не за что!

Мой стиль / методология кодирования взят из [очень] старой книги: ««Элементы стиля программирования» Кернигана и Плаугера.

Примеры из этой книги написаны на Фортране, но максимумы находятся в одном ряду с «Завершенным кодом» Стива Макконнелла.

Из главы 7[Эффективность и инструменты]:

  • Сделайте это правильно, прежде чем сделать это быстрее.
  • Держите это правильно, когда вы сделаете это быстрее.
  • Сделайте так, чтобы вам было ясносделайте это быстрее.
  • Не жертвуйте ясностью ради небольшого увеличения "эффективности".
  • Не напрягайтесь, чтобы повторно использовать код;Вместо этого реорганизуйте.
  • Убедитесь, что особые случаи действительно особенные.
  • Упрощайте, чтобы сделать это быстрее.
  • Не делайте код, чтобы сделать это быстрее - найдителучший алгоритм.
  • Инструмент ваших программ.Измерьте, прежде чем вносить изменения в «эффективность».
...