【问题标题】:c++ stl convolutionc++ stl卷积
【发布时间】:2012-11-16 04:24:35
【问题描述】:

是否有一个很好的算法实现来计算 C++ STL 中两个范围的 卷积(甚至提升)? 即带有原型的东西(两个范围a..bc..d的卷积):

template< class Iterator >
void convolution(Iterator a, Iterator b, Iterator c, Iterator d);

修改a..b范围

【问题讨论】:

  • 写一个添加到最初用零填充的第三个范围并不难。就地,我不知道。
  • 假设这将是一个离散卷积,其中范围的长度相同,编写这并不太难 - 它与变换非常相似。事实上,您甚至可以使用 transform 来做到这一点,第三个参数是一个反向迭代器。

标签: c++ algorithm stl convolution


【解决方案1】:

是的 std::transform

std::transform(a, b, c, a, Op);

// a b is the the first input range
// c   is the start of the second range (which must be at least as large as (b-a)
// 
// We then use a as the output iterator as well.

// Op is a BinaryFunction

回答关于如何在 cmets 中进行状态累积的评论:

struct Operator
{
    State& state;
    Operator(Sate& state) : state(state) {}
    Type operator()(TypeR1 const& r1Value, TypeR2 const& r2Value) const
    {
        Plop(state, r1Value, r2Value);
        return Convolute(state, r2Value, r2Value);
    }
};
State  theState  = 0;
Operator Op(theState);

【讨论】:

  • 在我看来,每个 STL 算法都由某种单一的循环组成。因此,很可能不仅仅是一种 STL 算法表达了问题的解决方案,而是组合(即std::inner_productstd::transform)可以。
  • 我不明白Op 是如何在不内部构建大型临时容器的情况下累积所有 O(n^2) 产品术语的。
  • @j_random_hacker:让 Op 成为函子很容易。
  • @LokiAstari:但是你同意你的例子中的State 成员需要包含两个临时容器——一个用来保存第一个序列中迄今为止看到的所有元素,另一个是迄今为止已经看到的第二个序列的所有元素? Plop() 需要将传入operator()() 的两个新元素中的每一个添加到这些内部容器中,并且Convolute() 需要循环(这可以使用std::inner_product() 完成)构建一个产品总和。最后,需要反转迭代器范围之一。
【解决方案2】:

我不太确定从两个序列到这两个序列之一的“卷积”应该是什么:这似乎与我的理解不同。下面是使用可变数量迭代器的convolution 版本。因为我现在实在是太懒了,所以我将使用一个不常见的概念,将目标迭代器作为第一个参数而不是最后一个参数传递。下面是对应zip()算法的实现:

#include <tuple>

namespace algo
{
    template <typename... T>
    void dummy(T...)
    {
    }

    template <typename To, typename InIt, typename... It>
    To zip(To to, InIt it, InIt end, It... its)
    {
        for (; it != end; ++it, ++to) {
            *to = std::make_tuple(*it, *its...);
            algo::dummy(++its...);
        }
        return to;
    }
}    

下面是一个简单的测试程序,我用来验证上面的内容是否符合我的预期:

#include <deque>
#include <iostream>
#include <iterator>
#include <list>
#include <vector>

enum class e { a = 'a', b = 'b', c = 'c' };

std::ostream& operator<< (std::ostream& out,
                          std::tuple<int, double, e> const& v)
{
    return out << "["
               << std::get<0>(v) << ", "
               << std::get<1>(v) << ", "
               << char(std::get<2>(v)) << "]";
}

int main()
{
    typedef std::tuple<int, double, e> tuple;
    std::vector<int>   v{ 1, 2, 3 };
    std::deque<double> d{ 1.1, 2.2, 3.3 };
    std::list<e>       l{ e::a, e::b, e::c };
    std::vector<tuple> r;

    algo::zip(std::back_inserter(r), v.begin(), v.end(), d.begin(), l.begin());

    std::copy(r.begin(), r.end(),
              std::ostream_iterator<tuple>(std::cout, "\n"));
}                                        

【讨论】:

  • 如果您调用 zip(OutIt, InIt, InItPack...) 并且 InItPack 包含参数,您还可以使用更多模板代码来确保可变传递的迭代器的长度匹配对(not std::pair)。然而,它大约有 150 行代码,主要是由于无偿使用递归模板来实现辅助函数。
猜你喜欢
  • 2012-11-06
  • 1970-01-01
  • 2012-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多