Напомним коротко основные понятия и определения, относящиеся к дискретному предобразованию Фурье (ДПФ). Более подробно об этом см., например, в главе 1 книги [].
Пусть $$v={U_i,i=0,\dots,n-1} $$ является вектором с вещественными или комплексными компонентами. Дискретным преобразованием Фурье вектора называется вектор $$V $$ длины $$n $$ с комплексными компонентами, определяемыми равенствами:
$$\label{travial}V_k=\sum_{i=0}^{n-1}w^{ik}v_i,k=0,\dots,n-1,$$ где $$w=e^{-j2pi/n}$$ и $$j=\sqrt{-1}$$.
Вычисление преобразования Фурье в виде (1), как оно записано выше, требует порядка $$n^2$$ умножений и $$n^2$$ сложений.
В книге [], раздел 32.3, приведен алгоритм вычисления ДПФ, требующий порядка $$n log n$$ операций, и потому названный алгоритмом быстрого преобразования Фурье (БПФ). Однако, этот алгоритм трудно поддается
Однако, существует другой алгоритм - так называемый алгоритм Кули-Тьюки (книга [], глава 4), который применим когда число $$n$$ является составным. В частности, если $$n = n1 n2$$ для некоторых $$n1$$ и $$n2$$, то алгоритм Кули-Тьюки требует порядка $$n(n1+n2)+n$$ умножений, но который эффективно параллелится. Особенности параллельной реализации алгоритма Кули-Тьюки состоят в том, что
Суть алгоритма Кули-Тьюки (дополнительные подробности см. в книге [], глава 4) состоит в преобразовании выражения ~(\ref{travial}) в соответствии со следующими равенствами для индексов $$i$$ и $$k$$:
$$i = i1 + n1 i2,~i1 = 0, \dots , n1 -1,~ i2 = 0, \dots , n2 - 1,$$ $$k = n2 k1 + k2,~ k1 = 0, \dots, n1 - 1,~ k2 = 0, \dots, n2 - 1.$$Тогда, выражение (1) может быть переписано в виде
$$}\label{travial}V_k1,k2=\sum_{i1=0}^{n1-1}b^{i1k1}[w^{i1k2}\sum_{i2=0}^{n2-1}y^{i2k2}v_{i1i2}].$$
в котором
$$v_{i1,i2} = v_{i1+n1i2},$$ $$V_{k1,k2} = V_{n2k1+k2},$$ $$\beta = \omega^{n2},$$ $$\gamma = \omega^{n1}.$$
В этом выражении, при каждом значении $$i1 $$ внутренняя сумма представляет собой $$n2 $$ -точечное преобразование Фурье, а внешняя сумма есть $$n1 $$ -точечное преобразование Фурье. Соответственно,

Заметим, что реального отображения исходного вектора в
Ниже представлена базовая часть БПФ-алгоритма Кули-Тьюки, которая включает в себя 4 шага:
// Cooley-Tukey Algorithm
public Complex[] FFT(Complex[] X, int n1, int n2)
{
int n = X.Length;
Complex[,] Y = new Complex[n1,n2];
Complex[,] P = new Complex[n1,n2];
Complex[] T = new Complex[n];
for (int k = 0; k < n1; k++)
ColumnFFT(X, Y, n1, n2 ,k);
for (int k = 0; k < n1; k++)
TwiddleFactorMult(Y, n1, n2, k);
for (int i = 0; i < n2; i++)
RowFFT(Y, P, i, n1);
//Sort elements in right order to create output vector
for (int j = 0; j < n2; j++)
for (int i = 0; i < n1; i++)
T[i * n2 + j] = P[i, j];
return T;
}
Здесь, ColumnFFT и RowFFT - функции, осуществляющие БПФ для одного столбца и, соответственно, строки матрицы, функция TwiddleFactorMult - функция, осуществляющая домножение столбца матрицы на дополнительный множитель, а функция TL_idx реализует
Параллельная реализация БПФ-алгоритма Кули-Тьюки состоит из двух следующих друг за другом циклов , где в первом цикле выполняются шаги (1) и (2)
// Cooley-Tukey Algorithm. Parallel Implementation
public Complex[] FFT(Complex[] X, int n1, int n2)
{
int n = X.Length;
Complex[,] Y = new Complex[n1, n2];
Complex[,] P = new Complex[n1, n2];
Complex[] T = new Complex[n];
Parallel.For(0,n1,k= > {
ColumnFFT(X, Y, n1, n2, k);
TwiddleFactorMult(Y, n1, n2, k);
});
Parallel.For(0, n2, q = >
{
RowFFT(Y, P, q, n1);
//Sort elements in right order to create output vector
for (int i = 0; i < n1; i++)
T[i * n2 + q] = P[i, q];
});
return T;
}
Полностью код параллельной версии БПФ-алгоритма Кули-Тьюки приведен ниже:
//PFX Parallel Fast Fourier Transform (pFFT)
//This implementation is based on the Cooley-Tukey algorithm
using System;
using System.Text;
using System.Threading;
public class Complex
{
public double Re = 0.0;
public double Im = 0.0;
public Complex() { }
public Complex(double re, double im) { Re = re; Im = im; }
public override string ToString()
{
return Re + " " + Im;
}
}
class Program
{
public static void Main(string[] args)
{
if (args.Length != 3)
{
Console.WriteLine(Usage());
return;
}
//n - input vector length (must be power of two)
//n1 - number of Cooley-Tukey's matrix columns
//n2 - number of Cooley-Tukey's matrix rows
int n = 0, n1 = 0, n2 = 0;
n = (int)Math.Pow(2, Int32.Parse(args[0]));
n1 = Int32.Parse(args[1]);
//input vector generation
Complex[] X = new Complex[n];
Random r = new Random();
for (int i = 0; i < n; i++)
X[i] = new Complex(r.NextDouble() * 10, 0);
if ((n1 > n) || (n1 < = 0))
{
Console.WriteLine(Usage() + " Param n1 is invalid: n1= " + n1.ToString() + ". Vector length= " + X.Length.ToString());
return;
}
n2 = n / n1;
Console.WriteLine("*RUN*");
DateTime dt1 = DateTime.Now;
Complex[] pY = (new Program()).FFT(X, n1, n2);
DateTime dt2 = DateTime.Now;
Console.WriteLine(" Parallel FFT: ");
Console.WriteLine(" n= " + n + " n1= " + n1 + " n2= " + n2 +
" Elapsed time is " + (dt2 - dt1).TotalSeconds);
}
// Cooley-Tukey Algorithm. Parallel Implementation
public Complex[] FFT(Complex[] X, int n1, int n2)
{
int n = X.Length;
Complex[,] Y = new Complex[n1, n2];
Complex[,] P = new Complex[n1, n2];
Complex[] T = new Complex[n];
Parallel.For(0,n1,k= > {
ColumnFFT(X, Y, n1, n2, k);
TwiddleFactorMult(Y, n1, n2, k);
});
Parallel.For(0, n2, q = >
{
RowFFT(Y, P, q, n1);
//Sort elements in right order to create output vector
for (int i = 0; i < n1; i++)
T[i * n2 + q] = P[i, q];
});
return T;
}
private void TwiddleFactorMult(Complex[,] Y, int n1, int n2, int k)
{
//Column Twiddle Factor Multiplication
double wn_Re = 0, arg = 0, wn_Im = 0, tmp = 0;
for (int q = 0; q < n2; q++)
{
arg = 2 * Math.PI * k * q / (n2 * n1);
wn_Re = Math.Cos(arg);
wn_Im = Math.Sin(arg);
tmp = Y[k, q].Re * wn_Re - Y[k, q].Im * wn_Im;
Y[k, q].Im = Y[k, q].Re * wn_Im + Y[k, q].Im * wn_Re;
Y[k, q].Re = tmp;
}
}
public void ColumnFFT(Complex[] a, Complex[,] A, int n1, int n2, int k)
{
int q, m, m2, s;
double wn_Re, wn_Im, w_Re, w_Im;
double arg, t_Re, t_Im;
double u_Re, u_Im, tmp;
int logN = 0;
m = n2;
while (m > 1)
{
m = m / 2;
logN++;
}
int temp;
for (q = 0; q < n2; q++)
{
temp = bit_reverse(q, logN);
A[k, temp] = a[k + q * n1];
}
for (s = 1; s < = logN; s++)
{
m = 1 < < s;
arg = 2.0 * Math.PI / m;
wn_Re = Math.Cos(arg);
wn_Im = Math.Sin(arg);
w_Re = 1.0; w_Im = 0.0;
m2 = m > > 1;
for (int i = 0; i < m2; i++)
{
for (int j = i; j < n2; j += m)
{
t_Re = w_Re * A[k, j + m2].Re - w_Im * A[k, j + m2].Im;
t_Im = w_Re * A[k, j + m2].Im + w_Im * A[k, j + m2].Re;
u_Re = A[k, j].Re;
u_Im = A[k, j].Im;
A[k, j].Re = u_Re + t_Re;
A[k, j].Im = u_Im + t_Im;
A[k, j + m2].Re = u_Re - t_Re;
A[k, j + m2].Im = u_Im - t_Im;
}
tmp = w_Re * wn_Re - w_Im * wn_Im;
w_Im = w_Re * wn_Im + w_Im * wn_Re;
w_Re = tmp;
}
}
}
public void RowFFT(Complex[,] a, Complex[,] A, int q, int n1)
{
int j, k, m, m2, s;
double wn_Re, wn_Im, w_Re, w_Im;
double arg, t_Re, t_Im;
double u_Re, u_Im, tmp;
int logN = 0;
m = n1;
while (m > 1)
{
m = m / 2;
logN++;
}
for (k = 0; k < n1; k++)
A[bit_reverse(k, logN), q] = a[k, q];
for (s = 1; s < = logN; s++)
{
m = 1 < < s;
arg = 2.0 * Math.PI / m;
wn_Re = Math.Cos(arg);
wn_Im = Math.Sin(arg);
w_Re = 1.0; w_Im = 0.0;
m2 = m > > 1;
for (j = 0; j < m2; j++)
{
for (k = j; k <= n1; k += m)
{
t_Re = w_Re * A[k + m2, q].Re - w_Im * A[k + m2, q].Im;
t_Im = w_Re * A[k + m2, q].Im + w_Im * A[k + m2, q].Re;
u_Re = A[k, q].Re;
u_Im = A[k, q].Im;
A[k, q].Re = u_Re + t_Re;
A[k, q].Im = u_Im + t_Im;
A[k + m2, q].Re = u_Re - t_Re;
A[k + m2, q].Im = u_Im - t_Im;
}
tmp = w_Re * wn_Re - w_Im * wn_Im;
w_Im = w_Re * wn_Im + w_Im * wn_Re;
w_Re = tmp;
}
}
}
public int bit_reverse(int k, int size)
{
int right_unit = 1;
int left_unit = 1 < < (size - 1);
int result = 0, bit;
for (int i = 0; i < size; i++)
{
bit = k right_unit;
if (bit != 0)
result = result | left_unit;
right_unit = right_unit < < 1;
left_unit = left_unit > > 1;
}
return (result);
}
static string Usage()
{
string pname = System.Reflection.Assembly.GetEntryAssembly().GetName().Name;
StringBuilder s = new StringBuilder();
for (int i = 0; i < Console.WindowWidth; i++) s.Append(" - ");
return (s.ToString() + Environment.NewLine +
" Usage: " + pname + " p n1 np " + Environment.NewLine +
" where " + Environment.NewLine +
" p - n=2^p - input vector length " + Environment.NewLine +
" n1 - width of block " + Environment.NewLine +
" np - number of processes " + Environment.NewLine +
s.ToString() + Environment.NewLine);
}
}
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.