Преобразование, описываемое этой формулой, по сути, описывает
exp(-2*i*...)
. Вычисления, стоящие за даннойформулой, могут озадачить всех, кто незнаком с комплексными числами (и тех, кому просто не нравится математика), но N
элементов) суммируются с помощью переменной цикла j
. Переменная k
— еще одна переменная цикла, поскольку преобразование Фурье вычисляет не одно значение, а целый вектор. В данном векторе каждая точка графика представляет собой интенсивность и фазу определенной частоты повторяющейся волны. При реализации этой формулы с помощью циклов получится примерно такой код:csignal fourier_transform(const csignal &s) {
csignal t(s.size());
const double pol {-2.0 * M_PI / s.size()};
for (size_t k {0}; k < s.size(); ++k) {
for (size_t j {0}; j < s.size(); ++j) {
t[k] += s[j] * polar(1.0, pol * k * j);
}
}
return t;
}
Тип csignal
std::complex
. Функция std::polar
, по сути, выполняет часть Эта версия работает хорошо, но давайте реализуем преобразование Фурье с помощью инструментов, доступных в STL.
Как это делается
В данном примере мы реализуем преобразование Фурье, а затем трансформируем с его помощью некоторые сигналы.
1. Сначала включим все необходимые заголовочные файлы и объявим об использовании пространства имен std
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
2. Точка графика сигнала представляет собой комплексное число, которое будет выражено с помощью типажа std::complex
double
. Таким образом, псевдоним типа cmplx
будет расшифровываться в виде двух связанных значений типа double
, которые представляют csignal
:using cmplx = complex
using csignal = vector
3. Чтобы проитерировать по возрастающей численной последовательности, мы возьмем
k
и j
и формулы преобразования будут итерировать по подобным последовательностям.class num_iterator {
size_t i;
public:
explicit num_iterator(size_t position) : i{position} {}
size_t operator*() const { return i; }
num_iterator& operator++() {
++i;
return *this;
}
bool operator!=(const num_iterator &other) const {
return i != other.i;
}
};
4. Функция преобразования Фурье будет принимать сигнал и возвращать новый. Последний представляет собой преобразование Фурье, выполненное для входного сигнала. Обратное преобразование Фурье выполняется аналогично прямому, поэтому предоставим необязательный параметр булева типа, который указывает на направление преобразования. Обратите внимание: наличие подобных параметров зачастую указывает на плохой стиль программирования, особенно если в сигнатуре функции их несколько. Здесь мы для краткости применили всего один параметр булева типа.
Первое, что нужно сделать, — это выделить память для нового вектора сигналов, имеющего размер исходного сигнала:
csignal fourier_transform(const csignal &s, bool back = false)
{
csignal t (s.size());
5. В формуле имеются два множителя, которые всегда выглядят одинаково. Поместим их в отдельные переменные:
const double pol {2.0 * M_PI * (back ? -1.0 : 1.0)};
const double div {back ? 1.0 : double(s.size())};