如何使用算法填充向量的向量

Posted

技术标签:

【中文标题】如何使用算法填充向量的向量【英文标题】:How to use algorithms to fill vector of vectors 【发布时间】:2015-10-22 11:44:17 【问题描述】:

我有

typedef std::vector<int> IVec;
typedef std::vector<IVec> IMat;

我想知道如何使用 std 算法填充IMat,即如何用更少的代码执行以下操作(所有IVecs 具有相同的大小)?

void fill(IMat& mat)
    for (int i=0;i<mat.size();i++)
        for (int j=0;j<mat[i].size();j++)
            mat[i][j] = i*j;
        
    

PS:已经有一种用常数填充矩阵的方法会对我有所帮助。最好使用 C++11 之前的算法。

【问题讨论】:

Sice 值取决于索引......只要留在循环中。 需要初始化空向量,还是填充已经不为空的向量的值?另外,你想用基于索引的值(i*j)填充它们,还是任何值(例如0)都可以? @SingerOfTheFall 不确定我是否理解您的第一个问题,我的IMat 已经初始化,即不为空。是的,这些值应该取决于指数。但是,它已经可以帮助我用常量填充它。 为了好玩,我在没有显式循环或for_each 的情况下尝试了这个,并设法使用嵌套的std::transform 来做到这一点。 coliru.stacked-crooked.com/a/96f803182ad4abad 从技术上讲,您可以将所有内容放在一行中以使其“更少代码”,但是您为什么要这样做? OP 中的简单循环更加简洁。 @AndyG 我不得不承认,我问这个问题的唯一原因是为了更好地理解如何使用算法。当然,我发布的代码完全没问题,但我不确定是否有更优雅的方式来编写它。似乎没有,而且所有建议的解决方案都使用 lambda,而我必须编写 C++11 之前的代码。值得一试;) 【参考方案1】:

最好的解决方案是您已经实施的解决方案。它利用 i/j 作为偏移量和输入来计算算法。

标准算法必须对元素使用迭代器并且维护计数器。这种数据镜像是问题的明确标志。但它可以做到,如果你想花哨的话,即使在一条线上:

for_each(mat.begin(), mat.end(), [&](auto& i)  static auto row = 0; auto column = 0; generate(i.begin(), i.end(), [&]()  return row * column++; ); ++row; );

但正如所说,仅仅因为可以完成并不意味着它应该完成。解决这个问题的最佳方法是for-loop。如果这是您的事情,甚至可以在一条线上进行:

for(auto i = 0U;i < mat.size();i++) for(auto j = 0U;j < mat[i].size();j++) mat[i][j] = i*j;

顺便说一句,我的标准算法在 Clang 3.7.0、gcc 5.1 和 Visual Studio 2015 上运行良好。但是 previously I used transform rather than generate。似乎有一些 implementation bugs in gcc 5.1 and Visual Studio 2015 捕获了 lambda 范围 static 变量。

【讨论】:

如果合适的话,把东西放在一行是非常“我的事”,但如果我必须决定,在 for 循环体周围省略 是被禁止的;) @tobi303 因为换行符是免费的,所以我在我的个人代码中大量使用它们。 (连同花括号。)我认为它使它更具可读性。但归根结底,我认为在这种情况下,最重要的决定不是是否在一条线上进行,而是在for-loop 中进行。 +1 仅针对此声明:“但正如所述,只是因为可以完成并不意味着应该完成”【参考方案2】:

我不知道这是否比双 for 循环更好,但在 C++11 中使用 STL 的一种可能方法是使用两个 for_each,如下所示:

int i(0);
std::for_each(mat.begin(), mat.end(),
[&i](IVec &ivec)int j(0); std::for_each(ivec.begin(), ivec.end(), 
                           [&i,&j](auto &k)k = i*j++;); ++i;);

LIVE DEMO

【讨论】:

我很确定生成的顺序不能保证 => 取决于实现,你填写了错误的值。 @deviantfan 不知道,然后惊喜。 @deviantfan 你能提供任何证据来支持这一点吗? en.cppreference.com/w/cpp/algorithm/… 将其实现为:while (first != last) *first++ = g();。我相信 所有 迭代器算法都是顺序的。如果不是这种情况,可能会有很多问题,例如 lambdas。 @JonathanMee 在标准没有指定算法排序的任何地方,您都应该假设实现可以利用该说法来实现并行性。 for_each 是有序的(从头到尾,参见 §25.2.4.2)。为什么无序的东西对 lambdas 来说是个问题?【参考方案3】:

只是想我会进一步评论乔纳森的出色回答。

暂时忽略 c++11 语法,假设我们已经编写了一些支持类(暂时不管如何)。

我们可以想出这样的代码:

auto main() -> int

    // define a matrix (vector of vectors)
    IMat mat;

    // resize it through some previously defined function
    resize(mat, 10, 10);

    // get an object that is a pseudo-container representing its extent
    auto extent = extent_of(mat);

    // generate values in the pseudo-container which forwards to the matrix
    std::generate(extent.begin(), 
                  extent.end(), 
                  [](auto pxy)  pxy.set_value(pxy.x * pxy.y); );

    // or even

    for (auto pxy : extent_of(mat)) 
        pxy.set_value(product(pxy.coordinates()));
    

    return 0;

100 行支持代码稍后(可迭代容器及其代理并非微不足道),这将编译和工作。

虽然很聪明,但存在一些问题:

多出 100 行代码是一件小事。 在我看来,这段代码实际上不如你的代码表现力。即,您的代码在做什么是显而易见的。对于我的,您必须对额外的 100 行代码做出一些假设或推理。 我的代码比你的代码需要很多更多的维护(和文档)

有时少即是多。

【讨论】:

以上是关于如何使用算法填充向量的向量的主要内容,如果未能解决你的问题,请参考以下文章

如何填充结构的向量

如何对向量使用查找算法

如何使用 MFCC 系数向量训练机器学习算法?

如何选择特征并使用支持向量机算法进行训练?

如何创建一个填充了 C 风格函数指针和 lambda 的向量(有和没有捕获)

如何使用带有R的遗传算法优化支持向量机的参数