2012-11-16 7 views
10

Czy istnieje dobra implementacja algorytmu obliczania splot dwóch zakresów w C++ STL (lub nawet zwiększenie)? czyli coś z prototypem (splot dwóch zakresach a..b i c..d):C++ stl convolution

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

która modyfikuje a..b zakres

+0

To nie jest trudno napisać to dodaje trzeci zakres początkowo wypełniony zerami. W miejscu, nie wiem. – aschepler

+0

Zakładając, że będzie to dyskretna splot, gdzie zakresy są tej samej długości, nie jest to zbyt trudne - byłoby dość podobne do transformacji. W rzeczywistości możesz nawet użyć transformacji, aby zrobić to z trzecim parametrem będącym odwrotnym iteratorem. – Yuushi

Odpowiedz

1

Tak std :: przekształcić

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 

Aby odpowiedzieć na komentarz, w jaki sposób wykonać nagromadzenie stanu w komentarzach:

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); 
+3

Wydaje mi się, że każdy z algorytmów STL składa się z pojedynczej pętli. Dlatego najprawdopodobniej nie tylko jeden algorytm STL wyraża rozwiązanie problemu, ale kombinacja (np. Std :: inner_product' i 'std :: transform') może. – Orient

+0

Nie widzę, jak 'Op' może gromadzić wszystkie terminy produktów O (n^2) bez wewnętrznego budowania dużego tymczasowego kontenera. –

+0

@j_random_hacker: To proste, aby Op był funktorem. –

2

Nie jestem do końca pewien, jak wygląda "splot" z dwóch sekwencji do jednej z tych dwóch sekwencji: Wydaje się, że jest to inne rozumienie niż moje zrozumienie. Poniżej znajduje się wersja convolution przy użyciu zmiennej liczby iteratorów. Ponieważ na razie jestem po prostu zbyt leniwy, użyję raczej niecodziennego pomysłu na przekazanie iteratora celu jako pierwszej argumentacji, a nie ostatniego argumentu. Tutaj jest wdrożenie odpowiednich zip() algorytmów:

#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; 
    } 
}  

Poniżej znajduje się prosty program testów użyłem, aby sprawdzić, że powyższe ma co zamierzałem to zrobić:

#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")); 
}           
+0

Przy nieco większym kodzie szablonów, możesz go uzyskać, aby upewnić się, że długości dopasowanych varioralnie iteratorów są zgodne, pod warunkiem, że wywołasz zip (OutIt, InIt, InItPack ...) i InItPack zawiera pary argumentów (* not * std: : para ). Jest to jednak około 150 linii kodu, głównie ze względu na nieodpłatne korzystanie z szablonów rekursywnych dla funkcji pomocniczych. – moshbear