c++ - 智能迭代器同时迭代两个数组并执行返回自定义类型的操作
问题描述
我在 STL 中工作,例如仅标头DSP 库。今天我开始优化一些功能,我必须实现一个估计信号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_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);
这段代码符合要求,似乎很容易重用于信号处理中的其他常见应用程序,但我仍然在犹豫这是否是最优雅和最健壮的方式。
所以我的问题是:
有没有更优雅的方法来解决这个问题?任何处理这种情况的参考模式或成语?
解决方案
推荐阅读
- c# - 对象包含有关使用它们的上下文的信息是否是一种代码异味?
- performance-testing - JMtere:响应时间图突然出现峰值,然后又下降到持续运行的原因是什么?
- python - 从json响应中提取值数组的最佳方法
- javascript - 使用 javascript 输入部分时间的焦点分钟
- spring - 如何在 r2dbc 中批量执行多个插入?
- visual-studio-code - 在终端上显示当前分支
- php - FOSUser 没有加密密码 Symfony 4
- python - 使用条件复制目录及其子目录和文件
- reactjs - 将脊椎动画与 React 集成
- sql - ORACLE - 更新 xml 中的多个属性