Интеллектуальный итератор для одновременного перебора двух массивов и выполнения операции, возвращающей пользовательский тип

#c #arrays #stl #c 14

#c #массивы #stl #c 14

Вопрос:

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

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

Формула SNR

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

введите описание изображения здесь

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

 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) пробел.
  • Я не могу изменить сигнатуру сигнала отклонения.

Код нарушает один из них, поскольку он выделяет память для шумового сигнала, 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_functoramp;amp; f) :
        first1_(first1),
        first2_(first2),
        functor_(f) {
    }

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

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

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

    constexpr zip_iteratoramp; 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);
  

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

Итак, мой вопрос:

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

Комментарии:

1. Для рабочего кода codereview.stackexchange.com кажется более подходящим.

2. Вы могли бы избежать std::function (например, с помощью make_zip_iterator ).

3. Кстати, в настоящее время выполняется слияние zip_iterator с transform_iterator (оба доступны в boost или другой библиотеке, но не в std).

4. @Jarod42 спасибо за совет. Что касается использования boost, к сожалению, я не могу использовать boost или какие-либо внешние библиотеки.