如何计算 (A + B) * (A + B) ? A、B 是矩阵
我有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
I have 2 exercises, one uses struct and the other uses class, use +, * overloadings to calculate with matrices.
My Matrix type:
struct matrix
{
int** a;
int m;
int n;
};
Which "m", "n" are the number of rows and columns, "a" is a pointer-to-pointer that will be dynamic memory allocated in run-time.
And Operators overloaded are: +, *, +=, *=
I have no problem with 2 matrices, in both addition and multiplication. But I get in trouble when I need to display the value of (A + B) * (A + B) expression. Note that A + (A * B) is ok.
I try to display the whole of expression, it seems to be overflow. Then I declare a matrix type C, assign C = A + B, C is correct. But if I display C * C, the result remains the same, very bad, although A * A is nice.
Can someone explain my problem? How can I fix it?
I test with two 4x4 matrices, their elements are numbered from 1 to 16.
My code:
#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;
}
Result:
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
一些评论:
1:使用成员方法而不是函数
2:不要使用
using namespace std
。3:如果一个数组有 m 个成员,则更传统的是使用 <在 for 循环中。
4:使用比
i
更长的变量名称。尝试在代码中搜索变量“i”的所有实例。您会得到很多误报。像loopM
这样的东西会更容易阅读。5:使用[]运算符
6:正常情况下new永远不会返回NULL。因此,不要编写检查它的代码。如果失败,将会抛出异常。这允许您从正常代码流中删除错误检测代码并处理异常中的错误。
7:删除NULL对象是可以的。因此,在删除之前不要测试 NULL。
8:您忘记删除所有成员。
注意:如果您使用了构造函数/析构函数,我会更有信心这是正确的,因为编译器将保证如果构造函数抛出异常,则不会调用析构函数。另一方面,您对函数的使用并不能保证这一点,您可能会捕获异常并仍然在无效的矩阵对象上执行 FreeMatrix()。但由于您当前的代码没有异常处理,我觉得它会起作用。
9:你的运算符 + 似乎使用了一些随机临时变量?
更喜欢在函数内声明 temp 并返回一个副本。但要使其工作,您将需要正确的复制构造函数和赋值运算符(见下文)。
10:在你的 += 函数中。将这些值求和到一个临时变量中,然后重新分配回 mat1。没有理由不这样做。
11:每行只声明一个变量。
原因是它捕获了一些与指针相关的问题。在我看来,它还使代码更易于阅读(这也是一个优点)。所有公司都会开始使用它,所以只要习惯它即可。
12:您的矩阵分配永远不会失败(如前所述)。它会抛出一个异常。所以填充永远不会打印“内存不足”。另外我会注意到您返回的是一个整数,其中 bool 是指示失败的更好类型(如果您可以指示失败)。
13:这里我们有一个潜在的问题。您的代码正在按照您的预期执行
operator +
。但它也在执行matrix::operator=
。您没有定义此方法,但编译器自动生成此方法的代码。而且它并没有按照你的想法去做。幸运的是你没有释放 mat3 因此它没有爆炸。类(或结构)包含由(分配然后指针并删除它们)拥有的指针的任何代码都需要遵守 3 的规则(加上有正确的构造函数)。
原因是,如果您实际上没有专门禁用它们,编译器将为您生成 4 个方法。如果您开始手动分配/删除内存,其中两种方法(复制构造和赋值运算符)将导致您的释放难以正确完成。因此,您应该禁用它们或显式定义所有 4 个编译器生成的方法。
在您的情况下,编译器会生成以下 4 个方法:
现在考虑以下代码将执行的操作:
14:关注点分离。
这是一项需要学习的重要技术。这意味着一个类应该做一件事。它要么应该包含执行某些业务逻辑的逻辑,要么应该包含内存管理(或其他管理内容)。它不应该两者兼而有之。
这意味着您的矩阵对象应该专注于执行矩阵运算的逻辑,并将内存管理委托给专门为内存管理设计的另一种对象类型。我将就此保留,因为
Charles Bailey
在他的回答中详细介绍了这一点。Some comments:
1: Use member methods rather than functions
2: Don't use
using namespace std
.3: If an array has m members it is more traditional to use < in the for loops.
4: Use longer variables names than
i
. Try searching the code for all the instance of the variable 'i'. You will get a lot of false positive hits. Something likeloopM
would be easier to read.5: Use the [] operator
6: Under normal situations new will never return NULL. So do not write code that checks for it. If it fails an exception will be thrown. This allows you to remove error detection code from normal code flow and handle the errors in exceptions.
7: It is OK to delete NULL objects. So don' test for NULL before a delete.
8: You forgot to delete all the members.
Note: I would be more confident this is correct if you had used constructor/destructor as the compiler will guarantee that destructor is not called if the constructor throws an exception. On the other hand your use of functions does not guarantee this and you may catch an exception and still execute the FreeMatrix() on an invalid matrix object. But since your current code has no exception handling I feel safe it will work.
9: Your operator + seems to use some random temp variable?
Prefer to declare temp inside the function and return a copy. But for this to work you will need correct copy constructor and assignment operator (see below).
10: In your += function. You sum the values into a temp variable then re-assign back to the mat1. There is no reason not to do this in place.
11: Only declare one variable per line.
The reason is it catches a couple of problems associated with pointers. In my opinion it also makes the code easier to read (which is also a plus). All compaies will inisit on it so just get used to it.
12: Your matrix allocation will never fail (as pointed out previously). It will throw an expcetion. So the fillowing will never print "Out of memory". Also I would note you are returning an integer where a bool would have been a better type to indicate failure (if you could have indicated failure).
13: Here we have a potential problem. Your code is executing
operator +
as you expect. But it is also executingmatrix::operator=
. You did not define this but the compiler automatically generated the code for this method. And it is not doing what you think. Luckily you do not free mat3 thus it is not blowing up.Any code where where a class (or struct) contains pointers that are owned by (you allocate then pointers and delete them), needs to obey the rule of 3 (plus have a correct constructor).
The reason is that if you do not actually specifically disable them the compiler will generate 4 methods for you. If you start manually allocating/deleting the memory two of these methods (copy construct and assignment operator) will do stuff that makes your deallocation hard to do correctly. As a result you should either disable them or explicitly define all 4 compiler generated methods.
In your case the compiler is generates the following 4 methods:
Now consider what the following code will do:
14: Separation of concerns.
This is an important technique to learn. It means a class should do one thing. Either it should contain the logic to perform some business logic or it should contain memory management (or other management stuff). It should not do both.
This means your matrix object should concentrate on the logic of doing matrix operations and delegate the memory management to another object type that is specifically designed for memory management. I will leave it at that as
Charles Bailey
covers that in detail in his answer.这个定义
matrix temp;
在命名空间范围内是一个巨大的警钟。您绝对不需要将其用作重载运算符的一部分。您的
operator+
和operator*
重载不应返回引用,它们应按值返回一个matrix
。这将消除在复杂表达式的多个槽中无形地重用temp
的问题。这会导致观察结果出现意外值。要使其正常工作,您需要确保您的矩阵类是可复制和可分配的。您要么需要使用更高级别的容器(例如 std::vector )来删除所有手动内存管理,要么需要为您的类提供使用定义的复制构造函数、复制赋值运算符和析构函数。
您的手动内存管理当前不正确。
FreeMatrix
删除mat.a
成员,但不会释放mat.a
元素指向的任何已分配数组。此外,检查new
表达式是否导致NULL
是徒劳的。new
要么成功,要么抛出异常。对我来说,我认为最简单的方法是为您的矩阵提供一个构造函数并使用 std::vector 实现它。
例如:
这样您的矩阵将是可复制和可分配的,您可以取消 AllocMatrix 和 FreeMatrix 并返回矩阵> 在适当的情况下按值。
当然,您需要将诸如
*(*(temp.a + i) + j)
之类的表达式更改为等效但更具可读性的temp.a[i] [j]
在切换到向量
之前。This definition
matrix temp;
at namespace scope is a huge alarm bell. You absolutely shouldn't need to use this as a part of your overloaded operators.Your
operator+
andoperator*
overloads shouldn't return a reference, they should return amatrix
by value. This would eliminate the problem wheretemp
is being invisibly re-used in multiple slots in complex expressions. This is causing your unexpected values in the observed results.To get this to work you need to ensure that your
matrix
class is copyable and assignable. You either need to use higher level containers such asstd::vector
to remove all of the manual memory management or you need to give your class a used-defined copy constructor, copy assignment operator and destructor.Your manual memory management is currently incorrect.
FreeMatrix
deletes themat.a
member but it does not free any of the allocated arrays that the elements ofmat.a
pointed to. Also, checking whether anew
expression resulted inNULL
is futile.new
will either succeed or throw an exception.For me, I think that the simplest approach would be to give your
matrix
a constructor and implement it usingstd::vector
.E.g.:
This way your
matrix
will be copyable and assignable and you can do away withAllocMatrix
andFreeMatrix
and returnmatrix
by value where appropriate.You will, of course, need to change expressions such as
*(*(temp.a + i) + j)
to the equivalent, but much more readable,temp.a[i][j]
before switching tovector
.A*A 有效但 C*C 无效?也许问题不在于您的代码,而在于矩阵的选择:第一个矩阵中的列数是否等于第二个矩阵中的行数?它必须用于矩阵乘法! (在 A*A 和 C*C 的情况下,这意味着这些矩阵必须是方阵,除非您使用隐式转置。)
如果这不是解决方案,请发布
A*A works but C*C does not? Maybe the problem is not in your code but in the choice of matrices: is the number of columns in the first matrix equal to the number of rows in the second matrix? It has to be for matrix multiplication! (In the case of A*A and C*C that would mean those matrices have to be square unless you use implicit transposition.)
If this is not the solution, please post