使用std :: transform的C ++ 2D矩阵算术运算?

时间:2018-04-24 04:54:18

标签: c++ c++17

我想知道是否可以使用std :: transform进行2D矩阵乘法?矩阵存储为2d矢量说:

data.begin()

我试图简单地将Finder -> Go to Folder -> Go 传递给转换函数,但这不起作用。有人可以提供一些建议或读物吗?谢谢!

4 个答案:

答案 0 :(得分:1)

tl; dr:不要这样做,也要更改矩阵数据结构

你接近这个错误的方式。 std::transform旨在作为线性操作,其中每个元素独立于其他元素进行转换。虽然技术上可以在用std::transform迭代矩阵的同时读取其他矩阵元素,但这不是一个好主意 - 设计明智和性能明智。

此外,最好让整个矩阵的数据在内存中连续,而不是在不同的地方分配n个单独的向量。此外,矩阵数据有时以列主要方式而非主要方式访问。所以 - 为什么要像这样持有你的矩阵?将所有数据保存在标准库数据类型的简单组合中本身并不是更好。

也许你应该花点时间看看实际上专门用于矩阵乘法的库如何保持它们的矩阵,例如: EigenIntel MKL

答案 1 :(得分:0)

以下是一些帮助您入门的基本代码:

    #include <algorithm>
    #include <cctype>
    #include <iostream>
    #include <string>
    #include <vector>

    using namespace std;

    int main()
    {
       vector<vector<int>> data(3);

       data[0] = vector<int>{1, 2, 4};
       data[1] = vector<int>{1, 2, 3};
       data[2] = vector<int>{1, 2, 3};

       vector<vector<int>> data2(3);

       data2[0] = vector<int>{1, 2};
       data2[1] = vector<int>{1, 2};
       data2[2] = vector<int>{1, 2};

       vector<vector<int>> result(3);

       std::transform(data.begin(), data.end(), result.begin(),
       [data2](vector<int> row) -> vector<int> {
           vector<int> ret;
           for (int i = 0; i < data2[0].size(); i++) {
               int rowcolsum = 0;
               for (int k = 0; k < row.size(); k++) {
                   rowcolsum += row[k] * data2[k][i];
               }
               ret.push_back(rowcolsum);
           }
           return ret;
       });

       printf("%d, %d\n", result[0][0], result[0][1]);
       printf("%d, %d\n", result[1][0], result[1][1]);
       printf("%d, %d\n", result[1][0], result[1][1]);
    }

请注意,此代码无效,并不是我建议你这样做的。但是,由于您使用std::transform请求了解决方案,这就是我提供的解决方案,尽管在这种情况下使用率很低。其他答案可以指导您进行矩阵乘法的更好,更好的方法。如果您需要使用矩阵,我强烈建议使用专用于该功能的库,而不是自己实现。

答案 2 :(得分:0)

std::transform不是正确的算法。如果你真的想要,你可以transform一系列索引(std::pair<std::size_t, std::size_t>)而不是我使用的显式循环。

using Matrix = std::vector<std::vector<double>>;
using Slice = std::vector<double>; 
// Either a row or a column

Matrix operator*(Matrix lhs, Matrix rhs)
{
    auto rowN = [](const Matrix & mat, std::size_t n) -> Slice
    {
        return mat[n]; 
        // copy of the nth row
    }
    auto colN = [](const Matrix & mat, std::size_t n) -> Slice
    {
        Slice result(mat.size());
        std::transform(mat.begin(), mat.end(), result.begin(), [n](auto & vec){ return vec[n]; });
        // have to copy each row's nth element to get nth column
        return result;
    }

    Matrix result(lhs.size(), { rhs.size(), 0 }); 
    // may be the wrong way round, can't recall. Doesn't matter for square matrices

    for (std::size_t r = 0; r < lhs.size(); r++)
    {
        auto row = rowN(lhs, r);
        for (std::size_t c = 0; c < rhs.size(); c++)
        {
             auto col = colN(rhs, c);
             result[r][c] = std::inner_product(row.begin(), row.end(), col.begin(), 0.);
        }
    }
}

我可能已经完成了转置,但是使用源矩阵中的列交换行很简单。您还可以缓存colN的结果,该结果用于交换空间。

答案 3 :(得分:0)

您可以使用inner_product¹和transform简洁地实现矩阵乘法,并使用0个不必要的数据副本(与其他解决方案不同):

#include <boost/range/numeric.hpp>
#include <boost/range/adaptor/transformed.hpp>

#include <vector>
#include <iostream>

template<class T>
using Matrix = std::vector<std::vector<T>>;

template<class T>
Matrix<T> dot(Matrix<T> const& a, Matrix<T> const& b) {
    using boost::adaptors::transformed;
    auto rows = a.size();
    auto cols = b[0].size();
    Matrix<T> result(rows, std::vector<T>(cols));
    for(decltype(rows) r = 0; r < rows; ++r)
        for(decltype(cols) c = 0; c < cols; ++c)
            result[r][c] = inner_product(a[r], b | transformed([c](auto& row) { return row[c]; }), T{});
    return result;
}

用法:

template<class T>
void print_matrix(Matrix<T> const& a) {
    for(auto& r : a) {
        for(auto& c : r)
            std::cout << c << ' ';
        std::cout << '\n';
    }
    std::cout << '\n';
}

int main() {
    Matrix<int> a{
        {1,2,3},
        {4,5,6}
    };
    Matrix<int> b{
        {1,2},
        {3,4},
        {5,6}
    };
    auto r = dot(a, b);

    print_matrix(a);
    print_matrix(b);
    print_matrix(r);

}

输出:

1 2 3 
4 5 6 

1 2 
3 4 
5 6 

22 28 
49 64 

矩阵的更有效存储是std::vector<T>(rows * cols)

¹矩阵乘法结果单元格值是输入的相应行和列的内积。

如果碰巧经常乘以一个矩阵,那么转置该矩阵以避免遍历行以提取列是最有效的。这也使内部产品操作易于矢量化,因为两个输入都变为普通数组(行·行与行·col)。