#c #arrays #stl #c 14
#c #массивы #stl #c 14
Вопрос:
Я работаю в библиотеке DSP, подобной STL, только для заголовков. Сегодня я начал оптимизировать некоторые функциональные возможности, и мне пришлось реализовать функцию, которая оценивает 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 или какие-либо внешние библиотеки.