【发布时间】:2020-06-12 22:46:38
【问题描述】:
我想请你帮忙。我已经在 C++ 中实现了分而治之的算法。它工作正常,但它真的很慢。 512x512 矩阵的计算时间为 90 秒,与 0.4 秒的朴素算法相比,这是不可接受的。你能告诉我,如果,以及我做错了什么?
主要的分治功能是:
std::vector<std::vector<int>> Matrix::divideAndConquer(std::vector<std::vector<int>>& matrix1,
std::vector<std::vector<int>>& matrix2)
{
if (matrix1.size() == 1) {
return naive(matrix1, matrix2);
}
int newSize = matrix1.size() / 2;
std::vector<int> inner(newSize);
std::vector<std::vector<int>> a11(newSize, inner);
std::vector<std::vector<int>> a12(newSize, inner);
std::vector<std::vector<int>> a21(newSize, inner);
std::vector<std::vector<int>> a22(newSize, inner);
std::vector<std::vector<int>> b11(newSize, inner);
std::vector<std::vector<int>> b12(newSize, inner);
std::vector<std::vector<int>> b21(newSize, inner);
std::vector<std::vector<int>> b22(newSize, inner);
Matrix::divideMatrix(matrix1, a11, a12, a21, a22);
Matrix::divideMatrix(matrix2, b11, b12, b21, b22);
std::vector<std::vector<int>> m1 = divideAndConquer(a11, b11);
std::vector<std::vector<int>> m2 = divideAndConquer(a12, b21);
std::vector<std::vector<int>> m3 = divideAndConquer(a11, b12);
std::vector<std::vector<int>> m4 = divideAndConquer(a12, b22);
std::vector<std::vector<int>> m5 = divideAndConquer(a21, b11);
std::vector<std::vector<int>> m6 = divideAndConquer(a22, b21);
std::vector<std::vector<int>> m7 = divideAndConquer(a21, b12);
std::vector<std::vector<int>> m8 = divideAndConquer(a22, b22);
std::vector<std::vector<int>> c1 = add(m1, m2);
std::vector<std::vector<int>> c2 = add(m3, m4);
std::vector<std::vector<int>> c3 = add(m5, m6);
std::vector<std::vector<int>> c4 = add(m7, m8);
return combine(c1, c2, c3, c4);
}
然后除法矩阵函数为:
void Matrix::divideMatrix(std::vector<std::vector<int>>& matrix, std::vector<std::vector<int>>& m1,
std::vector<std::vector<int>>& m2, std::vector<std::vector<int>>& m3, std::vector<std::vector<int>>&
m4)
{
for (int i = 0; i < matrix.size() / 2; i++) {
for (int j = 0; j < matrix.size() / 2; j++) {
m1[i][j] = matrix[i][j];
m2[i][j] = matrix[i][j + matrix.size() / 2];
m3[i][j] = matrix[i + (matrix.size() / 2)][j];
m4[i][j] = matrix[i + (matrix.size() / 2)][j + (matrix.size() / 2)];
}
}
}
组合方法是:
std::vector<std::vector<int>> Matrix::combine(std::vector<std::vector<int>>& m1, std::vector<std::vector<int>>& m2, std::vector<std::vector<int>>& m3, std::vector<std::vector<int>>& m4)
{
std::vector<int> inner(m1[0].size() + m2[0].size());
std::vector<std::vector<int>> result(m1.size() + m2.size(), inner);
for (int i = 0; i < m1.size(); i++) {
for (int j = 0; j < m2.size(); j++) {
result[i][j] = m1[i][j];
result[i][j + m1[0].size()] = m2[i][j];
result[i + m1.size()][j] = m3[i][j];
result[i + m1.size()][j + m3[0].size()] = m4[i][j];
}
}
return result;
}
最后添加方法是:
std::vector<std::vector<int>> Matrix::add(std::vector<std::vector<int>>& matrix1, std::vector<std::vector<int>>& matrix2)
{
std::vector<std::vector<int>> result(matrix1);
for (int i = 0; i < matrix1.size(); i++) {
for (int j = 0; j < matrix1.size(); j++) {
result[i][j] = matrix1[i][j] + matrix2[i][j];
}
}
return result;
}
朴素的函数只是将 matrix1[0][0] * matrix2[0][0] 的乘积返回到一个新向量中
【问题讨论】:
-
您有向量的向量,并且您正在复制矩阵 log2(N) 次的每个元素。通过使矩阵的底层数据结构成为大小为 NxN 的单个向量,您可以获得一些加速(查看Code Review 以获得有关实现矩阵类的许多建议)。要做的事情是拥有某种“矩阵视图”类,该类具有成员变量 Matrix 引用和可以访问的有效索引。那么你就不会复制数据这么多次了。
-
Here is a really simple and effective matrix class 做了约翰所说的一些事情。
-
每个向量声明创建并分配一个新向量。这东西正在吞噬内存,就像它已经过时了一样。这里的方法是在这里为向量实现一些东西,这相当于
std::string_view为std::strings 所做的可变版本。我可以看到一种方法,但这将是一大堆代码。但它应该很快,因为它会避免所有内存副本。 -
@user4581301 覆盖单个缓冲区。 John 还提到了“矩阵视图”。
标签: c++ algorithm divide-and-conquer