向量<向量<int>>在第一维上的点积

4

我有

vector < vector < int > > data_mat ( 3, vector < int > (4) );
vector          < int >   data_vec ( 3                     ); 

其中data_mat可以被视为一个矩阵,data_vec被视为一个列向量,我正在寻找一种方法来计算data_mat的每一列与data_vec的内积,并将其存储在另一个vector < int > data_out (4)中。

例如,使用for_eachtransform的示例http://liveworkspace.org/code/2bW3X5%241,可用于计算矩阵的列和:

sum=vector<int> (data_mat[0].size());
for_each(data_mat.begin(), data_mat.end(),
         [&](const std::vector<int>& c) {
            std::transform(c.begin(), c.end(), sum.begin(), sum.begin(),
                           [](int d1, double d2) 
                             { return d1 + d2; }
                          );
            }
        );

是否有可能以类似的方式(或使用STL函数的稍微不同的方式)计算矩阵列与向量的列点积?

问题在于,“d2 = d1 + d2”技巧在列内积情况下不起作用——如果有一种方法也包括d3,那么问题就解决了(d3 = d3 + d1 * d2),但是在transform中似乎不存在三元函数。


inner_product? - Peter Wood
注意,如果你想进行严肃的数学计算,那么最好放弃STL,转而使用C库,比如GSL。STL甚至会涉及到这种事情,这让我感到惊讶,速度差异可能达到两个数量级... - Matt Phillips
我不确定我理解你想要实现什么。你谈论点积,但我在这里看不到任何乘法。 - Andy Prowl
2
不需要去任何C库,STL也没有问题,但是如果你想做严格的数学运算,那么至少要放弃这个非常不适合的向量向量,并使用一个适当的矩阵数据结构(它存储其内部数据连续)。告诉你动态数组的动态数组是表示二维数组的好方法的人是错的。除此之外,这仍然是一个有趣的问题。 - Christian Rau
已经理解了,@ChristianRau!老实说,这是向他人证明使用STL的概念的一部分,事实上,如果你绝对坚持,可以将这些迭代器用于几乎任何事情。我卡在这个问题上了,但我很想知道是否可以完成它。我猜做矩阵最简单的方法是这样的https://dev59.com/LGnWa4cB1Zd3GeqPwQYU - 在演示了无望的低效/不适当/惰性等方式后,我将专门使用它。 - alle_meije
显示剩余3条评论
1个回答

5
实际上,您几乎可以一对一地使用现有的列求和方法。您不需要使用三元std :: transform作为内部循环,因为在将矩阵行相加之前,您缩放矩阵行的因子对于每一行都是恒定的,因为它是来自列向量的行值,并且与矩阵行一起迭代,因此外部的std :: for_each也迭代。

所以我们需要做的就是遍历矩阵的行,并将每个完整的行乘以相应的列向量中的值,并将缩放后的行添加到总和向量中。但不幸的是,这需要一个std :: for_each函数,它同时迭代两个范围,即矩阵的行和列向量的行。要实现这一点,我们可以使用通常的一元std :: for_each并手动执行对列向量的迭代,使用另一个迭代器:

std::vector<int> sum(data_mat[0].size());
auto vec_iter = data_vec.begin();
std::for_each(data_mat.begin(), data_mat.end(), 
              [&](const std::vector<int>& row) {
                 int vec_value = *vec_iter++;    //manually advance vector row
                 std::transform(row.begin(), row.end(), sum.begin(), sum.begin(),
                                [=](int a, int b) { return a*vec_value + b; });
              });

std::for_each中添加额外的手动迭代并非使用标准库算法的惯用方式,但不幸的是我们没有二进制的std::for_each可以使用。
另一个选择是将std::transform用作外循环(它可以迭代两个范围),但我们在每个外部迭代中实际上并没有计算单个值来返回,因此我们必须从外部lambda返回一些虚拟值,并通过使用某种虚拟输出迭代器将其丢弃。这也不是最干净的解决方案:
//output iterator that just discards any output
struct discard_iterator : std::iterator<std::output_iterator_tag,
                                        void, void, void, void>
{
    discard_iterator& operator*() { return *this; }
    discard_iterator& operator++() { return *this; }
    discard_iterator& operator++(int) { return *this; }
    template<typename T> discard_iterator& operator=(T&&) { return *this; }
};

//iterate over rows of matrix and vector, misusing transform as binary for_each
std::vector<int> sum(data_mat[0].size());
std::transform(data_mat.begin(), data_mat.end(), 
               data_vec.begin(), discard_iterator(), 
               [&](const std::vector<int>& row, int vec_value) {
                   return std::transform(row.begin(), row.end(), 
                                         sum.begin(), sum.begin(),
                                         [=](int a, int b) { 
                                             return a*vec_value + b;
                                         });
               });
编辑:虽然这个问题已经在评论中讨论过了,我理解并赞赏这个问题的理论性质,但是我仍然建议实际应用中不要使用动态数组的动态数组来表示结构化定义良好的二维数组,例如矩阵。一个适当的矩阵数据结构(将其内容连续存储)和适当的运算符几乎总是更好的选择。但是由于它们的通用性,您仍然可以使用标准库算法来处理这样的自定义数据结构(甚至让矩阵类型提供自己的迭代器)。

谢谢!我没有想到创建一个额外的向量并从“内部”访问它的想法。在我想要展示的应用程序中,行是编号图像,列是像素索引--在这种情况下,事实上情况并不太糟糕,因为大块仍然是连续的。即使如此,如果您对数据有足够的了解,可以定义内积,那么受益于此类知识的数据结构始终更有效率。 - alle_meije
@alle_meije 是的,这没有意义。但是等一下,我会重新设计它,在最后我们甚至不需要一个临时向量,而是需要一个二进制的 for_each - Christian Rau
@alle_meije 好的,已更新答案。std::for_each 无法同时迭代两个范围,这导致了完全干净和流畅的STL解决方案不可行,但它使我们免于需要额外的临时向量,而我的第一个(错误的)解决方案使用了它。 - Christian Rau
太棒了!谢谢Christian,我私下里喜欢手动篡改“官方”迭代器的外观 :)。 - alle_meije
这是我使用代码的程序:https://github.com/amwink/bias/blob/master/cpp/fastecm/fastecm.cpp - alle_meije

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接