Как я могу рассчитать (A + B) * (A + B)?A, B - матрицы - PullRequest
2 голосов
/ 12 марта 2011

У меня есть 2 упражнения, одно использует struct , а другое использует class , используйте +, * перегрузки для вычисления с помощью матриц.

Тип моей матрицы:

struct matrix 
{
   int** a;
   int m;
   int n;
};

Какие "m", "n" - это количество строк и столбцов, "a" - это указатель на указатель, который будет динамической памятью, выделяемой во время выполнения.

И перегруженные операторы: +, *, + =, * =

У меня нет проблем с 2 матрицами, как в сложении, так и в умножении.Но у меня возникают проблемы, когда мне нужно отобразить значение выражения (A + B) * (A + B).Обратите внимание, что с A + (A * B) все в порядке.

Я пытаюсь отобразить все выражение, оно кажется переполненным.Затем я объявляю матрицу типа C, назначая C = A + B, C является правильным.Но если я покажу C * C, результат останется прежним, очень плохим, хотя A * A - это хорошо.

Может кто-нибудь объяснить мою проблему?Как это исправить?

Я тестирую с двумя матрицами 4x4, их элементы пронумерованы от 1 до 16.

Мой код:

#include <iostream>
using namespace std;
struct matrix
{
    int** a;
    int m;
    int n;
};
matrix temp;

matrix InputMatrix(matrix &mat)
{
    for (int i=0; i <= mat.m-1; i++)
    {
        for (int j=0; j <= mat.n-1; j++)
        {
            cout.width(5);
            cout << "[" << i+1 << "," << j+1 << "] = ";

            *(*(mat.a + i) + j) = i*mat.m + j + 1;
            cout << *(*(mat.a + i) + j);

            /*int x = rand()%20; // random matrix
            cout << x;
            *(*(mat.a + i) + j) = x;*/
        }
        cout << endl;
    }
    return mat;
}

int AllocMatrix(matrix &mat)
{
    mat.a = new int*[mat.m];
    if (mat.a == NULL)
    {
        return 0;
    }
    for (int i=0; i <= mat.m-1; i++)
    {
        *(mat.a + i) = new int[mat.n];
        if (*(mat.a + i) == NULL)
        {
            return 0;
        }
    }
    return 1;
}
int FreeMatrix(matrix &mat)
{
    if (mat.a != NULL)
    {
        delete [] mat.a;
    }
    return 0;
}

int DispMatrix(const matrix &mat)
{
    for (int i=0; i <= mat.m-1; i++)
    {
        for (int j=0; j<= mat.n-1; j++)
        {
            cout.width(7);
            cout << *(*(mat.a + i) + j);
        }
        cout << endl;
    }
    cout << endl;
    return 0;
}

matrix & operator +(const matrix &mat1, const matrix &mat2)
{
    for (int i=0; i <= temp.m-1; i++)
    {
        for (int j=0; j <= temp.n-1; j++)
        {
            *(*(temp.a + i) + j) = *(*(mat1.a + i) + j) + *(*(mat2.a + i) + j);
        }
    }
    return temp;
}

matrix & operator +(const matrix &mat1, const int k)
{
    for (int i=0; i <= temp.m-1; i++)
    {
        for (int j=0; j <= temp.n-1; j++)
        {
            *(*(temp.a + i) + j) = *(*(mat1.a + i) + j) + k;
        }
    }
    return temp;
}

matrix & operator +=(matrix &mat1, const matrix &mat2)
{
    for (int i=0; i <= mat1.m-1; i++)
    {
        for (int j=0; j <= mat1.n-1; j++)
        {
            *(*(temp.a + i) + j) = *(*(mat1.a + i) + j) + *(*(mat2.a + i) + j);
        }
    }
    for (int i=0; i <= temp.m-1; i++)
    {
        for (int j=0; j <= temp.n-1; j++)
        {
            *(*(mat1.a + i) + j) = *(*(temp.a + i) + j);
        }
    }
    return mat1;
}

matrix &  operator *(const matrix &mat1, const matrix &mat2)
{
    for (int i=0; i <= mat1.m-1; i++)
    {
        for (int j=0; j <= mat2.n-1; j++)
        {
            int tong = 0;
            for (int k=0; k <= mat2.m-1; k++)
            {
                tong += (*(*(mat1.a + i) + k)) * (*(*(mat2.a + k) + j));
            }
            *(*(temp.a + i) + j) = tong;
        }
    }
    return temp;
}
matrix &  operator *(const matrix &mat1, const int k)
{
    for (int i=0; i <= temp.m-1; i++)
    {
        for (int j=0; j <= temp.n-1; j++)
        {
            *(*(temp.a + i) + j) = *(*(mat1.a + i) + j) * k;
        }
    }
    return temp;
}

matrix &  operator *=(matrix &mat1, const matrix &mat2)
{
    for (int i=0; i <= mat1.m-1; i++)
    {
        for (int j=0; j <= mat2.n-1; j++)
        {
            int tong = 0;
            for (int k=0; k <= mat2.m-1; k++)
            {
                tong += (*(*(mat1.a + i) + k)) * (*(*(mat2.a + k) + j));
            }
            *(*(temp.a + i) + j) = tong;
        }
    }
    for (int i=0; i <= temp.m-1; i++)
    {
        for (int j=0; j <= temp.n-1; j++)
        {
            *(*(mat1.a + i) + j) = *(*(temp.a + i) + j);
        }
    }
    return mat1;
}

int main()
{
    matrix mat1, mat2, mat3;
    int m1 = 0, n1 = 0, m2 = 0, n2 = 0;

    m1 = m2 = n1 = n2 = 4;

    mat1.m = m1;
    mat1.n = n1;

    mat2.m = m2;
    mat2.n = n2;

    mat3.m = m1;
    mat3.n = n1;
    AllocMatrix(mat3);

    if (!AllocMatrix(mat1))
    {
        cout << "Out of memory!" << endl;
        FreeMatrix(mat1);
        return 1;
    }
    if (!AllocMatrix(mat2))
    {
        cout << "Out of memory!" << endl;
        FreeMatrix(mat1);
        FreeMatrix(mat2);
        return 1;
    }
    cout << "Matrix - 1:" << endl;
    mat1 = InputMatrix(mat1);
    cout << "Matrix - 2:" << endl;
    mat2 = InputMatrix(mat2);

    if ((mat1.m == mat2.m)&&(mat1.n == mat2.n))
    {
        temp.m = mat1.m;
        temp.n = mat1.n;
        if (!AllocMatrix(temp))
        {
            cout << "Out of memory!" << endl;
            FreeMatrix(mat1);
            FreeMatrix(mat2);
            FreeMatrix(temp);
            return 1;
        }
        cout << "Ressult: " << endl;
        mat3 = mat1 + mat2;
        DispMatrix(mat3);
        DispMatrix(mat3 * mat3);
        FreeMatrix(temp);
    }

    FreeMatrix(mat1);
    FreeMatrix(mat2);
    system("pause");
    return 0;
}

Результат:

Matrix - 1:
    [1,1] = 1    [1,2] = 2    [1,3] = 3    [1,4] = 4
    [2,1] = 5    [2,2] = 6    [2,3] = 7    [2,4] = 8
    [3,1] = 9    [3,2] = 10    [3,3] = 11    [3,4] = 12
    [4,1] = 13    [4,2] = 14    [4,3] = 15    [4,4] = 16
Matrix - 2:
    [1,1] = 1    [1,2] = 2    [1,3] = 3    [1,4] = 4
    [2,1] = 5    [2,2] = 6    [2,3] = 7    [2,4] = 8
    [3,1] = 9    [3,2] = 10    [3,3] = 11    [3,4] = 12
    [4,1] = 13    [4,2] = 14    [4,3] = 15    [4,4] = 16
Ressult:
      2      4      6      8
     10     12     14     16
     18     20     22     24
     26     28     30     32

    360   1832  28180 708768
   43888039688236210260317821152
  95260335311192-6444114522130541536
2990856-14161730721164069912-1507182592

Ответы [ 3 ]

4 голосов
/ 12 марта 2011

Это определение matrix temp; в области именного пространства - это огромный сигнал тревоги. Вам абсолютно не нужно использовать это как часть ваших перегруженных операторов.

Ваши перегрузки operator+ и operator* не должны возвращать ссылку, они должны возвращать matrix по значению. Это позволило бы устранить проблему, когда temp невидимо повторно используется в нескольких слотах в сложных выражениях. Это вызывает ваши неожиданные значения в наблюдаемых результатах.

Чтобы это сработало, вам нужно убедиться, что ваш класс matrix можно копировать и назначать. Вам нужно либо использовать контейнеры более высокого уровня, такие как std::vector, чтобы удалить все ручное управление памятью, либо предоставить вашему классу конструктор копирования, определенный пользователем, оператор назначения копирования и деструктор.

Ваше ручное управление памятью в настоящее время неверно. FreeMatrix удаляет элемент mat.a, но не освобождает ни один из выделенных массивов, на которые указывают элементы mat.a. Также бесполезно проверять, привело ли выражение new к значению NULL. new будет либо успешным, либо выдаст исключение.

Для меня, я думаю, что самым простым подходом было бы дать вашему matrix конструктор и реализовать его, используя std::vector.

например:.

struct matrix
{
    matrix( int m, int n )
        : a( std::vector< std::vector<int> >( m, std::vector<int>( n, 0 ) );
    {
    }

    std::vector< std::vector<int> > a;
};

Таким образом, ваш matrix будет копируемым и назначаемым, и вы можете отказаться от AllocMatrix и FreeMatrix и вернуть matrix значением, где это уместно.

Вам, конечно, потребуется изменить выражения, такие как *(*(temp.a + i) + j), на эквивалентные, но гораздо более удобочитаемые, temp.a[i][j], прежде чем переключаться на vector.

4 голосов
/ 12 марта 2011

Некоторые комментарии:

1: использовать методы-члены, а не функции
2: не использовать using namespace std.
3: если в массиве есть m членов, его более традиционно использовать<в циклах for. </p>

for (int i=0; i <= mat.m-1; i++)

// More traditional to use:

for (int i=0; i < mat.m; ++i)

4: использовать более длинные имена переменных, чем i.Попробуйте найти в коде все экземпляры переменной «i».Вы получите много ложных срабатываний.Что-то вроде loopM будет проще для чтения.

5: Используйте оператор []

*(*(mat.a + i) + j) = BLA;

// Cab be written as:

mat.a[i][j] = BLA;

6: В нормальных ситуациях new никогда не вернет NULL.Так что не пишите код, который проверяет это.Если это не удастся, будет выдано исключение.Это позволяет вам удалить код обнаружения ошибок из обычного потока кода и обрабатывать ошибки в исключениях.

mat.a = new int*[mat.m];
// This is a waste of time. Here mat.a will NEVER be NULL
if (mat.a == NULL)
{
    return 0;
}

7: все в порядке, чтобы удалить объекты NULL.Так что не проверяйте NULL перед удалением.

if (mat.a != NULL)
{
    delete [] mat.a;
}

// If it is NULL nothing bad will happen.

delete [] mat.a

8: Вы забыли удалить все элементы.
Примечание: я был бы более уверен, что это правильно, если вы использовали конструктор / деструктор каккомпилятор гарантирует, что деструктор не будет вызван, если конструктор выдает исключение.С другой стороны, использование вами функций не гарантирует этого, и вы можете поймать исключение и все же выполнить FreeMatrix () для недопустимого объекта матрицы.Но так как в вашем текущем коде нет обработки исключений, я чувствую себя в безопасности.

 for(int loop = 0;loop < m;++loop)
 { 
     delete [] mat.a[loop];
 }
 delete [] mat.a;

9: ваш оператор +, кажется, использует некоторую случайную переменную temp?
Предпочитаете объявлять temp внутри функции и возвращатькопия.Но для этого вам понадобится правильный конструктор копирования и оператор присваивания (см. Ниже).

matrix operator +(const matrix &mat1, const matrix &mat2) 
{
    matrix temp;  // add mat1 and mat2 into temp
}
return temp;

10: в вашей функции + =.Вы суммируете значения во временную переменную, а затем снова присваиваете mat1.Нет причин не делать этого на месте.

11: объявлять только одну переменную в строке.

matrix mat1, mat2, mat3;

// All coding standards say use:

matrix     mat1;
matrix     mat2;
matrix     mat3;

Причина в том, что она обнаруживает пару проблем, связанных с указателями.На мой взгляд, это также облегчает чтение кода (что также является плюсом).Все компании примут участие в нем, поэтому просто привыкните к нему.

12: Ваше распределение матриц никогда не будет неудачным (как указывалось ранее).Это бросит исключение.Таким образом, слежка никогда не напечатает «Недостаточно памяти».Также я хотел бы отметить, что вы возвращаете целое число, где bool был бы лучшим типом для обозначения сбоя (если бы вы могли указать сбой).

if (!AllocMatrix(mat1))
{
    //
    // This will never be executed. EVER.
    //
    cout << "Out of memory!" << endl;
    FreeMatrix(mat1);
    return 1;
}

13: Здесь у нас есть потенциальная проблема.Ваш код выполняет operator +, как вы ожидаете.Но он также выполняет matrix::operator=.Вы не определили это, но компилятор автоматически сгенерировал код для этого метода.И это не делает то, что вы думаете.К счастью, вы не освобождаете mat3, поэтому он не взрывается.

    mat3 = mat1 + mat2;

Любой код, в котором класс (или структура) содержит указатели, которыми владеют (вы размещаете указатели и удаляете их), необходимособлюдайте правило 3 (плюс есть правильный конструктор).

struct matrix 
{
   int** a;
   int m;
   int n;
};

Причина в том, что если вы не отключите их специально, компилятор сгенерирует для вас 4 метода.Если вы начнете выделять / удалять память вручную, два из этих методов (конструкция копирования и оператор присваивания) будут делать то, что затрудняет правильное освобождение.В результате вы должны либо отключить их, либо явным образом определить все 4 метода, сгенерированных компилятором.

В вашем случае компилятор генерирует следующие 4 метода:

matrix::matrix() { /* Do nothing or value initialize */ }
matrix::~matrxc(){ /* Do nothing */ }
matrix::matrix(matrix const& rhs)
    : a(rhs.a)
    , m(rhs.m)
    , n(rhs.n)
{}
matrix& matrix::operator+(matrix const& rhs)
{
    a = rhs.a;
    m = rhs.m;
    n = rhs.n;
    return *this;
} 

Теперь рассмотрим следующий кодсделаю:

matrix  m1;
m1.m = 5;
m1.n = 5;
AllocMatrix(m1));

matrix  m2;
m2.m = 5;
m2.n = 5;
AllocMatrix(m2));

m2 = m1;  /* Simplified version of m3 = m1 + m2; */
          // The problem is that the a member is being copied from m1 to m2.
          // But the copy is a shallow copy. So now both m1 and m2 point at the same
          // piece of memory.

FreeMatrix(m2);  // Frees m2.a
FreeMatrix(m1);  // Whops. This Frees m1.a but it points at the same memory as m2.a
                 //        So now you have a double delete.

14: Разделение интересов.

Это важная техника для изучения.Это означает, что класс должен сделать одну вещь.Либо он должен содержать логику для выполнения некоторой бизнес-логики, либо он должен содержать управление памятью (или другие элементы управления).Это не должно делать обоих.

Это означает, что ваш матричный объект должен сосредоточиться на логике выполнения матричных операций и делегировать управление памятью другому типу объекта, который специально разработан для управления памятью.Я оставлю это так, как Charles Bailey подробно описывает это в своем ответе.

0 голосов
/ 12 марта 2011

A * A работает, но C * C не работает? Может быть, проблема не в вашем коде, а в выборе матриц: равно ли количество столбцов в первой матрице количеству строк во второй матрице? Это должно быть для умножения матриц! (В случае A * A и C * C это означало бы, что эти матрицы должны быть квадратными, если вы не используете неявное преобразование.)

Если это не решение, пожалуйста, напишите

  • ваш код,
  • ваши матрицы,
  • ваш вывод и
  • ErrorMessages / stackdump
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...