Умный итератор для итерации по двум массивам одновременно и выполнения операции, которая возвращает пользовательский тип - PullRequest
0 голосов
/ 05 апреля 2019

Я работаю в STL, как только заголовок DSP библиотека .Сегодня я начал оптимизировать некоторые функции, и мне пришлось реализовать функцию, которая оценивает SNR сигнала.

Если предположить, что мы работаем с сигналами с нулевым средним, дисперсияравна мощности, поэтому SNR - это отношение дисперсии сигнала и шума.

SNR formula

На практике мы не знаем шумовой сигнал, и он постоянно изменяется во времени.Обычно мы можем приблизить шум как разницу между вашим чистым сигналом и записанным:

enter image description here

Что касается кода библиотеки, то здесь уже естьСуществующий код для оценки дисперсии, поэтому в качестве первого подхода я сделал что-то вроде этого:

const auto functor = [](const value_type left, const value_type right) {  
    return left - right; 
};

// Allocation of memory, O(N)
std::vector<float> noise(N);
std::transform(std::cbegin(recorded), std::cend(recorded), std::cbegin(signal), std::begin(noise), functor);

const auto var_ref   = statistics::variance(std::cbegin(recorded), std::cend(recorded));
const auto var_noise = statistics::variance(std::cbegin(noise), std::cend(noise));

const auto SNR = converter::pow2db(var_ref / var_noise);

Это список требований реализации:

  • Я не могу изменитьвходные данные (только для чтения).
  • Я не могу использовать boost или какую-либо внешнюю библиотеку, просто стандарт C ++
  • Я должен избегать временных выделений: O(1) пробел.
  • Iне может изменить сигнатуру дисперсионного сигнала.

Код нарушает один из них, так как выделяет память для шумового сигнала, O(N) пробел.

Мне удалось найти решение, но я не совсем уверен, что оно самое элегантное.По сути, я использовал идею функции zip в python и создал итератор, который выполняет итерацию по двум массивам одновременно и выполняет операцию при каждом обращении к ней (отложенная инициализация).

Это кодитератора:

template <class Iter1, class Iter2, class zip_functor>
class zip_iterator {
public:
    using first_type = typename std::iterator_traits<Iter1>::value_type;
    using second_type = typename std::iterator_traits<Iter2>::value_type;
    using iterator_category = std::input_iterator_tag;

    constexpr zip_iterator(Iter1 first1, Iter2 first2, zip_functor&& f) :
        first1_(first1),
        first2_(first2),
        functor_(f) {
    }

    constexpr auto operator !=(const zip_iterator& other) const {
        return this->first1_ != other.first1_ and
               this->first2_ != other.first2_;
    }

    constexpr auto operator ==(const zip_iterator& other) const {
        return this->first1_ == other.first1_ and
               this->first2_ == other.first2_;
    }

    constexpr zip_iterator& operator++() {
        first1_ = std::next(first1_);
        first2_ = std::next(first2_);
        return *this;
    }

    constexpr zip_iterator& operator--() {
        first1_ = std::prev(first1_);
        first2_ = std::prev(first2_);
        return *this;
    }

    constexpr auto operator*() {
        return functor_(*first1_, *first2_);
    }

private:
    Iter1 first1_;
    Iter2 first2_;
    zip_functor functor_;
};

При новой реализации код становится:

// Alias of the custom iterator
using first_iter = typename std::array<float, N>::const_iterator;
using second_iter = typename std::array<float, N>::const_iterator;
using zip_iter = zip_iterator<first_iter, second_iter,
        std::function<float(float, float)>>;

// This performs the estimation of the noise in each sample
const auto functor = [](const auto left, const auto right) {
        return left - right;
};

// Estimates the variance of the noise in O(1) space
auto start = zip_iter(std::cbegin(recorded), std::cbegin(signal), functor);
auto end = zip_iter(std::cend(recorded), std::cend(signal), functor);
const auto var_noise = edsp::statistics::variance(start, end);
const auto var_ref = edsp::statistics::variance(std::cbegin(recorded), std::cend(recorded));

const auto SNR = converter::pow2db(var_ref / var_noise);

Этот код соответствует требованию и, кажется, его легко использовать для других распространенных приложений при обработке сигналов,но все же я сомневаюсь, если это самый элегантный и надежный способ сделать это.

Поэтому мой вопрос:

Есть ли более элегантный способ решения этой проблемы?Любой эталонный образец или идиома, которая обрабатывает эту ситуацию?

...